mirror of
https://github.com/SigNoz/signoz.git
synced 2026-05-06 10:30:31 +01:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05cdaff264 | ||
|
|
062fa4bad0 |
@@ -40,7 +40,7 @@ services:
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
schema-migrator-sync:
|
||||
image: signoz/signoz-schema-migrator:v0.111.41
|
||||
image: signoz/signoz-schema-migrator:v0.111.40
|
||||
container_name: schema-migrator-sync
|
||||
command:
|
||||
- sync
|
||||
@@ -53,7 +53,7 @@ services:
|
||||
condition: service_healthy
|
||||
restart: on-failure
|
||||
schema-migrator-async:
|
||||
image: signoz/signoz-schema-migrator:v0.111.41
|
||||
image: signoz/signoz-schema-migrator:v0.111.40
|
||||
container_name: schema-migrator-async
|
||||
command:
|
||||
- async
|
||||
|
||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -2,7 +2,7 @@
|
||||
# Owners are automatically requested for review for PRs that changes code
|
||||
# that they own.
|
||||
|
||||
/frontend/ @SigNoz/frontend @YounixM
|
||||
/frontend/ @YounixM
|
||||
/frontend/src/container/MetricsApplication @srikanthccv
|
||||
/frontend/src/container/NewWidget/RightContainer/types.ts @srikanthccv
|
||||
/deploy/ @SigNoz/devops
|
||||
|
||||
75
.github/pull_request_template.md
vendored
75
.github/pull_request_template.md
vendored
@@ -1,74 +1,17 @@
|
||||
## 📄 Summary
|
||||
### Summary
|
||||
|
||||
<!-- Describe the purpose of the PR in a few sentences. What does it fix/add/update? -->
|
||||
<!-- ✍️ A clear and concise description...-->
|
||||
|
||||
---
|
||||
#### Related Issues / PR's
|
||||
|
||||
## ✅ Changes
|
||||
<!-- ✍️ Add the issues being resolved here and related PR's where applicable -->
|
||||
|
||||
- [ ] Feature: Brief description
|
||||
- [ ] Bug fix: Brief description
|
||||
#### Screenshots
|
||||
|
||||
---
|
||||
NA
|
||||
|
||||
## 🏷️ Required: Add Relevant Labels
|
||||
<!-- ✍️ Add screenshots of before and after changes where applicable-->
|
||||
|
||||
> ⚠️ **Manually add appropriate labels in the PR sidebar**
|
||||
Please select one or more labels (as applicable):
|
||||
#### Affected Areas and Manually Tested Areas
|
||||
|
||||
ex:
|
||||
|
||||
- `frontend`
|
||||
- `backend`
|
||||
- `devops`
|
||||
- `bug`
|
||||
- `enhancement`
|
||||
- `ui`
|
||||
- `test`
|
||||
|
||||
---
|
||||
|
||||
## 👥 Reviewers
|
||||
|
||||
> Tag the relevant teams for review:
|
||||
|
||||
- [ ] @SigNoz/frontend
|
||||
- [ ] @SigNoz/backend
|
||||
- [ ] @SigNoz/devops
|
||||
|
||||
---
|
||||
|
||||
## 🧪 How to Test
|
||||
|
||||
<!-- Describe how reviewers can test this PR -->
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Related Issues
|
||||
|
||||
<!-- Reference any related issues (e.g. Fixes #123, Closes #456) -->
|
||||
Closes #
|
||||
|
||||
---
|
||||
|
||||
## 📸 Screenshots / Screen Recording (if applicable / mandatory for UI related changes)
|
||||
|
||||
<!-- Add screenshots or GIFs to help visualize changes -->
|
||||
|
||||
---
|
||||
|
||||
## 📋 Checklist
|
||||
|
||||
- [ ] Dev Review
|
||||
- [ ] Test cases added (Unit/ Integration / E2E)
|
||||
- [ ] Manually tested the changes
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 👀 Notes for Reviewers
|
||||
|
||||
<!-- Anything reviewers should keep in mind while reviewing -->
|
||||
<!-- ✍️ Add details of blast radius and dev testing areas where applicable-->
|
||||
|
||||
1
.github/workflows/build-community.yaml
vendored
1
.github/workflows/build-community.yaml
vendored
@@ -62,7 +62,6 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.23
|
||||
GO_NAME: signoz-community
|
||||
GO_INPUT_ARTIFACT_CACHE_KEY: community-jsbuild-${{ github.sha }}
|
||||
GO_INPUT_ARTIFACT_PATH: frontend/build
|
||||
|
||||
1
.github/workflows/build-enterprise.yaml
vendored
1
.github/workflows/build-enterprise.yaml
vendored
@@ -94,7 +94,6 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.23
|
||||
GO_INPUT_ARTIFACT_CACHE_KEY: enterprise-jsbuild-${{ github.sha }}
|
||||
GO_INPUT_ARTIFACT_PATH: frontend/build
|
||||
GO_BUILD_CONTEXT: ./ee/query-service
|
||||
|
||||
1
.github/workflows/build-staging.yaml
vendored
1
.github/workflows/build-staging.yaml
vendored
@@ -91,7 +91,6 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.23
|
||||
GO_INPUT_ARTIFACT_CACHE_KEY: staging-jsbuild-${{ github.sha }}
|
||||
GO_INPUT_ARTIFACT_PATH: frontend/build
|
||||
GO_BUILD_CONTEXT: ./ee/query-service
|
||||
|
||||
5
.github/workflows/goci.yaml
vendored
5
.github/workflows/goci.yaml
vendored
@@ -18,7 +18,6 @@ jobs:
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_TEST_CONTEXT: ./...
|
||||
GO_VERSION: 1.23
|
||||
fmt:
|
||||
if: |
|
||||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
||||
@@ -27,7 +26,6 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.23
|
||||
lint:
|
||||
if: |
|
||||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
||||
@@ -36,7 +34,6 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.23
|
||||
build:
|
||||
if: |
|
||||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
||||
@@ -48,7 +45,7 @@ jobs:
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
go-version: "1.22"
|
||||
- name: qemu-install
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: aarch64-install
|
||||
|
||||
4
.github/workflows/gor-signoz-community.yaml
vendored
4
.github/workflows/gor-signoz-community.yaml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
go-version: "1.22"
|
||||
- name: cross-compilation-tools
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
@@ -122,7 +122,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
go-version: "1.22"
|
||||
|
||||
# copy the caches from build
|
||||
- name: get-sha
|
||||
|
||||
4
.github/workflows/gor-signoz.yaml
vendored
4
.github/workflows/gor-signoz.yaml
vendored
@@ -73,7 +73,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
go-version: "1.22"
|
||||
- name: cross-compilation-tools
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
- name: setup-go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
go-version: "1.22"
|
||||
|
||||
# copy the caches from build
|
||||
- name: get-sha
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -60,7 +60,9 @@ ee/query-service/db
|
||||
|
||||
e2e/node_modules/
|
||||
e2e/test-results/
|
||||
e2e/playwright-report/
|
||||
e2e/blob-report/
|
||||
e2e/playwright/.cache/
|
||||
e2e/.auth
|
||||
|
||||
# go
|
||||
|
||||
8
Makefile
8
Makefile
@@ -76,7 +76,9 @@ go-run-enterprise: ## Runs the enterprise go backend server
|
||||
go run -race \
|
||||
$(GO_BUILD_CONTEXT_ENTERPRISE)/main.go \
|
||||
--config ./conf/prometheus.yml \
|
||||
--cluster cluster
|
||||
--cluster cluster \
|
||||
--use-logs-new-schema true \
|
||||
--use-trace-new-schema true
|
||||
|
||||
.PHONY: go-test
|
||||
go-test: ## Runs go unit tests
|
||||
@@ -94,7 +96,9 @@ go-run-community: ## Runs the community go backend server
|
||||
go run -race \
|
||||
$(GO_BUILD_CONTEXT_COMMUNITY)/main.go \
|
||||
--config ./conf/prometheus.yml \
|
||||
--cluster cluster
|
||||
--cluster cluster \
|
||||
--use-logs-new-schema true \
|
||||
--use-trace-new-schema true
|
||||
|
||||
.PHONY: go-build-community $(GO_BUILD_ARCHS_COMMUNITY)
|
||||
go-build-community: ## Builds the go backend server for community
|
||||
|
||||
@@ -164,9 +164,3 @@ alertmanager:
|
||||
maintenance_interval: 15m
|
||||
# Retention of the notification logs.
|
||||
retention: 120h
|
||||
|
||||
|
||||
##################### Analytics #####################
|
||||
analytics:
|
||||
# Whether to enable analytics.
|
||||
enabled: false
|
||||
|
||||
@@ -174,9 +174,11 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.82.0
|
||||
image: signoz/signoz:v0.81.0
|
||||
command:
|
||||
- --config=/root/config/prometheus.yml
|
||||
- --use-logs-new-schema=true
|
||||
- --use-trace-new-schema=true
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
# - "6060:6060" # pprof port
|
||||
@@ -206,7 +208,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.111.41
|
||||
image: signoz/signoz-otel-collector:v0.111.40
|
||||
command:
|
||||
- --config=/etc/otel-collector-config.yaml
|
||||
- --manager-config=/etc/manager-config.yaml
|
||||
@@ -230,7 +232,7 @@ services:
|
||||
- signoz
|
||||
schema-migrator:
|
||||
!!merge <<: *common
|
||||
image: signoz/signoz-schema-migrator:v0.111.41
|
||||
image: signoz/signoz-schema-migrator:v0.111.40
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
|
||||
@@ -110,9 +110,11 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.82.0
|
||||
image: signoz/signoz:v0.81.0
|
||||
command:
|
||||
- --config=/root/config/prometheus.yml
|
||||
- --use-logs-new-schema=true
|
||||
- --use-trace-new-schema=true
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
# - "6060:6060" # pprof port
|
||||
@@ -141,7 +143,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.111.41
|
||||
image: signoz/signoz-otel-collector:v0.111.40
|
||||
command:
|
||||
- --config=/etc/otel-collector-config.yaml
|
||||
- --manager-config=/etc/manager-config.yaml
|
||||
@@ -165,7 +167,7 @@ services:
|
||||
- signoz
|
||||
schema-migrator:
|
||||
!!merge <<: *common
|
||||
image: signoz/signoz-schema-migrator:v0.111.41
|
||||
image: signoz/signoz-schema-migrator:v0.111.40
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
|
||||
@@ -26,7 +26,7 @@ processors:
|
||||
detectors: [env, system]
|
||||
timeout: 2s
|
||||
signozspanmetrics/delta:
|
||||
metrics_exporter: clickhousemetricswrite, signozclickhousemetrics
|
||||
metrics_exporter: clickhousemetricswrite
|
||||
metrics_flush_interval: 60s
|
||||
latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
|
||||
dimensions_cache_size: 100000
|
||||
@@ -64,10 +64,8 @@ exporters:
|
||||
endpoint: tcp://clickhouse:9000/signoz_metrics
|
||||
resource_to_telemetry_conversion:
|
||||
enabled: true
|
||||
disable_v2: true
|
||||
clickhousemetricswrite/prometheus:
|
||||
endpoint: tcp://clickhouse:9000/signoz_metrics
|
||||
disable_v2: true
|
||||
signozclickhousemetrics:
|
||||
dsn: tcp://clickhouse:9000/signoz_metrics
|
||||
clickhouselogsexporter:
|
||||
|
||||
@@ -177,10 +177,12 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.82.0}
|
||||
image: signoz/signoz:${VERSION:-v0.81.0}
|
||||
container_name: signoz
|
||||
command:
|
||||
- --config=/root/config/prometheus.yml
|
||||
- --use-logs-new-schema=true
|
||||
- --use-trace-new-schema=true
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
# - "6060:6060" # pprof port
|
||||
@@ -210,7 +212,7 @@ services:
|
||||
# TODO: support otel-collector multiple replicas. Nginx/Traefik for loadbalancing?
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.111.41}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.111.40}
|
||||
container_name: signoz-otel-collector
|
||||
command:
|
||||
- --config=/etc/otel-collector-config.yaml
|
||||
@@ -236,7 +238,7 @@ services:
|
||||
condition: service_healthy
|
||||
schema-migrator-sync:
|
||||
!!merge <<: *common
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.41}
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.40}
|
||||
container_name: schema-migrator-sync
|
||||
command:
|
||||
- sync
|
||||
@@ -247,7 +249,7 @@ services:
|
||||
condition: service_healthy
|
||||
schema-migrator-async:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.41}
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.40}
|
||||
container_name: schema-migrator-async
|
||||
command:
|
||||
- async
|
||||
|
||||
@@ -110,10 +110,12 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.82.0}
|
||||
image: signoz/signoz:${VERSION:-v0.81.0}
|
||||
container_name: signoz
|
||||
command:
|
||||
- --config=/root/config/prometheus.yml
|
||||
- --use-logs-new-schema=true
|
||||
- --use-trace-new-schema=true
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
# - "6060:6060" # pprof port
|
||||
@@ -142,7 +144,7 @@ services:
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.111.41}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.111.40}
|
||||
container_name: signoz-otel-collector
|
||||
command:
|
||||
- --config=/etc/otel-collector-config.yaml
|
||||
@@ -164,7 +166,7 @@ services:
|
||||
condition: service_healthy
|
||||
schema-migrator-sync:
|
||||
!!merge <<: *common
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.41}
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.40}
|
||||
container_name: schema-migrator-sync
|
||||
command:
|
||||
- sync
|
||||
@@ -176,7 +178,7 @@ services:
|
||||
restart: on-failure
|
||||
schema-migrator-async:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.41}
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-v0.111.40}
|
||||
container_name: schema-migrator-async
|
||||
command:
|
||||
- async
|
||||
|
||||
@@ -26,7 +26,7 @@ processors:
|
||||
detectors: [env, system]
|
||||
timeout: 2s
|
||||
signozspanmetrics/delta:
|
||||
metrics_exporter: clickhousemetricswrite, signozclickhousemetrics
|
||||
metrics_exporter: clickhousemetricswrite
|
||||
metrics_flush_interval: 60s
|
||||
latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
|
||||
dimensions_cache_size: 100000
|
||||
@@ -62,12 +62,10 @@ exporters:
|
||||
use_new_schema: true
|
||||
clickhousemetricswrite:
|
||||
endpoint: tcp://clickhouse:9000/signoz_metrics
|
||||
disable_v2: true
|
||||
resource_to_telemetry_conversion:
|
||||
enabled: true
|
||||
clickhousemetricswrite/prometheus:
|
||||
endpoint: tcp://clickhouse:9000/signoz_metrics
|
||||
disable_v2: true
|
||||
signozclickhousemetrics:
|
||||
dsn: tcp://clickhouse:9000/signoz_metrics
|
||||
clickhouselogsexporter:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.23-bullseye
|
||||
FROM golang:1.22-bullseye
|
||||
|
||||
ARG OS="linux"
|
||||
ARG TARGETARCH
|
||||
|
||||
@@ -41,8 +41,8 @@ func (ah *APIHandler) lockUnlockDashboard(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
dashboard, apiErr := dashboards.GetDashboard(r.Context(), claims.OrgID, uuid)
|
||||
if apiErr != nil {
|
||||
dashboard, err := dashboards.GetDashboard(r.Context(), claims.OrgID, uuid)
|
||||
if err != nil {
|
||||
render.Error(w, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to get dashboard"))
|
||||
return
|
||||
}
|
||||
@@ -53,8 +53,8 @@ func (ah *APIHandler) lockUnlockDashboard(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
// Lock/Unlock the dashboard
|
||||
apiErr = dashboards.LockUnlockDashboard(r.Context(), claims.OrgID, uuid, lock)
|
||||
if apiErr != nil {
|
||||
err = dashboards.LockUnlockDashboard(r.Context(), claims.OrgID, uuid, lock)
|
||||
if err != nil {
|
||||
render.Error(w, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to lock/unlock dashboard"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -322,7 +322,6 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
|
||||
apiHandler.RegisterMessagingQueuesRoutes(r, am)
|
||||
apiHandler.RegisterThirdPartyApiRoutes(r, am)
|
||||
apiHandler.MetricExplorerRoutes(r, am)
|
||||
apiHandler.RegisterTraceFunnelsRoutes(r, am)
|
||||
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
|
||||
@@ -62,6 +62,13 @@ var BasicPlan = basemodel.FeatureSet{
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.TraceFunnels,
|
||||
Active: false,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
}
|
||||
|
||||
var EnterprisePlan = basemodel.FeatureSet{
|
||||
@@ -114,4 +121,11 @@ var EnterprisePlan = basemodel.FeatureSet{
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
basemodel.Feature{
|
||||
Name: basemodel.TraceFunnels,
|
||||
Active: false,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -106,7 +106,3 @@ func (provider *provider) WrapAlreadyExistsErrf(err error, code errors.Code, for
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (dialect *dialect) ToggleForeignKeyConstraint(ctx context.Context, bun *bun.DB, enable bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
# Ignore artifacts:
|
||||
build
|
||||
coverage
|
||||
public/
|
||||
|
||||
# Ignore all MD files:
|
||||
**/*.md
|
||||
|
||||
# Ignore all JSON files:
|
||||
**/*.json
|
||||
**/*.md
|
||||
@@ -3,4 +3,5 @@ BUNDLE_ANALYSER="true"
|
||||
FRONTEND_API_ENDPOINT="http://localhost:8080/"
|
||||
INTERCOM_APP_ID="intercom-app-id"
|
||||
|
||||
PLAYWRIGHT_TEST_BASE_URL="http://localhost:8080"
|
||||
CI="1"
|
||||
@@ -30,6 +30,11 @@ const config: Config.InitialOptions = {
|
||||
testPathIgnorePatterns: ['/node_modules/', '/public/'],
|
||||
moduleDirectories: ['node_modules', 'src'],
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
testEnvironmentOptions: {
|
||||
'jest-playwright': {
|
||||
browsers: ['chromium', 'firefox', 'webkit'],
|
||||
},
|
||||
},
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
statements: 80,
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
"jest:coverage": "jest --coverage",
|
||||
"jest:watch": "jest --watch",
|
||||
"postinstall": "yarn i18n:generate-hash && (is-ci || yarn husky:configure)",
|
||||
"playwright": "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",
|
||||
"test": "jest",
|
||||
@@ -28,9 +32,10 @@
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "6.0.0",
|
||||
"@ant-design/icons": "4.8.0",
|
||||
"@dnd-kit/core": "6.1.0",
|
||||
"@dnd-kit/core": "6.3.1",
|
||||
"@dnd-kit/modifiers": "7.0.0",
|
||||
"@dnd-kit/sortable": "8.0.0",
|
||||
"@dnd-kit/sortable": "10.0.0",
|
||||
"@dnd-kit/utilities": "3.2.2",
|
||||
"@grafana/data": "^11.2.3",
|
||||
"@mdx-js/loader": "2.3.0",
|
||||
"@mdx-js/react": "2.3.0",
|
||||
@@ -78,7 +83,6 @@
|
||||
"history": "4.10.1",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"http-proxy-middleware": "3.0.3",
|
||||
"http-status-codes": "2.3.0",
|
||||
"i18next": "^21.6.12",
|
||||
"i18next-browser-languagedetector": "^6.1.3",
|
||||
"i18next-http-backend": "^1.3.2",
|
||||
@@ -87,7 +91,7 @@
|
||||
"less": "^4.1.2",
|
||||
"less-loader": "^10.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-react": "0.498.0",
|
||||
"lucide-react": "0.427.0",
|
||||
"mini-css-extract-plugin": "2.4.5",
|
||||
"motion": "12.4.13",
|
||||
"overlayscrollbars": "^2.8.1",
|
||||
@@ -160,6 +164,7 @@
|
||||
"@commitlint/config-conventional": "^16.2.4",
|
||||
"@faker-js/faker": "9.3.0",
|
||||
"@jest/globals": "^27.5.1",
|
||||
"@playwright/test": "^1.22.0",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
@@ -253,7 +258,6 @@
|
||||
"http-proxy-middleware": "3.0.3",
|
||||
"cross-spawn": "7.0.5",
|
||||
"cookie": "^0.7.1",
|
||||
"serialize-javascript": "6.0.2",
|
||||
"prismjs": "1.30.0"
|
||||
"serialize-javascript": "6.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
23
frontend/playwright.config.ts
Normal file
23
frontend/playwright.config.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { PlaywrightTestConfig } from '@playwright/test';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
preserveOutput: 'always',
|
||||
name: 'Signoz',
|
||||
testDir: './tests',
|
||||
use: {
|
||||
trace: 'retain-on-failure',
|
||||
baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://localhost:3301',
|
||||
},
|
||||
updateSnapshots: 'all',
|
||||
fullyParallel: !!process.env.CI,
|
||||
quiet: false,
|
||||
testMatch: ['**/*.spec.ts'],
|
||||
reporter: process.env.CI ? 'github' : 'list',
|
||||
};
|
||||
|
||||
export default config;
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.9 KiB |
@@ -1 +0,0 @@
|
||||
<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><g stroke="#C0C1C3" stroke-width="1.167" stroke-linecap="round" stroke-linejoin="round"><path d="m12.192 3.18-1.167 2.33-.583 1.165M7.31 12.74a.583.583 0 0 1-.835-.24L1.808 3.179"/><path d="M7 1.167c2.9 0 5.25.783 5.25 1.75 0 .966-2.35 1.75-5.25 1.75s-5.25-.784-5.25-1.75c0-.967 2.35-1.75 5.25-1.75ZM8.75 10.5h3.5M10.5 12.25v-3.5"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 418 B |
@@ -1 +0,0 @@
|
||||
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)" stroke-linecap="round" stroke-linejoin="round"><path d="M8 14.666A6.667 6.667 0 1 0 8 1.333a6.667 6.667 0 0 0 0 13.333Z" fill="#C0C1C3" stroke="#C0C1C3" stroke-width="2"/><path d="M8 11.333v-4H6.333M8 4.667h.007" stroke="#121317" stroke-width="1.333"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
Before Width: | Height: | Size: 439 B |
@@ -9,7 +9,7 @@ done
|
||||
# create temporary tsconfig which includes only passed files
|
||||
str="{
|
||||
\"extends\": \"./tsconfig.json\",
|
||||
\"include\": [ \"src/typings/**/*.ts\",\"src/**/*.d.ts\", \"./babel.config.js\", \"./jest.config.ts\", \"./.eslintrc.js\",\"./__mocks__\",\"./public\",\"./tests\",\"./commitlint.config.ts\",\"./webpack.config.js\",\"./webpack.config.prod.js\",\"./jest.setup.ts\",\"./**/*.d.ts\",$files]
|
||||
\"include\": [ \"src/typings/**/*.ts\",\"src/**/*.d.ts\", \"./babel.config.js\", \"./jest.config.ts\", \"./.eslintrc.js\",\"./__mocks__\",\"./conf/default.conf\",\"./public\",\"./tests\",\"./playwright.config.ts\",\"./commitlint.config.ts\",\"./webpack.config.js\",\"./webpack.config.prod.js\",\"./jest.setup.ts\",\"./**/*.d.ts\",$files]
|
||||
}"
|
||||
echo $str > tsconfig.tmp
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import getLocalStorageApi from 'api/browser/localstorage/get';
|
||||
import setLocalStorageApi from 'api/browser/localstorage/set';
|
||||
import getOrgUser from 'api/v1/user/getOrgUser';
|
||||
import getOrgUser from 'api/user/getOrgUser';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import ROUTES from 'constants/routes';
|
||||
|
||||
@@ -47,10 +47,9 @@ export const TracesFunnels = Loadable(
|
||||
import(/* webpackChunkName: "Traces Funnels" */ 'pages/TracesModulePage'),
|
||||
);
|
||||
export const TracesFunnelDetails = Loadable(
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "Traces Funnel Details" */ 'pages/TracesModulePage'
|
||||
/* webpackChunkName: "Traces Funnel Details" */ 'pages/TracesFunnelDetails'
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -531,7 +531,6 @@ export const oldRoutes = [
|
||||
'/traces-save-views',
|
||||
'/settings/access-tokens',
|
||||
'/messaging-queues',
|
||||
'/alerts/edit',
|
||||
];
|
||||
|
||||
export const oldNewRoutesMapping: Record<string, string> = {
|
||||
@@ -542,7 +541,6 @@ export const oldNewRoutesMapping: Record<string, string> = {
|
||||
'/traces-save-views': '/traces/saved-views',
|
||||
'/settings/access-tokens': '/settings/api-keys',
|
||||
'/messaging-queues': '/messaging-queues/overview',
|
||||
'/alerts/edit': '/alerts/overview',
|
||||
};
|
||||
|
||||
export const ROUTES_NOT_TO_BE_OVERRIDEN: string[] = [
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp } from 'types/api';
|
||||
import APIError from 'types/api/error';
|
||||
|
||||
// reference - https://axios-http.com/docs/handling_errors
|
||||
export function ErrorResponseHandlerV2(error: AxiosError<ErrorV2Resp>): never {
|
||||
const { response, request } = error;
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
if (response) {
|
||||
throw new APIError({
|
||||
httpStatusCode: response.status || 500,
|
||||
error: {
|
||||
code: response.data.error.code,
|
||||
message: response.data.error.message,
|
||||
url: response.data.error.url,
|
||||
errors: response.data.error.errors,
|
||||
},
|
||||
});
|
||||
}
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
if (request) {
|
||||
throw new APIError({
|
||||
httpStatusCode: error.status || 500,
|
||||
error: {
|
||||
code: error.code || error.name,
|
||||
message: error.message,
|
||||
url: '',
|
||||
errors: [],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
throw new APIError({
|
||||
httpStatusCode: error.status || 500,
|
||||
error: {
|
||||
code: error.name,
|
||||
message: error.message,
|
||||
url: '',
|
||||
errors: [],
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createEmail';
|
||||
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/channels', {
|
||||
const response = await axios.post('/channels', {
|
||||
name: props.name,
|
||||
email_configs: [
|
||||
{
|
||||
@@ -21,12 +21,13 @@ const create = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createMsTeams';
|
||||
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/channels', {
|
||||
const response = await axios.post('/channels', {
|
||||
name: props.name,
|
||||
msteamsv2_configs: [
|
||||
{
|
||||
@@ -21,12 +21,13 @@ const create = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createOpsgenie';
|
||||
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/channels', {
|
||||
const response = await axios.post('/channels', {
|
||||
name: props.name,
|
||||
opsgenie_configs: [
|
||||
{
|
||||
@@ -24,12 +24,13 @@ const create = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createPager';
|
||||
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/channels', {
|
||||
const response = await axios.post('/channels', {
|
||||
name: props.name,
|
||||
pagerduty_configs: [
|
||||
{
|
||||
@@ -29,12 +29,13 @@ const create = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createSlack';
|
||||
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/channels', {
|
||||
const response = await axios.post('/channels', {
|
||||
name: props.name,
|
||||
slack_configs: [
|
||||
{
|
||||
@@ -22,12 +22,13 @@ const create = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createWebhook';
|
||||
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
let httpConfig = {};
|
||||
const username = props.username ? props.username.trim() : '';
|
||||
@@ -28,7 +28,7 @@ const create = async (
|
||||
};
|
||||
}
|
||||
|
||||
const response = await axios.post<PayloadProps>('/channels', {
|
||||
const response = await axios.post('/channels', {
|
||||
name: props.name,
|
||||
webhook_configs: [
|
||||
{
|
||||
@@ -40,12 +40,13 @@ const create = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/delete';
|
||||
|
||||
const deleteChannel = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.delete<PayloadProps>(`/channels/${props.id}`);
|
||||
const response = await axios.delete(`/channels/${props.id}`);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editEmail';
|
||||
|
||||
const editEmail = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.put<PayloadProps>(`/channels/${props.id}`, {
|
||||
const response = await axios.put(`/channels/${props.id}`, {
|
||||
name: props.name,
|
||||
email_configs: [
|
||||
{
|
||||
@@ -21,12 +21,13 @@ const editEmail = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editMsTeams';
|
||||
|
||||
const editMsTeams = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.put<PayloadProps>(`/channels/${props.id}`, {
|
||||
const response = await axios.put(`/channels/${props.id}`, {
|
||||
name: props.name,
|
||||
msteamsv2_configs: [
|
||||
{
|
||||
@@ -21,12 +21,13 @@ const editMsTeams = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editOpsgenie';
|
||||
|
||||
const editOpsgenie = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps> | ErrorResponse> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.put<PayloadProps>(`/channels/${props.id}`, {
|
||||
const response = await axios.put(`/channels/${props.id}`, {
|
||||
name: props.name,
|
||||
opsgenie_configs: [
|
||||
{
|
||||
@@ -25,12 +25,13 @@ const editOpsgenie = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editPager';
|
||||
|
||||
const editPager = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.put<PayloadProps>(`/channels/${props.id}`, {
|
||||
const response = await axios.put(`/channels/${props.id}`, {
|
||||
name: props.name,
|
||||
pagerduty_configs: [
|
||||
{
|
||||
@@ -29,12 +29,13 @@ const editPager = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editSlack';
|
||||
|
||||
const editSlack = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.put<PayloadProps>(`/channels/${props.id}`, {
|
||||
const response = await axios.put(`/channels/${props.id}`, {
|
||||
name: props.name,
|
||||
slack_configs: [
|
||||
{
|
||||
@@ -22,12 +22,13 @@ const editSlack = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/editWebhook';
|
||||
|
||||
const editWebhook = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
let httpConfig = {};
|
||||
const username = props.username ? props.username.trim() : '';
|
||||
@@ -28,7 +28,7 @@ const editWebhook = async (
|
||||
};
|
||||
}
|
||||
|
||||
const response = await axios.put<PayloadProps>(`/channels/${props.id}`, {
|
||||
const response = await axios.put(`/channels/${props.id}`, {
|
||||
name: props.name,
|
||||
webhook_configs: [
|
||||
{
|
||||
@@ -40,12 +40,13 @@ const editWebhook = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/get';
|
||||
import { Channels } from 'types/api/channels/getAll';
|
||||
|
||||
const get = async (props: Props): Promise<SuccessResponseV2<Channels>> => {
|
||||
const get = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>(`/channels/${props.id}`);
|
||||
const response = await axios.get(`/channels/${props.id}`);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { Channels, PayloadProps } from 'types/api/channels/getAll';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps } from 'types/api/channels/getAll';
|
||||
|
||||
const getAll = async (): Promise<SuccessResponseV2<Channels[]>> => {
|
||||
const getAll = async (): Promise<
|
||||
SuccessResponse<PayloadProps> | ErrorResponse
|
||||
> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>('/channels');
|
||||
const response = await axios.get('/channels');
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createEmail';
|
||||
|
||||
const testEmail = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/testChannel', {
|
||||
const response = await axios.post('/testChannel', {
|
||||
name: props.name,
|
||||
email_configs: [
|
||||
{
|
||||
@@ -21,12 +21,13 @@ const testEmail = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createMsTeams';
|
||||
|
||||
const testMsTeams = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/testChannel', {
|
||||
const response = await axios.post('/testChannel', {
|
||||
name: props.name,
|
||||
msteamsv2_configs: [
|
||||
{
|
||||
@@ -21,12 +21,13 @@ const testMsTeams = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createOpsgenie';
|
||||
|
||||
const testOpsgenie = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/testChannel', {
|
||||
const response = await axios.post('/testChannel', {
|
||||
name: props.name,
|
||||
opsgenie_configs: [
|
||||
{
|
||||
@@ -24,12 +24,13 @@ const testOpsgenie = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createPager';
|
||||
|
||||
const testPager = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/testChannel', {
|
||||
const response = await axios.post('/testChannel', {
|
||||
name: props.name,
|
||||
pagerduty_configs: [
|
||||
{
|
||||
@@ -29,12 +29,13 @@ const testPager = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createSlack';
|
||||
|
||||
const testSlack = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>('/testChannel', {
|
||||
const response = await axios.post('/testChannel', {
|
||||
name: props.name,
|
||||
slack_configs: [
|
||||
{
|
||||
@@ -22,12 +22,13 @@ const testSlack = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/channels/createWebhook';
|
||||
|
||||
const testWebhook = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
let httpConfig = {};
|
||||
const username = props.username ? props.username.trim() : '';
|
||||
@@ -28,7 +28,7 @@ const testWebhook = async (
|
||||
};
|
||||
}
|
||||
|
||||
const response = await axios.post<PayloadProps>('/testChannel', {
|
||||
const response = await axios.post('/testChannel', {
|
||||
name: props.name,
|
||||
webhook_configs: [
|
||||
{
|
||||
@@ -40,12 +40,13 @@ const testWebhook = async (
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
throw error;
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import getLocalStorageApi from 'api/browser/localstorage/get';
|
||||
import loginApi from 'api/v1/user/login';
|
||||
import loginApi from 'api/user/login';
|
||||
import afterLogin from 'AppRoutes/utils';
|
||||
import axios, { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
|
||||
import { ENVIRONMENT } from 'constants/env';
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export interface InspectMetricsRequest {
|
||||
metricName: string;
|
||||
start: number;
|
||||
end: number;
|
||||
filters: TagFilter;
|
||||
}
|
||||
|
||||
export interface InspectMetricsResponse {
|
||||
status: string;
|
||||
data: {
|
||||
series: InspectMetricsSeries[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface InspectMetricsSeries {
|
||||
title?: string;
|
||||
strokeColor?: string;
|
||||
labels: Record<string, string>;
|
||||
labelsArray: Array<Record<string, string>>;
|
||||
values: InspectMetricsTimestampValue[];
|
||||
}
|
||||
|
||||
interface InspectMetricsTimestampValue {
|
||||
timestamp: number;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export const getInspectMetricsDetails = async (
|
||||
request: InspectMetricsRequest,
|
||||
signal?: AbortSignal,
|
||||
headers?: Record<string, string>,
|
||||
): Promise<SuccessResponse<InspectMetricsResponse> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post(`/metrics/inspect`, request, {
|
||||
signal,
|
||||
headers,
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
CreateFunnelPayload,
|
||||
CreateFunnelResponse,
|
||||
FunnelData,
|
||||
FunnelStepData,
|
||||
} from 'types/api/traceFunnels';
|
||||
|
||||
const FUNNELS_BASE_PATH = '/trace-funnels';
|
||||
@@ -14,7 +13,7 @@ export const createFunnel = async (
|
||||
payload: CreateFunnelPayload,
|
||||
): Promise<SuccessResponse<CreateFunnelResponse> | ErrorResponse> => {
|
||||
const response: AxiosResponse = await axios.post(
|
||||
`${FUNNELS_BASE_PATH}/new`,
|
||||
`${FUNNELS_BASE_PATH}/new-funnel`,
|
||||
payload,
|
||||
);
|
||||
|
||||
@@ -26,45 +25,60 @@ export const createFunnel = async (
|
||||
};
|
||||
};
|
||||
|
||||
export const getFunnelsList = async (): Promise<
|
||||
interface GetFunnelsListParams {
|
||||
search?: string;
|
||||
}
|
||||
|
||||
export const getFunnelsList = async ({
|
||||
search = '',
|
||||
}: GetFunnelsListParams = {}): Promise<
|
||||
SuccessResponse<FunnelData[]> | ErrorResponse
|
||||
> => {
|
||||
const response: AxiosResponse = await axios.get(`${FUNNELS_BASE_PATH}/list`);
|
||||
const params = new URLSearchParams();
|
||||
if (search.length) {
|
||||
params.set('search', search);
|
||||
}
|
||||
|
||||
const response: AxiosResponse = await axios.get(
|
||||
`${FUNNELS_BASE_PATH}/list${
|
||||
params.toString() ? `?${params.toString()}` : ''
|
||||
}`,
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: '',
|
||||
payload: response.data.data,
|
||||
payload: response.data,
|
||||
};
|
||||
};
|
||||
|
||||
export const getFunnelById = async (
|
||||
funnelId?: string,
|
||||
funnelId: string,
|
||||
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
|
||||
const response: AxiosResponse = await axios.get(
|
||||
`${FUNNELS_BASE_PATH}/${funnelId}`,
|
||||
`${FUNNELS_BASE_PATH}/get/${funnelId}`,
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: '',
|
||||
payload: response.data.data,
|
||||
payload: response.data,
|
||||
};
|
||||
};
|
||||
|
||||
export interface RenameFunnelPayload {
|
||||
funnel_id: string;
|
||||
interface RenameFunnelPayload {
|
||||
id: string;
|
||||
funnel_name: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export const renameFunnel = async (
|
||||
payload: RenameFunnelPayload,
|
||||
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
|
||||
const response: AxiosResponse = await axios.put(
|
||||
`${FUNNELS_BASE_PATH}/${payload.funnel_id}`,
|
||||
payload,
|
||||
`${FUNNELS_BASE_PATH}/${payload.id}/update`,
|
||||
{ funnel_name: payload.funnel_name },
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -83,7 +97,7 @@ export const deleteFunnel = async (
|
||||
payload: DeleteFunnelPayload,
|
||||
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
|
||||
const response: AxiosResponse = await axios.delete(
|
||||
`${FUNNELS_BASE_PATH}/${payload.id}`,
|
||||
`${FUNNELS_BASE_PATH}/delete/${payload.id}`,
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -93,247 +107,3 @@ export const deleteFunnel = async (
|
||||
payload: response.data,
|
||||
};
|
||||
};
|
||||
|
||||
export interface UpdateFunnelStepsPayload {
|
||||
funnel_id: string;
|
||||
steps: FunnelStepData[];
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export const updateFunnelSteps = async (
|
||||
payload: UpdateFunnelStepsPayload,
|
||||
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
|
||||
const response: AxiosResponse = await axios.put(
|
||||
`${FUNNELS_BASE_PATH}/steps/update`,
|
||||
payload,
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Funnel steps updated successfully',
|
||||
payload: response.data.data,
|
||||
};
|
||||
};
|
||||
|
||||
export interface ValidateFunnelPayload {
|
||||
start_time: number;
|
||||
end_time: number;
|
||||
}
|
||||
|
||||
export interface ValidateFunnelResponse {
|
||||
status: string;
|
||||
data: Array<{
|
||||
timestamp: string;
|
||||
data: {
|
||||
trace_id: string;
|
||||
};
|
||||
}> | null;
|
||||
}
|
||||
|
||||
export const validateFunnelSteps = async (
|
||||
funnelId: string,
|
||||
payload: ValidateFunnelPayload,
|
||||
signal?: AbortSignal,
|
||||
): Promise<SuccessResponse<ValidateFunnelResponse> | ErrorResponse> => {
|
||||
const response = await axios.post(
|
||||
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/validate`,
|
||||
payload,
|
||||
{ signal },
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: '',
|
||||
payload: response.data,
|
||||
};
|
||||
};
|
||||
|
||||
export interface UpdateFunnelStepDetailsPayload {
|
||||
funnel_id: string;
|
||||
steps: Array<{
|
||||
step_name: string;
|
||||
description: string;
|
||||
}>;
|
||||
updated_at: number;
|
||||
}
|
||||
|
||||
interface UpdateFunnelDescriptionPayload {
|
||||
funnel_id: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export const saveFunnelDescription = async (
|
||||
payload: UpdateFunnelDescriptionPayload,
|
||||
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
|
||||
const response: AxiosResponse = await axios.post(
|
||||
`${FUNNELS_BASE_PATH}/save`,
|
||||
payload,
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Funnel description updated successfully',
|
||||
payload: response.data,
|
||||
};
|
||||
};
|
||||
|
||||
export interface FunnelOverviewPayload {
|
||||
start_time: number;
|
||||
end_time: number;
|
||||
step_start?: number;
|
||||
step_end?: number;
|
||||
}
|
||||
|
||||
export interface FunnelOverviewResponse {
|
||||
status: string;
|
||||
data: Array<{
|
||||
timestamp: string;
|
||||
data: {
|
||||
avg_duration: number;
|
||||
avg_rate: number;
|
||||
conversion_rate: number | null;
|
||||
errors: number;
|
||||
p99_latency: number;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
export const getFunnelOverview = async (
|
||||
funnelId: string,
|
||||
payload: FunnelOverviewPayload,
|
||||
signal?: AbortSignal,
|
||||
): Promise<SuccessResponse<FunnelOverviewResponse> | ErrorResponse> => {
|
||||
const response = await axios.post(
|
||||
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/overview`,
|
||||
payload,
|
||||
{
|
||||
signal,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: '',
|
||||
payload: response.data,
|
||||
};
|
||||
};
|
||||
|
||||
export interface SlowTracesPayload {
|
||||
start_time: number;
|
||||
end_time: number;
|
||||
step_a_order: number;
|
||||
step_b_order: number;
|
||||
}
|
||||
|
||||
export interface SlowTraceData {
|
||||
status: string;
|
||||
data: Array<{
|
||||
timestamp: string;
|
||||
data: {
|
||||
duration_ms: string;
|
||||
span_count: number;
|
||||
trace_id: string;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
export const getFunnelSlowTraces = async (
|
||||
funnelId: string,
|
||||
payload: SlowTracesPayload,
|
||||
signal?: AbortSignal,
|
||||
): Promise<SuccessResponse<SlowTraceData> | ErrorResponse> => {
|
||||
const response = await axios.post(
|
||||
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/slow-traces`,
|
||||
payload,
|
||||
{
|
||||
signal,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: '',
|
||||
payload: response.data,
|
||||
};
|
||||
};
|
||||
export interface ErrorTracesPayload {
|
||||
start_time: number;
|
||||
end_time: number;
|
||||
step_a_order: number;
|
||||
step_b_order: number;
|
||||
}
|
||||
|
||||
export interface ErrorTraceData {
|
||||
status: string;
|
||||
data: Array<{
|
||||
timestamp: string;
|
||||
data: {
|
||||
duration_ms: string;
|
||||
span_count: number;
|
||||
trace_id: string;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
export const getFunnelErrorTraces = async (
|
||||
funnelId: string,
|
||||
payload: ErrorTracesPayload,
|
||||
signal?: AbortSignal,
|
||||
): Promise<SuccessResponse<ErrorTraceData> | ErrorResponse> => {
|
||||
const response: AxiosResponse = await axios.post(
|
||||
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/error-traces`,
|
||||
payload,
|
||||
{
|
||||
signal,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: '',
|
||||
payload: response.data,
|
||||
};
|
||||
};
|
||||
|
||||
export interface FunnelStepsPayload {
|
||||
start_time: number;
|
||||
end_time: number;
|
||||
}
|
||||
|
||||
export interface FunnelStepGraphMetrics {
|
||||
[key: `total_s${number}_spans`]: number;
|
||||
[key: `total_s${number}_errored_spans`]: number;
|
||||
}
|
||||
|
||||
export interface FunnelStepsResponse {
|
||||
status: string;
|
||||
data: Array<{
|
||||
timestamp: string;
|
||||
data: FunnelStepGraphMetrics;
|
||||
}>;
|
||||
}
|
||||
|
||||
export const getFunnelSteps = async (
|
||||
funnelId: string,
|
||||
payload: FunnelStepsPayload,
|
||||
signal?: AbortSignal,
|
||||
): Promise<SuccessResponse<FunnelStepsResponse> | ErrorResponse> => {
|
||||
const response = await axios.post(
|
||||
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/steps`,
|
||||
payload,
|
||||
{ signal },
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: '',
|
||||
payload: response.data,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ const getPreference = async (): Promise<
|
||||
SuccessResponse<PayloadProps> | ErrorResponse
|
||||
> => {
|
||||
try {
|
||||
const response = await axios.get(`/user/preferences`);
|
||||
const response = await axios.get(`/userPreferences`);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
@@ -1,26 +0,0 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import { PayloadProps, Props } from 'types/api/user/login';
|
||||
|
||||
const login = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<PayloadProps>> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>(`/login`, {
|
||||
...props,
|
||||
});
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
// this line is never reached but ts isn't detecting the never type properly for the ErrorResponseHandlerV2
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export default login;
|
||||
@@ -9,27 +9,18 @@ import { useCeleryFilterOptions } from 'components/CeleryTask/useCeleryFilterOpt
|
||||
import { SelectMaxTagPlaceholder } from 'components/MessagingQueues/MQCommon/MQCommon';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
|
||||
export interface SelectOptionConfig {
|
||||
interface SelectOptionConfig {
|
||||
placeholder: string;
|
||||
queryParam: QueryParams;
|
||||
filterType: string | string[];
|
||||
shouldSetQueryParams?: boolean;
|
||||
onChange?: (value: string | string[]) => void;
|
||||
values?: string | string[];
|
||||
isMultiple?: boolean;
|
||||
}
|
||||
|
||||
export function FilterSelect({
|
||||
function FilterSelect({
|
||||
placeholder,
|
||||
queryParam,
|
||||
filterType,
|
||||
values,
|
||||
shouldSetQueryParams,
|
||||
onChange,
|
||||
isMultiple,
|
||||
}: SelectOptionConfig): JSX.Element {
|
||||
const { handleSearch, isFetching, options } = useCeleryFilterOptions(
|
||||
filterType,
|
||||
@@ -39,73 +30,20 @@ export function FilterSelect({
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
|
||||
// Add state to track the current search input
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
|
||||
// Use externally provided `values` if `shouldSetQueryParams` is false, otherwise get from URL params.
|
||||
const selectValue =
|
||||
!shouldSetQueryParams && !!values?.length
|
||||
? values
|
||||
: getValuesFromQueryParams(queryParam, urlQuery) || [];
|
||||
|
||||
// Memoize options to include the typed value if not present
|
||||
const mergedOptions = useMemo(() => {
|
||||
if (
|
||||
!!searchValue.trim().length &&
|
||||
!options.some((opt) => opt.value === searchValue)
|
||||
) {
|
||||
return [{ value: searchValue, label: searchValue }, ...options];
|
||||
}
|
||||
return options;
|
||||
}, [options, searchValue]);
|
||||
|
||||
const handleSelectChange = useCallback(
|
||||
(value: string | string[]): void => {
|
||||
handleSearch('');
|
||||
setSearchValue(''); // Clear search value after selection
|
||||
if (shouldSetQueryParams) {
|
||||
setQueryParamsFromOptions(
|
||||
value as string[],
|
||||
urlQuery,
|
||||
history,
|
||||
location,
|
||||
queryParam,
|
||||
);
|
||||
}
|
||||
onChange?.(value);
|
||||
},
|
||||
[
|
||||
handleSearch,
|
||||
shouldSetQueryParams,
|
||||
urlQuery,
|
||||
history,
|
||||
location,
|
||||
queryParam,
|
||||
onChange,
|
||||
],
|
||||
);
|
||||
|
||||
// Update searchValue on user input
|
||||
const handleSearchInput = (input: string): void => {
|
||||
setSearchValue(input);
|
||||
handleSearch(input);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
key={filterType.toString()}
|
||||
placeholder={placeholder}
|
||||
showSearch
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...(isMultiple ? { mode: 'multiple' } : {})}
|
||||
options={mergedOptions}
|
||||
mode="multiple"
|
||||
options={options}
|
||||
loading={isFetching}
|
||||
className="config-select-option"
|
||||
onSearch={handleSearchInput}
|
||||
onSearch={handleSearch}
|
||||
maxTagCount={4}
|
||||
allowClear
|
||||
maxTagPlaceholder={SelectMaxTagPlaceholder}
|
||||
value={selectValue}
|
||||
value={getValuesFromQueryParams(queryParam, urlQuery) || []}
|
||||
notFoundContent={
|
||||
isFetching ? (
|
||||
<span>
|
||||
@@ -115,18 +53,14 @@ export function FilterSelect({
|
||||
<span>No {placeholder} found</span>
|
||||
)
|
||||
}
|
||||
onChange={handleSelectChange}
|
||||
onChange={(value): void => {
|
||||
handleSearch('');
|
||||
setQueryParamsFromOptions(value, urlQuery, history, location, queryParam);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
FilterSelect.defaultProps = {
|
||||
shouldSetQueryParams: true,
|
||||
onChange: (): void => {},
|
||||
values: [],
|
||||
isMultiple: true,
|
||||
};
|
||||
|
||||
function CeleryOverviewConfigOptions(): JSX.Element {
|
||||
const selectConfigs: SelectOptionConfig[] = [
|
||||
{
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
.change-percentage-pill {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 1px 4px;
|
||||
border-radius: 50px;
|
||||
|
||||
&__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
&__label {
|
||||
font-family: 'Geist Mono';
|
||||
font-size: 12px;
|
||||
line-height: normal;
|
||||
}
|
||||
&--positive {
|
||||
.change-percentage-pill {
|
||||
&__icon {
|
||||
color: var(--bg-forest-500);
|
||||
}
|
||||
&__label {
|
||||
color: var(--bg-forest-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
&--negative {
|
||||
background: rgba(229, 72, 77, 0.1);
|
||||
|
||||
.change-percentage-pill {
|
||||
&__icon {
|
||||
color: var(--bg-cherry-500);
|
||||
}
|
||||
&__label {
|
||||
color: var(--bg-cherry-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import './ChangePercentagePill.styles.scss';
|
||||
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import cx from 'classnames';
|
||||
import { ArrowDown, ArrowUp } from 'lucide-react';
|
||||
|
||||
interface ChangePercentagePillProps {
|
||||
percentage: number;
|
||||
direction: number;
|
||||
}
|
||||
function ChangePercentagePill({
|
||||
percentage,
|
||||
direction,
|
||||
}: ChangePercentagePillProps): JSX.Element | null {
|
||||
if (direction === 0 || percentage === 0) {
|
||||
return null;
|
||||
}
|
||||
const isPositive = direction > 0;
|
||||
return (
|
||||
<div
|
||||
className={cx('change-percentage-pill', {
|
||||
'change-percentage-pill--positive': isPositive,
|
||||
'change-percentage-pill--negative': !isPositive,
|
||||
})}
|
||||
>
|
||||
<div className="change-percentage-pill__icon">
|
||||
{isPositive ? (
|
||||
<ArrowUp size={12} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
<ArrowDown size={12} color={Color.BG_CHERRY_500} />
|
||||
)}
|
||||
</div>
|
||||
<div className="change-percentage-pill__label">{percentage}%</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChangePercentagePill;
|
||||
@@ -1,11 +1,11 @@
|
||||
.dropdown-button {
|
||||
color: #fff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dropdown-button--dark {
|
||||
color: #000;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
.learn-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 0;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 18px; /* 128.571% */
|
||||
letter-spacing: -0.07px;
|
||||
&,
|
||||
&:hover {
|
||||
color: var(--bg-robin-400) !important;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 0;
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 18px; /* 128.571% */
|
||||
letter-spacing: -0.07px;
|
||||
&,&:hover {
|
||||
color: var(--bg-robin-400) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
.quick-filters-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
.quick-filters-settings-container {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.quick-filters {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-right: 1px solid var(--bg-slate-400);
|
||||
color: var(--bg-vanilla-100);
|
||||
|
||||
@@ -52,7 +62,7 @@
|
||||
.right-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -67,6 +77,22 @@
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.right-action-icon-container {
|
||||
display: flex;
|
||||
padding: 2px;
|
||||
.settings-icon {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover {
|
||||
background: var(--bg-slate-500);
|
||||
}
|
||||
&.active {
|
||||
background: var(--bg-slate-500);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,36 @@ import {
|
||||
VerticalAlignTopOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Tooltip, Typography } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import TypicalOverlayScrollbar from 'components/TypicalOverlayScrollbar/TypicalOverlayScrollbar';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { cloneDeep, isFunction } from 'lodash-es';
|
||||
import { Settings2 as SettingsIcon } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import Checkbox from './FilterRenderers/Checkbox/Checkbox';
|
||||
import Slider from './FilterRenderers/Slider/Slider';
|
||||
import useFilterConfig from './hooks/useFilterConfig';
|
||||
import QuickFiltersSettings from './QuickFiltersSettings/QuickFiltersSettings';
|
||||
import { FiltersType, IQuickFiltersProps, QuickFiltersSource } from './types';
|
||||
|
||||
export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
const { config, handleFilterVisibilityChange, source, onFilterChange } = props;
|
||||
const {
|
||||
config,
|
||||
handleFilterVisibilityChange,
|
||||
source,
|
||||
onFilterChange,
|
||||
signal,
|
||||
} = props;
|
||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||
|
||||
const {
|
||||
filterConfig,
|
||||
isDynamicFilters,
|
||||
customFilters,
|
||||
setIsStale,
|
||||
} = useFilterConfig({ signal, config });
|
||||
|
||||
const {
|
||||
currentQuery,
|
||||
@@ -63,64 +82,99 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
currentQuery.builder.queryData?.[lastUsedQuery || 0]?.queryName;
|
||||
|
||||
return (
|
||||
<div className="quick-filters">
|
||||
{source !== QuickFiltersSource.INFRA_MONITORING &&
|
||||
source !== QuickFiltersSource.API_MONITORING && (
|
||||
<section className="header">
|
||||
<section className="left-actions">
|
||||
<FilterOutlined />
|
||||
<Typography.Text className="text">
|
||||
{lastQueryName ? 'Filters for' : 'Filters'}
|
||||
</Typography.Text>
|
||||
{lastQueryName && (
|
||||
<Tooltip title={`Filter currently in sync with query ${lastQueryName}`}>
|
||||
<Typography.Text className="sync-tag">{lastQueryName}</Typography.Text>
|
||||
<div className="quick-filters-container">
|
||||
<div className="quick-filters">
|
||||
{source !== QuickFiltersSource.INFRA_MONITORING &&
|
||||
source !== QuickFiltersSource.API_MONITORING && (
|
||||
<section className="header">
|
||||
<section className="left-actions">
|
||||
<FilterOutlined />
|
||||
<Typography.Text className="text">
|
||||
{lastQueryName ? 'Filters for' : 'Filters'}
|
||||
</Typography.Text>
|
||||
{lastQueryName && (
|
||||
<Tooltip
|
||||
title={`Filter currently in sync with query ${lastQueryName}`}
|
||||
>
|
||||
<Typography.Text className="sync-tag">
|
||||
{lastQueryName}
|
||||
</Typography.Text>
|
||||
</Tooltip>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="right-actions">
|
||||
<Tooltip title="Reset All">
|
||||
<div className="right-action-icon-container">
|
||||
<SyncOutlined className="sync-icon" onClick={handleReset} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip title="Collapse Filters">
|
||||
<div className="right-action-icon-container">
|
||||
<VerticalAlignTopOutlined
|
||||
rotate={270}
|
||||
onClick={handleFilterVisibilityChange}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
{isDynamicFilters && (
|
||||
<Tooltip title="Settings">
|
||||
<div
|
||||
className={classNames('right-action-icon-container', {
|
||||
active: isSettingsOpen,
|
||||
})}
|
||||
>
|
||||
<SettingsIcon
|
||||
className="settings-icon"
|
||||
width={14}
|
||||
height={14}
|
||||
onClick={(): void => setIsSettingsOpen(true)}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</section>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section className="right-actions">
|
||||
<Tooltip title="Reset All">
|
||||
<SyncOutlined className="sync-icon" onClick={handleReset} />
|
||||
</Tooltip>
|
||||
<div className="divider-filter" />
|
||||
<Tooltip title="Collapse Filters">
|
||||
<VerticalAlignTopOutlined
|
||||
rotate={270}
|
||||
onClick={handleFilterVisibilityChange}
|
||||
/>
|
||||
</Tooltip>
|
||||
</section>
|
||||
<TypicalOverlayScrollbar>
|
||||
<section className="filters">
|
||||
{filterConfig.map((filter) => {
|
||||
switch (filter.type) {
|
||||
case FiltersType.CHECKBOX:
|
||||
return (
|
||||
<Checkbox
|
||||
source={source}
|
||||
filter={filter}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
);
|
||||
case FiltersType.SLIDER:
|
||||
return <Slider filter={filter} />;
|
||||
// eslint-disable-next-line sonarjs/no-duplicated-branches
|
||||
default:
|
||||
return (
|
||||
<Checkbox
|
||||
source={source}
|
||||
filter={filter}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</section>
|
||||
</TypicalOverlayScrollbar>
|
||||
</div>
|
||||
<div className="quick-filters-settings-container">
|
||||
{isSettingsOpen && (
|
||||
<QuickFiltersSettings
|
||||
signal={signal}
|
||||
setIsSettingsOpen={setIsSettingsOpen}
|
||||
customFilters={customFilters}
|
||||
setIsStale={setIsStale}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TypicalOverlayScrollbar>
|
||||
<section className="filters">
|
||||
{config.map((filter) => {
|
||||
switch (filter.type) {
|
||||
case FiltersType.CHECKBOX:
|
||||
return (
|
||||
<Checkbox
|
||||
source={source}
|
||||
filter={filter}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
);
|
||||
case FiltersType.SLIDER:
|
||||
return <Slider filter={filter} />;
|
||||
// eslint-disable-next-line sonarjs/no-duplicated-branches
|
||||
default:
|
||||
return (
|
||||
<Checkbox
|
||||
source={source}
|
||||
filter={filter}
|
||||
onFilterChange={onFilterChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</section>
|
||||
</TypicalOverlayScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
.qf-filter-item {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
// cursor: grab;
|
||||
user-select: none;
|
||||
|
||||
.qf-filter-content{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
// &:active {
|
||||
// cursor: grabbing;
|
||||
// }
|
||||
|
||||
&.drag-enabled {
|
||||
cursor: grab;
|
||||
&:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import './AddedFilters.styles.scss';
|
||||
|
||||
import {
|
||||
closestCenter,
|
||||
DndContext,
|
||||
DragEndEvent,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
} from '@dnd-kit/core';
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
useSortable,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { GripVertical } from 'lucide-react';
|
||||
import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters';
|
||||
|
||||
function SortableFilter({ filter }: { filter: FilterType }): JSX.Element {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
} = useSortable({ id: filter.key });
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className="qf-filter-item drag-enabled" // TODO: handle drag disabled when searching
|
||||
>
|
||||
<div className="qf-filter-content">
|
||||
<GripVertical size={16} />
|
||||
{filter.key}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AddedFilters({
|
||||
addedFilters,
|
||||
setAddedFilters,
|
||||
}: {
|
||||
addedFilters: FilterType[];
|
||||
setAddedFilters: React.Dispatch<React.SetStateAction<FilterType[]>>;
|
||||
}): JSX.Element {
|
||||
const sensors = useSensors(useSensor(PointerSensor));
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent): void => {
|
||||
const { active, over } = event;
|
||||
|
||||
if (over && active.id !== over.id) {
|
||||
setAddedFilters((items) => {
|
||||
const oldIndex = items.findIndex((item) => item.key === active.id);
|
||||
const newIndex = items.findIndex((item) => item.key === over.id);
|
||||
|
||||
return arrayMove(items, oldIndex, newIndex);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="qf-filters added-filters">
|
||||
<div className="qf-filters-header">ADDED FILTERS</div>
|
||||
<div className="qf-added-filters-list">
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
items={addedFilters.map((f) => f.key)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
{addedFilters.map((filter) => (
|
||||
<SortableFilter key={filter.key} filter={filter} />
|
||||
))}
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddedFilters;
|
||||
@@ -0,0 +1,10 @@
|
||||
function OtherFilters(): JSX.Element {
|
||||
return (
|
||||
<div className="qf-filters other-filters">
|
||||
<div className="qf-filters-header">OTHER FILTERS</div>
|
||||
<div className="qf-other-filters-list" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default OtherFilters;
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
.quick-filters-settings {
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
width: 342px;
|
||||
height: 100%;
|
||||
background: var(--bg-slate-500);
|
||||
|
||||
.qf-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10.5px;
|
||||
border-bottom: 1px solid var(--bg-slate-400);
|
||||
|
||||
.qf-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.qf-header-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.qf-footer {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
border-top: 1px solid var(--bg-slate-400);
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50%;
|
||||
|
||||
.ant-btn-icon {
|
||||
margin: 3px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add light theme
|
||||
// .light {
|
||||
// .quick-filters-settings {
|
||||
// background: var(--bg-slate-50);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
//ADDED FILTERS AND OTHER FILTERS COMMON STYLES
|
||||
|
||||
.qf-filters {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.qf-filters-header {
|
||||
color: var(--bg-slate-50);
|
||||
border-top: 1px solid var(--bg-slate-400);
|
||||
font-family: Inter;
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0.88px;
|
||||
text-transform: uppercase;
|
||||
margin-top: 12px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import './QuickFiltersSettings.styles.scss';
|
||||
|
||||
import Button from 'antd/es/button';
|
||||
import { CheckIcon, TableColumnsSplit, XIcon } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters';
|
||||
|
||||
import { SignalType } from '../types';
|
||||
import AddedFilters from './AddedFilters';
|
||||
import useQuickFilterSettings from './hooks/useQuickFilterSettings';
|
||||
import OtherFilters from './OtherFilters';
|
||||
|
||||
function QuickFiltersSettings({
|
||||
signal,
|
||||
setIsSettingsOpen,
|
||||
customFilters,
|
||||
setIsStale,
|
||||
}: {
|
||||
signal: SignalType | undefined;
|
||||
setIsSettingsOpen: (isSettingsOpen: boolean) => void;
|
||||
customFilters: FilterType[];
|
||||
setIsStale: (isStale: boolean) => void;
|
||||
}): JSX.Element {
|
||||
const {
|
||||
handleSettingsClose,
|
||||
handleDiscardChanges,
|
||||
addedFilters,
|
||||
setAddedFilters,
|
||||
handleSaveChanges,
|
||||
isUpdatingCustomFilters,
|
||||
} = useQuickFilterSettings({
|
||||
setIsSettingsOpen,
|
||||
customFilters,
|
||||
setIsStale,
|
||||
signal,
|
||||
});
|
||||
|
||||
const hasUnsavedChanges = useMemo(
|
||||
() =>
|
||||
// check if both arrays have the same length and same order of elements
|
||||
!(
|
||||
addedFilters.length === customFilters.length &&
|
||||
addedFilters.every(
|
||||
(filter, index) => filter.key === customFilters[index].key,
|
||||
)
|
||||
),
|
||||
[addedFilters, customFilters],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="quick-filters-settings">
|
||||
<div className="qf-header">
|
||||
<div className="qf-title">
|
||||
<TableColumnsSplit width={16} height={16} />
|
||||
Edit quick filters
|
||||
</div>
|
||||
<XIcon
|
||||
className="qf-header-icon"
|
||||
width={16}
|
||||
height={16}
|
||||
onClick={handleSettingsClose}
|
||||
/>
|
||||
</div>
|
||||
<AddedFilters
|
||||
addedFilters={addedFilters}
|
||||
setAddedFilters={setAddedFilters}
|
||||
/>
|
||||
<OtherFilters />
|
||||
{hasUnsavedChanges && (
|
||||
<div className="qf-footer">
|
||||
<Button
|
||||
type="default"
|
||||
onClick={handleDiscardChanges}
|
||||
icon={<XIcon width={16} height={16} />}
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleSaveChanges}
|
||||
icon={<CheckIcon width={16} height={16} />}
|
||||
loading={isUpdatingCustomFilters}
|
||||
>
|
||||
Save changes
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default QuickFiltersSettings;
|
||||
@@ -0,0 +1,82 @@
|
||||
import updateCustomFiltersAPI from 'api/quickFilters/updateCustomFilters';
|
||||
import { AxiosError } from 'axios';
|
||||
import { SignalType } from 'components/QuickFilters/types';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useMutation } from 'react-query';
|
||||
import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters';
|
||||
|
||||
interface UseQuickFilterSettingsProps {
|
||||
setIsSettingsOpen: (isSettingsOpen: boolean) => void;
|
||||
customFilters: FilterType[];
|
||||
setIsStale: (isStale: boolean) => void;
|
||||
signal?: SignalType;
|
||||
}
|
||||
|
||||
interface UseQuickFilterSettingsReturn {
|
||||
addedFilters: FilterType[];
|
||||
setAddedFilters: React.Dispatch<React.SetStateAction<FilterType[]>>;
|
||||
handleSettingsClose: () => void;
|
||||
handleDiscardChanges: () => void;
|
||||
handleSaveChanges: () => void;
|
||||
isUpdatingCustomFilters: boolean;
|
||||
}
|
||||
|
||||
const useQuickFilterSettings = ({
|
||||
customFilters,
|
||||
setIsSettingsOpen,
|
||||
setIsStale,
|
||||
signal,
|
||||
}: UseQuickFilterSettingsProps): UseQuickFilterSettingsReturn => {
|
||||
const [addedFilters, setAddedFilters] = useState<FilterType[]>(customFilters);
|
||||
|
||||
const {
|
||||
mutate: updateCustomFilters,
|
||||
isLoading: isUpdatingCustomFilters,
|
||||
} = useMutation(updateCustomFiltersAPI, {
|
||||
onSuccess: () => {
|
||||
// set isStale to true
|
||||
// close settings
|
||||
setIsStale(true);
|
||||
// display success toast
|
||||
},
|
||||
onError: (error: AxiosError) => {
|
||||
console.error('>>>>error', error);
|
||||
// display error toast
|
||||
// close settings
|
||||
},
|
||||
});
|
||||
|
||||
const handleSettingsClose = useCallback((): void => {
|
||||
setIsSettingsOpen(false);
|
||||
}, [setIsSettingsOpen]);
|
||||
|
||||
const handleDiscardChanges = useCallback((): void => {
|
||||
setAddedFilters(customFilters);
|
||||
}, [customFilters, setAddedFilters]);
|
||||
|
||||
const handleSaveChanges = useCallback((): void => {
|
||||
if (signal) {
|
||||
updateCustomFilters({
|
||||
data: {
|
||||
filters: addedFilters.map((filter) => ({
|
||||
key: filter.key,
|
||||
datatype: filter.dataType,
|
||||
type: filter.type,
|
||||
})),
|
||||
signal,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [addedFilters, signal, updateCustomFilters]);
|
||||
|
||||
return {
|
||||
handleSettingsClose,
|
||||
handleDiscardChanges,
|
||||
addedFilters,
|
||||
setAddedFilters,
|
||||
handleSaveChanges,
|
||||
isUpdatingCustomFilters,
|
||||
};
|
||||
};
|
||||
|
||||
export default useQuickFilterSettings;
|
||||
@@ -0,0 +1,62 @@
|
||||
import getCustomFilters from 'api/quickFilters/getCustomFilters';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import {
|
||||
Filter as FilterType,
|
||||
PayloadProps,
|
||||
} from 'types/api/quickFilters/getCustomFilters';
|
||||
|
||||
import { IQuickFiltersConfig, SignalType } from '../types';
|
||||
import { getFilterConfig } from '../utils';
|
||||
|
||||
interface UseFilterConfigProps {
|
||||
signal?: SignalType;
|
||||
config: IQuickFiltersConfig[];
|
||||
}
|
||||
interface UseFilterConfigReturn {
|
||||
filterConfig: IQuickFiltersConfig[];
|
||||
customFilters: FilterType[];
|
||||
setCustomFilters: React.Dispatch<React.SetStateAction<FilterType[]>>;
|
||||
isCustomFiltersLoading: boolean;
|
||||
isDynamicFilters: boolean;
|
||||
setIsStale: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const useFilterConfig = ({
|
||||
signal,
|
||||
config,
|
||||
}: UseFilterConfigProps): UseFilterConfigReturn => {
|
||||
const [customFilters, setCustomFilters] = useState<FilterType[]>([]);
|
||||
const [isStale, setIsStale] = useState(true);
|
||||
const isDynamicFilters = useMemo(() => customFilters.length > 0, [
|
||||
customFilters,
|
||||
]);
|
||||
const { isLoading: isCustomFiltersLoading } = useQuery<
|
||||
SuccessResponse<PayloadProps> | ErrorResponse,
|
||||
Error
|
||||
>(['addedFilters'], () => getCustomFilters({ signal: signal || '' }), {
|
||||
onSuccess: (data) => {
|
||||
if ('payload' in data && data.payload?.filters) {
|
||||
setCustomFilters(data.payload.filters || ([] as FilterType[]));
|
||||
}
|
||||
setIsStale(false);
|
||||
},
|
||||
enabled: !!signal && isStale,
|
||||
});
|
||||
const filterConfig = useMemo(() => getFilterConfig(customFilters, config), [
|
||||
config,
|
||||
customFilters,
|
||||
]);
|
||||
|
||||
return {
|
||||
filterConfig,
|
||||
customFilters,
|
||||
setCustomFilters,
|
||||
isCustomFiltersLoading,
|
||||
isDynamicFilters,
|
||||
setIsStale,
|
||||
};
|
||||
};
|
||||
|
||||
export default useFilterConfig;
|
||||
@@ -110,7 +110,6 @@ exports[`Quick Filters renders correctly with default props 1`] = `
|
||||
class="left-action"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-chevron-down"
|
||||
cursor="pointer"
|
||||
fill="none"
|
||||
@@ -348,7 +347,6 @@ exports[`Quick Filters renders correctly with default props 1`] = `
|
||||
class="left-action"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="lucide lucide-chevron-right"
|
||||
cursor="pointer"
|
||||
fill="none"
|
||||
|
||||
@@ -17,6 +17,13 @@ export enum SpecficFilterOperations {
|
||||
ONLY = 'ONLY',
|
||||
}
|
||||
|
||||
export enum SignalType {
|
||||
TRACES = 'traces',
|
||||
LOGS = 'logs',
|
||||
API_MONITORING = 'api_monitoring',
|
||||
EXCEPTIONS = 'exceptions',
|
||||
}
|
||||
|
||||
export interface IQuickFiltersConfig {
|
||||
type: FiltersType;
|
||||
title: string;
|
||||
@@ -33,6 +40,7 @@ export interface IQuickFiltersProps {
|
||||
handleFilterVisibilityChange: () => void;
|
||||
source: QuickFiltersSource;
|
||||
onFilterChange?: (query: Query) => void;
|
||||
signal?: SignalType;
|
||||
}
|
||||
|
||||
export enum QuickFiltersSource {
|
||||
|
||||
33
frontend/src/components/QuickFilters/utils.tsx
Normal file
33
frontend/src/components/QuickFilters/utils.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters';
|
||||
|
||||
import { FiltersType, IQuickFiltersConfig } from './types';
|
||||
|
||||
const getFilterName = (str: string): string =>
|
||||
// replace . and _ with space
|
||||
str.replace(/\./g, ' ').replace(/_/g, ' ');
|
||||
|
||||
export const getFilterConfig = (
|
||||
customFilters?: FilterType[],
|
||||
config?: IQuickFiltersConfig[],
|
||||
): IQuickFiltersConfig[] => {
|
||||
if (!customFilters?.length) {
|
||||
return config || [];
|
||||
}
|
||||
|
||||
return customFilters.map(
|
||||
(att, index) =>
|
||||
({
|
||||
type: FiltersType.CHECKBOX,
|
||||
title: getFilterName(att.key),
|
||||
attributeKey: {
|
||||
id: att.key,
|
||||
key: att.key,
|
||||
dataType: att.dataType,
|
||||
type: att.type,
|
||||
isColumn: att.isColumn,
|
||||
isJSON: att.isJSON,
|
||||
},
|
||||
defaultOpen: index === 0,
|
||||
} as IQuickFiltersConfig),
|
||||
);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user