mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-06 01:46:02 +08:00
commit
0ff4c040bf
34
.github/workflows/build.yaml
vendored
34
.github/workflows/build.yaml
vendored
@ -2,11 +2,12 @@ name: build-pipeline
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
|
- develop
|
||||||
- main
|
- main
|
||||||
- v*
|
- v*
|
||||||
paths:
|
paths:
|
||||||
- 'pkg/**'
|
- "pkg/**"
|
||||||
- 'frontend/**'
|
- "frontend/**"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get_filters:
|
get_filters:
|
||||||
@ -17,17 +18,17 @@ jobs:
|
|||||||
query-service: ${{ steps.filter.outputs.query-service }}
|
query-service: ${{ steps.filter.outputs.query-service }}
|
||||||
flattener: ${{ steps.filter.outputs.flattener }}
|
flattener: ${{ steps.filter.outputs.flattener }}
|
||||||
steps:
|
steps:
|
||||||
# For pull requests it's not necessary to checkout the code
|
# For pull requests it's not necessary to checkout the code
|
||||||
- uses: dorny/paths-filter@v2
|
- uses: dorny/paths-filter@v2
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
frontend:
|
frontend:
|
||||||
- 'frontend/**'
|
- 'frontend/**'
|
||||||
query-service:
|
query-service:
|
||||||
- 'pkg/query-service/**'
|
- 'pkg/query-service/**'
|
||||||
flattener:
|
flattener:
|
||||||
- 'pkg/processors/flattener/**'
|
- 'pkg/processors/flattener/**'
|
||||||
|
|
||||||
build-frontend:
|
build-frontend:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -39,12 +40,11 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: cd frontend && yarn install
|
run: cd frontend && yarn install
|
||||||
- name: Run Prettier
|
|
||||||
run: cd frontend && npm run prettify
|
|
||||||
continue-on-error: true
|
|
||||||
- name: Run ESLint
|
- name: Run ESLint
|
||||||
run: cd frontend && npm run lint
|
run: cd frontend && npm run lint
|
||||||
continue-on-error: true
|
- name: TSC
|
||||||
|
run: yarn tsc
|
||||||
|
working-directory: ./frontend
|
||||||
- name: Build frontend docker image
|
- name: Build frontend docker image
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
@ -17,19 +17,20 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.5.0
|
image: signoz/alertmanager:0.6.0
|
||||||
volumes:
|
volumes:
|
||||||
- ./alertmanager.yml:/prometheus/alertmanager.yml
|
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
command:
|
command:
|
||||||
- '--config.file=/prometheus/alertmanager.yml'
|
- --queryService.url=http://query-service:8080
|
||||||
- '--storage.path=/data'
|
- --storage.path=/data
|
||||||
|
depends_on:
|
||||||
|
- query-service
|
||||||
deploy:
|
deploy:
|
||||||
restart_policy:
|
restart_policy:
|
||||||
condition: on-failure
|
condition: on-failure
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.7.3
|
image: signoz/query-service:0.7.4
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
@ -48,10 +49,10 @@ services:
|
|||||||
restart_policy:
|
restart_policy:
|
||||||
condition: on-failure
|
condition: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
- clickhouse
|
- clickhouse
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.7.3
|
image: signoz/frontend:0.7.4
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
- query-service
|
||||||
ports:
|
ports:
|
||||||
|
@ -15,16 +15,17 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.5.0
|
image: signoz/alertmanager:0.6.0
|
||||||
volumes:
|
volumes:
|
||||||
- ./alertmanager.yml:/prometheus/alertmanager.yml
|
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
|
depends_on:
|
||||||
|
- query-service
|
||||||
command:
|
command:
|
||||||
- '--config.file=/prometheus/alertmanager.yml'
|
- --queryService.url=http://query-service:8080
|
||||||
- '--storage.path=/data'
|
- --storage.path=/data
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.7.3
|
image: signoz/query-service:0.7.4
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
volumes:
|
volumes:
|
||||||
@ -44,7 +45,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.7.3
|
image: signoz/frontend:0.7.4
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
- query-service
|
||||||
@ -66,7 +67,7 @@ services:
|
|||||||
# - "14268:14268" # Jaeger receiver
|
# - "14268:14268" # Jaeger receiver
|
||||||
# - "55678:55678" # OpenCensus receiver
|
# - "55678:55678" # OpenCensus receiver
|
||||||
# - "55679:55679" # zpages extension
|
# - "55679:55679" # zpages extension
|
||||||
# - "55680:55680" # OTLP gRPC legacy port
|
# - "55680:55680" # OTLP gRPC legacy receiver
|
||||||
# - "55681:55681" # OTLP HTTP legacy receiver
|
# - "55681:55681" # OTLP HTTP legacy receiver
|
||||||
mem_limit: 2000m
|
mem_limit: 2000m
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
@ -93,7 +94,7 @@ services:
|
|||||||
max-file: "3"
|
max-file: "3"
|
||||||
command: ["all"]
|
command: ["all"]
|
||||||
environment:
|
environment:
|
||||||
- JAEGER_ENDPOINT=http://otel-collector:14268/api/traces
|
- JAEGER_ENDPOINT=http://otel-collector:14268/api/traces
|
||||||
|
|
||||||
load-hotrod:
|
load-hotrod:
|
||||||
image: "grubykarol/locust:1.2.3-python3.9-alpine3.12"
|
image: "grubykarol/locust:1.2.3-python3.9-alpine3.12"
|
||||||
|
@ -15,19 +15,19 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.5.0
|
image: signoz/alertmanager:0.6.0
|
||||||
volumes:
|
volumes:
|
||||||
- ./alertmanager.yml:/prometheus/alertmanager.yml
|
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
|
depends_on:
|
||||||
|
- query-service
|
||||||
command:
|
command:
|
||||||
- '--config.file=/prometheus/alertmanager.yml'
|
- --queryService.url=http://query-service:8080
|
||||||
- '--storage.path=/data'
|
- --storage.path=/data
|
||||||
|
|
||||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
||||||
|
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.7.3
|
image: signoz/query-service:0.7.4
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
volumes:
|
volumes:
|
||||||
@ -40,14 +40,13 @@ services:
|
|||||||
- GODEBUG=netdns=go
|
- GODEBUG=netdns=go
|
||||||
- TELEMETRY_ENABLED=true
|
- TELEMETRY_ENABLED=true
|
||||||
- DEPLOYMENT_TYPE=docker-standalone-amd
|
- DEPLOYMENT_TYPE=docker-standalone-amd
|
||||||
|
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.7.3
|
image: signoz/frontend:0.7.4
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
- query-service
|
||||||
@ -69,7 +68,7 @@ services:
|
|||||||
# - "14268:14268" # Jaeger receiver
|
# - "14268:14268" # Jaeger receiver
|
||||||
# - "55678:55678" # OpenCensus receiver
|
# - "55678:55678" # OpenCensus receiver
|
||||||
# - "55679:55679" # zpages extension
|
# - "55679:55679" # zpages extension
|
||||||
# - "55680:55680" # OTLP gRPC legacy port
|
# - "55680:55680" # OTLP gRPC legacy receiver
|
||||||
# - "55681:55681" # OTLP HTTP legacy receiver
|
# - "55681:55681" # OTLP HTTP legacy receiver
|
||||||
mem_limit: 2000m
|
mem_limit: 2000m
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
@ -84,6 +84,23 @@ module.exports = {
|
|||||||
tsx: 'never',
|
tsx: 'never',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
|
||||||
|
'jsx-a11y/label-has-associated-control': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
required: {
|
||||||
|
some: ['nesting', 'id'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'jsx-a11y/label-has-for': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
required: {
|
||||||
|
some: ['nesting', 'id'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
// eslint rules need to remove
|
// eslint rules need to remove
|
||||||
'no-shadow': 'off',
|
'no-shadow': 'off',
|
||||||
|
4
frontend/.husky/pre-commit
Executable file
4
frontend/.husky/pre-commit
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
cd frontend && yarn lint-staged
|
@ -13,7 +13,9 @@
|
|||||||
"cypress:run": "cypress run",
|
"cypress:run": "cypress run",
|
||||||
"jest": "jest",
|
"jest": "jest",
|
||||||
"jest:coverage": "jest --coverage",
|
"jest:coverage": "jest --coverage",
|
||||||
"jest:watch": "jest --watch"
|
"jest:watch": "jest --watch",
|
||||||
|
"postinstall": "yarn husky:configure",
|
||||||
|
"husky:configure": "cd .. && husky install frontend/.husky"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.13.0"
|
"node": ">=12.13.0"
|
||||||
@ -21,6 +23,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/colors": "^6.0.0",
|
||||||
"@ant-design/icons": "^4.6.2",
|
"@ant-design/icons": "^4.6.2",
|
||||||
"@grafana/data": "^8.4.3",
|
"@grafana/data": "^8.4.3",
|
||||||
"@monaco-editor/react": "^4.3.1",
|
"@monaco-editor/react": "^4.3.1",
|
||||||
@ -102,6 +105,7 @@
|
|||||||
"@babel/preset-env": "^7.12.17",
|
"@babel/preset-env": "^7.12.17",
|
||||||
"@babel/preset-react": "^7.12.13",
|
"@babel/preset-react": "^7.12.13",
|
||||||
"@babel/preset-typescript": "^7.12.17",
|
"@babel/preset-typescript": "^7.12.17",
|
||||||
|
"@jest/globals": "^27.5.1",
|
||||||
"@testing-library/cypress": "^8.0.0",
|
"@testing-library/cypress": "^8.0.0",
|
||||||
"@types/color": "^3.0.3",
|
"@types/color": "^3.0.3",
|
||||||
"@types/compression-webpack-plugin": "^9.0.0",
|
"@types/compression-webpack-plugin": "^9.0.0",
|
||||||
@ -145,9 +149,9 @@
|
|||||||
"eslint-plugin-react-hooks": "^4.3.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||||
"eslint-plugin-sonarjs": "^0.12.0",
|
"eslint-plugin-sonarjs": "^0.12.0",
|
||||||
"husky": "4.3.8",
|
"husky": "^7.0.4",
|
||||||
"less-plugin-npm-import": "^2.1.0",
|
"less-plugin-npm-import": "^2.1.0",
|
||||||
"lint-staged": "10.5.3",
|
"lint-staged": "^12.3.7",
|
||||||
"portfinder-sync": "^0.0.2",
|
"portfinder-sync": "^0.0.2",
|
||||||
"prettier": "2.2.1",
|
"prettier": "2.2.1",
|
||||||
"react-hot-loader": "^4.13.0",
|
"react-hot-loader": "^4.13.0",
|
||||||
@ -155,5 +159,10 @@
|
|||||||
"typescript-plugin-css-modules": "^3.4.0",
|
"typescript-plugin-css-modules": "^3.4.0",
|
||||||
"webpack-bundle-analyzer": "^4.5.0",
|
"webpack-bundle-analyzer": "^4.5.0",
|
||||||
"webpack-cli": "^4.5.0"
|
"webpack-cli": "^4.5.0"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.(js|jsx|ts|tsx)": [
|
||||||
|
"eslint --fix"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,14 @@ import { ErrorResponse } from 'types/api';
|
|||||||
import { ErrorStatusCode } from 'types/common';
|
import { ErrorStatusCode } from 'types/common';
|
||||||
|
|
||||||
export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
|
export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
|
||||||
if (error.response) {
|
const { response, request } = error;
|
||||||
|
if (response) {
|
||||||
// client received an error response (5xx, 4xx)
|
// client received an error response (5xx, 4xx)
|
||||||
// making the error status code as standard Error Status Code
|
// making the error status code as standard Error Status Code
|
||||||
const statusCode = error.response.status as ErrorStatusCode;
|
const statusCode = response.status as ErrorStatusCode;
|
||||||
|
|
||||||
if (statusCode >= 400 && statusCode < 500) {
|
if (statusCode >= 400 && statusCode < 500) {
|
||||||
const { data } = error.response;
|
const { data } = response;
|
||||||
|
|
||||||
if (statusCode === 404) {
|
if (statusCode === 404) {
|
||||||
return {
|
return {
|
||||||
@ -35,7 +36,7 @@ export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
|
|||||||
message: null,
|
message: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (error.request) {
|
if (request) {
|
||||||
// client never received a response, or request never left
|
// client never received a response, or request never left
|
||||||
console.error('client never received a response, or request never left');
|
console.error('client never received a response, or request never left');
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
|
|||||||
return {
|
return {
|
||||||
statusCode: 500,
|
statusCode: 500,
|
||||||
payload: null,
|
payload: null,
|
||||||
error: error.toString(),
|
error: String(error),
|
||||||
message: null,
|
message: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
29
frontend/src/api/alerts/getTriggered.ts
Normal file
29
frontend/src/api/alerts/getTriggered.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { AxiosAlertManagerInstance } from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import convertObjectIntoParams from 'lib/query/convertObjectIntoParams';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { PayloadProps, Props } from 'types/api/alerts/getTriggered';
|
||||||
|
|
||||||
|
const getTriggered = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
const queryParams = convertObjectIntoParams(props);
|
||||||
|
|
||||||
|
const response = await AxiosAlertManagerInstance.get(
|
||||||
|
`/alerts?${queryParams}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getTriggered;
|
51
frontend/src/api/channels/createWebhook.ts
Normal file
51
frontend/src/api/channels/createWebhook.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { PayloadProps, Props } from 'types/api/channels/createWebhook';
|
||||||
|
|
||||||
|
const create = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
let httpConfig = {};
|
||||||
|
|
||||||
|
if (props.username !== '' && props.password !== '') {
|
||||||
|
httpConfig = {
|
||||||
|
basic_auth: {
|
||||||
|
username: props.username,
|
||||||
|
password: props.password,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (props.username === '' && props.password !== '') {
|
||||||
|
httpConfig = {
|
||||||
|
authorization: {
|
||||||
|
type: 'bearer',
|
||||||
|
credentials: props.password,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.post('/channels', {
|
||||||
|
name: props.name,
|
||||||
|
webhook_configs: [
|
||||||
|
{
|
||||||
|
send_resolved: true,
|
||||||
|
url: props.api_url,
|
||||||
|
http_config: httpConfig,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default create;
|
50
frontend/src/api/channels/editWebhook.ts
Normal file
50
frontend/src/api/channels/editWebhook.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { PayloadProps, Props } from 'types/api/channels/editWebhook';
|
||||||
|
|
||||||
|
const editWebhook = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
let httpConfig = {};
|
||||||
|
if (props.username !== '' && props.password !== '') {
|
||||||
|
httpConfig = {
|
||||||
|
basic_auth: {
|
||||||
|
username: props.username,
|
||||||
|
password: props.password,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (props.username === '' && props.password !== '') {
|
||||||
|
httpConfig = {
|
||||||
|
authorization: {
|
||||||
|
type: 'bearer',
|
||||||
|
credentials: props.password,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.put(`/channels/${props.id}`, {
|
||||||
|
name: props.name,
|
||||||
|
webhook_configs: [
|
||||||
|
{
|
||||||
|
send_resolved: true,
|
||||||
|
url: props.api_url,
|
||||||
|
http_config: httpConfig,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default editWebhook;
|
@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function Value(props: ValueProps): JSX.Element {
|
function Value(props: ValueProps): JSX.Element {
|
||||||
|
const { fillColor } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="78"
|
width="78"
|
||||||
@ -11,7 +13,7 @@ function Value(props: ValueProps): JSX.Element {
|
|||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M15.0215 17.875C14.2285 18.8184 13.2783 19.5771 12.1709 20.1514C11.0771 20.7256 9.87402 21.0127 8.56152 21.0127C6.83887 21.0127 5.33496 20.5889 4.0498 19.7412C2.77832 18.8936 1.79395 17.7041 1.09668 16.1729C0.399414 14.6279 0.0507812 12.9258 0.0507812 11.0664C0.0507812 9.07031 0.426758 7.27246 1.17871 5.67285C1.94434 4.07324 3.02441 2.84961 4.41895 2.00195C5.81348 1.1543 7.44043 0.730469 9.2998 0.730469C12.2529 0.730469 14.5771 1.83789 16.2725 4.05273C17.9814 6.25391 18.8359 9.26172 18.8359 13.0762V14.1836C18.8359 19.9941 17.6875 24.2393 15.3906 26.9189C13.0938 29.585 9.62793 30.9521 4.99316 31.0205H4.25488V27.8213H5.05469C8.18555 27.7666 10.5918 26.9531 12.2734 25.3809C13.9551 23.7949 14.8711 21.293 15.0215 17.875ZM9.17676 17.875C10.4482 17.875 11.6172 17.4854 12.6836 16.7061C13.7637 15.9268 14.5498 14.9629 15.042 13.8145V12.2969C15.042 9.80859 14.502 7.78516 13.4219 6.22656C12.3418 4.66797 10.9746 3.88867 9.32031 3.88867C7.65234 3.88867 6.3125 4.53125 5.30078 5.81641C4.28906 7.08789 3.7832 8.76953 3.7832 10.8613C3.7832 12.8984 4.26855 14.5801 5.23926 15.9062C6.22363 17.2188 7.53613 17.875 9.17676 17.875ZM24.5371 29.0107C24.5371 28.3545 24.7285 27.8076 25.1113 27.3701C25.5078 26.9326 26.0957 26.7139 26.875 26.7139C27.6543 26.7139 28.2422 26.9326 28.6387 27.3701C29.0488 27.8076 29.2539 28.3545 29.2539 29.0107C29.2539 29.6396 29.0488 30.166 28.6387 30.5898C28.2422 31.0137 27.6543 31.2256 26.875 31.2256C26.0957 31.2256 25.5078 31.0137 25.1113 30.5898C24.7285 30.166 24.5371 29.6396 24.5371 29.0107ZM51.1562 20.9717H55.2988V24.0684H51.1562V31H47.3418V24.0684H33.7451V21.833L47.1162 1.14062H51.1562V20.9717ZM38.0518 20.9717H47.3418V6.3291L46.8906 7.14941L38.0518 20.9717ZM73.6123 1.12012V4.33984H72.915C69.9619 4.39453 67.6104 5.26953 65.8604 6.96484C64.1104 8.66016 63.0986 11.0459 62.8252 14.1221C64.3975 12.3174 66.5439 11.415 69.2646 11.415C71.8623 11.415 73.9336 12.3311 75.4785 14.1631C77.0371 15.9951 77.8164 18.3604 77.8164 21.2588C77.8164 24.335 76.9756 26.7959 75.2939 28.6416C73.626 30.4873 71.3838 31.4102 68.5674 31.4102C65.71 31.4102 63.3926 30.3164 61.6152 28.1289C59.8379 25.9277 58.9492 23.0977 58.9492 19.6387V18.1826C58.9492 12.6865 60.1182 8.48926 62.4561 5.59082C64.8076 2.67871 68.3008 1.18848 72.9355 1.12012H73.6123ZM68.6289 14.5732C67.3301 14.5732 66.1338 14.9629 65.04 15.7422C63.9463 16.5215 63.1875 17.499 62.7637 18.6748V20.0693C62.7637 22.5303 63.3174 24.5127 64.4248 26.0166C65.5322 27.5205 66.9131 28.2725 68.5674 28.2725C70.2764 28.2725 71.6162 27.6436 72.5869 26.3857C73.5713 25.1279 74.0635 23.4805 74.0635 21.4434C74.0635 19.3926 73.5645 17.7383 72.5664 16.4805C71.582 15.209 70.2695 14.5732 68.6289 14.5732Z"
|
d="M15.0215 17.875C14.2285 18.8184 13.2783 19.5771 12.1709 20.1514C11.0771 20.7256 9.87402 21.0127 8.56152 21.0127C6.83887 21.0127 5.33496 20.5889 4.0498 19.7412C2.77832 18.8936 1.79395 17.7041 1.09668 16.1729C0.399414 14.6279 0.0507812 12.9258 0.0507812 11.0664C0.0507812 9.07031 0.426758 7.27246 1.17871 5.67285C1.94434 4.07324 3.02441 2.84961 4.41895 2.00195C5.81348 1.1543 7.44043 0.730469 9.2998 0.730469C12.2529 0.730469 14.5771 1.83789 16.2725 4.05273C17.9814 6.25391 18.8359 9.26172 18.8359 13.0762V14.1836C18.8359 19.9941 17.6875 24.2393 15.3906 26.9189C13.0938 29.585 9.62793 30.9521 4.99316 31.0205H4.25488V27.8213H5.05469C8.18555 27.7666 10.5918 26.9531 12.2734 25.3809C13.9551 23.7949 14.8711 21.293 15.0215 17.875ZM9.17676 17.875C10.4482 17.875 11.6172 17.4854 12.6836 16.7061C13.7637 15.9268 14.5498 14.9629 15.042 13.8145V12.2969C15.042 9.80859 14.502 7.78516 13.4219 6.22656C12.3418 4.66797 10.9746 3.88867 9.32031 3.88867C7.65234 3.88867 6.3125 4.53125 5.30078 5.81641C4.28906 7.08789 3.7832 8.76953 3.7832 10.8613C3.7832 12.8984 4.26855 14.5801 5.23926 15.9062C6.22363 17.2188 7.53613 17.875 9.17676 17.875ZM24.5371 29.0107C24.5371 28.3545 24.7285 27.8076 25.1113 27.3701C25.5078 26.9326 26.0957 26.7139 26.875 26.7139C27.6543 26.7139 28.2422 26.9326 28.6387 27.3701C29.0488 27.8076 29.2539 28.3545 29.2539 29.0107C29.2539 29.6396 29.0488 30.166 28.6387 30.5898C28.2422 31.0137 27.6543 31.2256 26.875 31.2256C26.0957 31.2256 25.5078 31.0137 25.1113 30.5898C24.7285 30.166 24.5371 29.6396 24.5371 29.0107ZM51.1562 20.9717H55.2988V24.0684H51.1562V31H47.3418V24.0684H33.7451V21.833L47.1162 1.14062H51.1562V20.9717ZM38.0518 20.9717H47.3418V6.3291L46.8906 7.14941L38.0518 20.9717ZM73.6123 1.12012V4.33984H72.915C69.9619 4.39453 67.6104 5.26953 65.8604 6.96484C64.1104 8.66016 63.0986 11.0459 62.8252 14.1221C64.3975 12.3174 66.5439 11.415 69.2646 11.415C71.8623 11.415 73.9336 12.3311 75.4785 14.1631C77.0371 15.9951 77.8164 18.3604 77.8164 21.2588C77.8164 24.335 76.9756 26.7959 75.2939 28.6416C73.626 30.4873 71.3838 31.4102 68.5674 31.4102C65.71 31.4102 63.3926 30.3164 61.6152 28.1289C59.8379 25.9277 58.9492 23.0977 58.9492 19.6387V18.1826C58.9492 12.6865 60.1182 8.48926 62.4561 5.59082C64.8076 2.67871 68.3008 1.18848 72.9355 1.12012H73.6123ZM68.6289 14.5732C67.3301 14.5732 66.1338 14.9629 65.04 15.7422C63.9463 16.5215 63.1875 17.499 62.7637 18.6748V20.0693C62.7637 22.5303 63.3174 24.5127 64.4248 26.0166C65.5322 27.5205 66.9131 28.2725 68.5674 28.2725C70.2764 28.2725 71.6162 27.6436 72.5869 26.3857C73.5713 25.1279 74.0635 23.4805 74.0635 21.4434C74.0635 19.3926 73.5645 17.7383 72.5664 16.4805C71.582 15.209 70.2695 14.5732 68.6289 14.5732Z"
|
||||||
fill={props.fillColor}
|
fill={fillColor}
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import generatePicker from 'antd/es/date-picker/generatePicker';
|
import generatePicker from 'antd/es/date-picker/generatePicker';
|
||||||
import { Dayjs } from 'dayjs';
|
import { Dayjs } from 'dayjs';
|
||||||
|
// included in antd
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
|
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
|
||||||
|
|
||||||
const DatePicker = generatePicker<Dayjs>(dayjsGenerateConfig);
|
const DatePicker = generatePicker<Dayjs>(dayjsGenerateConfig);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Chart, ChartType, Plugin } from 'chart.js';
|
import { Chart, ChartType, Plugin } from 'chart.js';
|
||||||
import { colors } from 'lib/getRandomColor';
|
import { colors } from 'lib/getRandomColor';
|
||||||
|
import { get } from 'lodash-es';
|
||||||
|
|
||||||
const getOrCreateLegendList = (
|
const getOrCreateLegendList = (
|
||||||
chart: Chart,
|
chart: Chart,
|
||||||
@ -40,9 +41,20 @@ export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reuse the built-in legendItems generator
|
// Reuse the built-in legendItems generator
|
||||||
const items = chart?.options?.plugins?.legend?.labels?.generateLabels(chart);
|
const items = get(chart, [
|
||||||
|
'options',
|
||||||
|
'plugins',
|
||||||
|
'legend',
|
||||||
|
'labels',
|
||||||
|
'generateLabels',
|
||||||
|
])
|
||||||
|
? get(chart, ['options', 'plugins', 'legend', 'labels', 'generateLabels'])(
|
||||||
|
chart,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
items?.forEach((item, index) => {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
items?.forEach((item: Record<any, any>, index: number) => {
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.style.alignItems = 'center';
|
li.style.alignItems = 'center';
|
||||||
li.style.cursor = 'pointer';
|
li.style.cursor = 'pointer';
|
||||||
@ -66,8 +78,8 @@ export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => {
|
|||||||
|
|
||||||
// Color box
|
// Color box
|
||||||
const boxSpan = document.createElement('span');
|
const boxSpan = document.createElement('span');
|
||||||
boxSpan.style.background = item.strokeStyle || colors[0];
|
boxSpan.style.background = `${item.strokeStyle}` || `${colors[0]}`;
|
||||||
boxSpan.style.borderColor = item?.strokeStyle;
|
boxSpan.style.borderColor = `${item?.strokeStyle}`;
|
||||||
boxSpan.style.borderWidth = `${item.lineWidth}px`;
|
boxSpan.style.borderWidth = `${item.lineWidth}px`;
|
||||||
boxSpan.style.display = 'inline-block';
|
boxSpan.style.display = 'inline-block';
|
||||||
boxSpan.style.minHeight = '20px';
|
boxSpan.style.minHeight = '20px';
|
||||||
|
@ -79,6 +79,7 @@ function Graph({
|
|||||||
return 'rgba(231,233,237,0.8)';
|
return 'rgba(231,233,237,0.8)';
|
||||||
}, [currentTheme]);
|
}, [currentTheme]);
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
const buildChart = useCallback(() => {
|
const buildChart = useCallback(() => {
|
||||||
if (lineChartRef.current !== undefined) {
|
if (lineChartRef.current !== undefined) {
|
||||||
lineChartRef.current.destroy();
|
lineChartRef.current.destroy();
|
||||||
@ -103,6 +104,21 @@ function Graph({
|
|||||||
legend: {
|
legend: {
|
||||||
display: false,
|
display: false,
|
||||||
},
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label(context) {
|
||||||
|
let label = context.dataset.label || '';
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
label += ': ';
|
||||||
|
}
|
||||||
|
if (context.parsed.y !== null) {
|
||||||
|
label += getYAxisFormattedValue(context.parsed.y, yAxisUnit);
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
layout: {
|
layout: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
@ -140,8 +156,11 @@ function Graph({
|
|||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
// Include a dollar sign in the ticks
|
// Include a dollar sign in the ticks
|
||||||
callback(value, index, ticks) {
|
callback(value) {
|
||||||
return getYAxisFormattedValue(value, yAxisUnit);
|
return getYAxisFormattedValue(
|
||||||
|
parseInt(value.toString(), 10),
|
||||||
|
yAxisUnit,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -201,18 +220,25 @@ interface GraphProps {
|
|||||||
data: Chart['data'];
|
data: Chart['data'];
|
||||||
title?: string;
|
title?: string;
|
||||||
isStacked?: boolean;
|
isStacked?: boolean;
|
||||||
label?: string[];
|
onClickHandler?: GraphOnClickHandler;
|
||||||
onClickHandler?: graphOnClickHandler;
|
|
||||||
name: string;
|
name: string;
|
||||||
yAxisUnit?: string;
|
yAxisUnit?: string;
|
||||||
forceReRender?: boolean | null | number;
|
forceReRender?: boolean | null | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type graphOnClickHandler = (
|
export type GraphOnClickHandler = (
|
||||||
event: ChartEvent,
|
event: ChartEvent,
|
||||||
elements: ActiveElement[],
|
elements: ActiveElement[],
|
||||||
chart: Chart,
|
chart: Chart,
|
||||||
data: ChartData,
|
data: ChartData,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
Graph.defaultProps = {
|
||||||
|
animate: undefined,
|
||||||
|
title: undefined,
|
||||||
|
isStacked: undefined,
|
||||||
|
onClickHandler: undefined,
|
||||||
|
yAxisUnit: undefined,
|
||||||
|
forceReRender: undefined,
|
||||||
|
};
|
||||||
export default Graph;
|
export default Graph;
|
||||||
|
@ -68,6 +68,37 @@ const TIME_UNITS_CONFIG: IAxisTimeUintConfig[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the relevant time unit based on the input time stamps (in ms)
|
||||||
|
*/
|
||||||
|
export const convertTimeRange = (
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
): IAxisTimeConfig => {
|
||||||
|
const MIN_INTERVALS = 6;
|
||||||
|
const range = end - start;
|
||||||
|
let relevantTimeUnit = TIME_UNITS_CONFIG[1];
|
||||||
|
let stepSize = 1;
|
||||||
|
try {
|
||||||
|
for (let idx = TIME_UNITS_CONFIG.length - 1; idx >= 0; idx -= 1) {
|
||||||
|
const timeUnit = TIME_UNITS_CONFIG[idx];
|
||||||
|
const units = range * timeUnit.multiplier;
|
||||||
|
const steps = units / MIN_INTERVALS;
|
||||||
|
if (steps >= 1) {
|
||||||
|
relevantTimeUnit = timeUnit;
|
||||||
|
stepSize = steps;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
unitName: relevantTimeUnit.unitName,
|
||||||
|
stepSize: Math.floor(stepSize) || 1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts Chart.js data's data-structure and returns the relevant time unit for the axis based on the range of the data.
|
* Accepts Chart.js data's data-structure and returns the relevant time unit for the axis based on the range of the data.
|
||||||
*/
|
*/
|
||||||
@ -77,10 +108,18 @@ export const useXAxisTimeUnit = (data: Chart['data']): IAxisTimeConfig => {
|
|||||||
try {
|
try {
|
||||||
let minTime = Number.POSITIVE_INFINITY;
|
let minTime = Number.POSITIVE_INFINITY;
|
||||||
let maxTime = Number.NEGATIVE_INFINITY;
|
let maxTime = Number.NEGATIVE_INFINITY;
|
||||||
data?.labels?.forEach((timeStamp: string | number): void => {
|
data?.labels?.forEach((timeStamp: unknown): void => {
|
||||||
if (typeof timeStamp === 'string') timeStamp = Date.parse(timeStamp);
|
const getTimeStamp = (time: string | number): Date | number | string => {
|
||||||
minTime = Math.min(timeStamp, minTime);
|
if (typeof timeStamp === 'string') {
|
||||||
maxTime = Math.max(timeStamp, maxTime);
|
return Date.parse(timeStamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return time;
|
||||||
|
};
|
||||||
|
const time = getTimeStamp(timeStamp as string | number);
|
||||||
|
|
||||||
|
minTime = Math.min(parseInt(time.toString(), 10), minTime);
|
||||||
|
maxTime = Math.max(parseInt(time.toString(), 10), maxTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
localTime = {
|
localTime = {
|
||||||
@ -113,34 +152,3 @@ export const useXAxisTimeUnit = (data: Chart['data']): IAxisTimeConfig => {
|
|||||||
|
|
||||||
return convertTimeRange(minTime, maxTime);
|
return convertTimeRange(minTime, maxTime);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the relevant time unit based on the input time stamps (in ms)
|
|
||||||
*/
|
|
||||||
export const convertTimeRange = (
|
|
||||||
start: number,
|
|
||||||
end: number,
|
|
||||||
): IAxisTimeConfig => {
|
|
||||||
const MIN_INTERVALS = 6;
|
|
||||||
const range = end - start;
|
|
||||||
let relevantTimeUnit = TIME_UNITS_CONFIG[1];
|
|
||||||
let stepSize = 1;
|
|
||||||
try {
|
|
||||||
for (let idx = TIME_UNITS_CONFIG.length - 1; idx >= 0; idx--) {
|
|
||||||
const timeUnit = TIME_UNITS_CONFIG[idx];
|
|
||||||
const units = range * timeUnit.multiplier;
|
|
||||||
const steps = units / MIN_INTERVALS;
|
|
||||||
if (steps >= 1) {
|
|
||||||
relevantTimeUnit = timeUnit;
|
|
||||||
stepSize = steps;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
unitName: relevantTimeUnit.unitName,
|
|
||||||
stepSize: Math.floor(stepSize) || 1,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
@ -3,7 +3,6 @@ import { formattedValueToString, getValueFormat } from '@grafana/data';
|
|||||||
export const getYAxisFormattedValue = (
|
export const getYAxisFormattedValue = (
|
||||||
value: number,
|
value: number,
|
||||||
format: string,
|
format: string,
|
||||||
decimal?: number,
|
|
||||||
): string => {
|
): string => {
|
||||||
try {
|
try {
|
||||||
return formattedValueToString(
|
return formattedValueToString(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Form, Input, InputProps } from 'antd';
|
import { Form, Input, InputProps, InputRef } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function InputComponent({
|
function InputComponent({
|
||||||
@ -22,11 +22,12 @@ function InputComponent({
|
|||||||
type={type}
|
type={type}
|
||||||
onChange={onChangeHandler}
|
onChange={onChangeHandler}
|
||||||
value={value}
|
value={value}
|
||||||
ref={ref}
|
ref={ref as React.Ref<InputRef>}
|
||||||
size={size}
|
size={size}
|
||||||
addonBefore={addonBefore}
|
addonBefore={addonBefore}
|
||||||
onBlur={onBlurHandler}
|
onBlur={onBlurHandler}
|
||||||
onPressEnter={onPressEnterHandler}
|
onPressEnter={onPressEnterHandler}
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -38,7 +39,7 @@ interface InputComponentProps extends InputProps {
|
|||||||
type?: InputProps['type'];
|
type?: InputProps['type'];
|
||||||
onChangeHandler?: React.ChangeEventHandler<HTMLInputElement>;
|
onChangeHandler?: React.ChangeEventHandler<HTMLInputElement>;
|
||||||
placeholder?: InputProps['placeholder'];
|
placeholder?: InputProps['placeholder'];
|
||||||
ref?: React.LegacyRef<Input>;
|
ref?: React.LegacyRef<InputRef>;
|
||||||
size?: InputProps['size'];
|
size?: InputProps['size'];
|
||||||
onBlurHandler?: React.FocusEventHandler<HTMLInputElement>;
|
onBlurHandler?: React.FocusEventHandler<HTMLInputElement>;
|
||||||
onPressEnterHandler?: React.KeyboardEventHandler<HTMLInputElement>;
|
onPressEnterHandler?: React.KeyboardEventHandler<HTMLInputElement>;
|
||||||
@ -47,4 +48,17 @@ interface InputComponentProps extends InputProps {
|
|||||||
addonBefore?: React.ReactNode;
|
addonBefore?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputComponent.defaultProps = {
|
||||||
|
type: undefined,
|
||||||
|
onChangeHandler: undefined,
|
||||||
|
placeholder: undefined,
|
||||||
|
ref: undefined,
|
||||||
|
size: undefined,
|
||||||
|
onBlurHandler: undefined,
|
||||||
|
onPressEnterHandler: undefined,
|
||||||
|
label: undefined,
|
||||||
|
labelOnTop: undefined,
|
||||||
|
addonBefore: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default InputComponent;
|
export default InputComponent;
|
||||||
|
@ -28,4 +28,9 @@ interface ModalProps {
|
|||||||
children: ReactElement;
|
children: ReactElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CustomModal.defaultProps = {
|
||||||
|
closable: undefined,
|
||||||
|
footer: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default CustomModal;
|
export default CustomModal;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { expect } from '@jest/globals';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
@ -10,8 +10,10 @@ function RouteTab({
|
|||||||
onChangeHandler,
|
onChangeHandler,
|
||||||
...rest
|
...rest
|
||||||
}: RouteTabProps & TabsProps): JSX.Element {
|
}: RouteTabProps & TabsProps): JSX.Element {
|
||||||
const onChange = (activeRoute: string) => {
|
const onChange = (activeRoute: string): void => {
|
||||||
onChangeHandler && onChangeHandler();
|
if (onChangeHandler) {
|
||||||
|
onChangeHandler();
|
||||||
|
}
|
||||||
|
|
||||||
const selectedRoute = routes.find((e) => e.name === activeRoute);
|
const selectedRoute = routes.find((e) => e.name === activeRoute);
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ function RouteTab({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
destroyInactiveTabPane
|
destroyInactiveTabPane
|
||||||
activeKey={activeKey}
|
activeKey={activeKey}
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
{routes.map(
|
{routes.map(
|
||||||
@ -48,4 +51,8 @@ interface RouteTabProps {
|
|||||||
onChangeHandler?: VoidFunction;
|
onChangeHandler?: VoidFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RouteTab.defaultProps = {
|
||||||
|
onChangeHandler: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default RouteTab;
|
export default RouteTab;
|
||||||
|
@ -17,5 +17,10 @@ interface SpinnerProps {
|
|||||||
tip?: SpinProps['tip'];
|
tip?: SpinProps['tip'];
|
||||||
height?: React.CSSProperties['height'];
|
height?: React.CSSProperties['height'];
|
||||||
}
|
}
|
||||||
|
Spinner.defaultProps = {
|
||||||
|
size: undefined,
|
||||||
|
tip: undefined,
|
||||||
|
height: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default Spinner;
|
export default Spinner;
|
||||||
|
@ -6,8 +6,8 @@ import styled, { FlattenSimpleInterpolation } from 'styled-components';
|
|||||||
|
|
||||||
import { IStyledClass } from './types';
|
import { IStyledClass } from './types';
|
||||||
|
|
||||||
const styledClass = (props: IStyledClass): FlattenSimpleInterpolation =>
|
const styledClass = (props: IStyledClass): FlattenSimpleInterpolation | null =>
|
||||||
props.styledclass;
|
props.styledclass || null;
|
||||||
|
|
||||||
type TStyledCol = AntD.ColProps & IStyledClass;
|
type TStyledCol = AntD.ColProps & IStyledClass;
|
||||||
const StyledCol = styled(AntD.Col)<TStyledCol>`
|
const StyledCol = styled(AntD.Col)<TStyledCol>`
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { css, FlattenSimpleInterpolation } from 'styled-components';
|
import { css, FlattenSimpleInterpolation } from 'styled-components';
|
||||||
|
|
||||||
const cssProprty = (key: string, value): FlattenSimpleInterpolation =>
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const cssProperty = (key: any, value: any): FlattenSimpleInterpolation =>
|
||||||
key &&
|
key &&
|
||||||
value &&
|
value &&
|
||||||
css`
|
css`
|
||||||
@ -15,8 +16,8 @@ export const Flex = ({
|
|||||||
flexDirection,
|
flexDirection,
|
||||||
flex,
|
flex,
|
||||||
}: IFlexProps): FlattenSimpleInterpolation => css`
|
}: IFlexProps): FlattenSimpleInterpolation => css`
|
||||||
${cssProprty('flex-direction', flexDirection)}
|
${cssProperty('flex-direction', flexDirection)}
|
||||||
${cssProprty('flex', flex)}
|
${cssProperty('flex', flex)}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface IDisplayProps {
|
interface IDisplayProps {
|
||||||
@ -25,7 +26,7 @@ interface IDisplayProps {
|
|||||||
export const Display = ({
|
export const Display = ({
|
||||||
display,
|
display,
|
||||||
}: IDisplayProps): FlattenSimpleInterpolation => css`
|
}: IDisplayProps): FlattenSimpleInterpolation => css`
|
||||||
${cssProprty('display', display)}
|
${cssProperty('display', display)}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface ISpacingProps {
|
interface ISpacingProps {
|
||||||
@ -36,6 +37,6 @@ export const Spacing = ({
|
|||||||
margin,
|
margin,
|
||||||
padding,
|
padding,
|
||||||
}: ISpacingProps): FlattenSimpleInterpolation => css`
|
}: ISpacingProps): FlattenSimpleInterpolation => css`
|
||||||
${cssProprty('margin', margin)}
|
${cssProperty('margin', margin)}
|
||||||
${cssProprty('padding', padding)}
|
${cssProperty('padding', padding)}
|
||||||
`;
|
`;
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
|
/* eslint-disable react/no-unstable-nested-components */
|
||||||
import { QuestionCircleFilled } from '@ant-design/icons';
|
import { QuestionCircleFilled } from '@ant-design/icons';
|
||||||
import { Tooltip } from 'antd';
|
import { Tooltip } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function TextToolTip({ text, url }: TextToolTipProps) {
|
function TextToolTip({ text, url }: TextToolTipProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
overlay={() => {
|
overlay={(): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{`${text} `}
|
{`${text} `}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Button, Dropdown, Menu, Typography } from 'antd';
|
import { Button, Dropdown, Menu, Typography } from 'antd';
|
||||||
import timeItems, {
|
import TimeItems, {
|
||||||
timePreferance,
|
timePreferance,
|
||||||
timePreferenceType,
|
timePreferenceType,
|
||||||
} from 'container/NewWidget/RightContainer/timeItems';
|
} from 'container/NewWidget/RightContainer/timeItems';
|
||||||
@ -13,7 +13,7 @@ function TimePreference({
|
|||||||
}: TimePreferenceDropDownProps): JSX.Element {
|
}: TimePreferenceDropDownProps): JSX.Element {
|
||||||
const timeMenuItemOnChangeHandler = useCallback(
|
const timeMenuItemOnChangeHandler = useCallback(
|
||||||
(event: TimeMenuItemOnChangeHandlerEvent) => {
|
(event: TimeMenuItemOnChangeHandlerEvent) => {
|
||||||
const selectedTime = timeItems.find((e) => e.enum === event.key);
|
const selectedTime = TimeItems.find((e) => e.enum === event.key);
|
||||||
if (selectedTime !== undefined) {
|
if (selectedTime !== undefined) {
|
||||||
setSelectedTime(selectedTime);
|
setSelectedTime(selectedTime);
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@ function TimePreference({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
{timeItems.map((item) => (
|
{TimeItems.map((item) => (
|
||||||
<Menu.Item onClick={timeMenuItemOnChangeHandler} key={item.enum}>
|
<Menu.Item onClick={timeMenuItemOnChangeHandler} key={item.enum}>
|
||||||
<Typography>{item.name}</Typography>
|
<Typography>{item.name}</Typography>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
@ -6,4 +6,4 @@ export const AUTH0_REDIRECT_PATH = '/redirect';
|
|||||||
|
|
||||||
export const DEFAULT_AUTH0_APP_REDIRECTION_PATH = ROUTES.APPLICATION;
|
export const DEFAULT_AUTH0_APP_REDIRECTION_PATH = ROUTES.APPLICATION;
|
||||||
|
|
||||||
export const IS_SIDEBAR_COLLAPSED = 'isSideBarCollapsed'
|
export const IS_SIDEBAR_COLLAPSED = 'isSideBarCollapsed';
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export enum LOCAL_STORAGE {
|
export enum LOCALSTORAGE {
|
||||||
METRICS_TIME_IN_DURATION = 'metricsTimeDurations',
|
METRICS_TIME_IN_DURATION = 'metricsTimeDurations',
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
export enum METRICS_PAGE_QUERY_PARAM {
|
export enum METRICS_PAGE_QUERY_PARAM {
|
||||||
interval = 'interval',
|
interval = 'interval',
|
||||||
startTime = 'startTime',
|
startTime = 'startTime',
|
||||||
|
@ -4,7 +4,7 @@ import { ColumnsType } from 'antd/lib/table';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { generatePath } from 'react-router';
|
import { generatePath } from 'react-router-dom';
|
||||||
import { Channels, PayloadProps } from 'types/api/channels/getAll';
|
import { Channels, PayloadProps } from 'types/api/channels/getAll';
|
||||||
|
|
||||||
import Delete from './Delete';
|
import Delete from './Delete';
|
||||||
|
@ -30,7 +30,8 @@ function Delete({ notifications, setChannels, id }: DeleteProps): JSX.Element {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
description: error.toString() || 'Something went wrong',
|
description:
|
||||||
|
error instanceof Error ? error.toString() : 'Something went wrong',
|
||||||
});
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
@ -4,17 +4,19 @@ import SideNav from 'container/SideNav';
|
|||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { ReactNode, useEffect, useState } from 'react';
|
import React, { ReactNode, useEffect, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import { Content, Layout } from './styles';
|
import { Content, Layout } from './styles';
|
||||||
|
|
||||||
const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||||
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
const [isSignUpPage, setIsSignUpPage] = useState(
|
const [isSignUpPage, setIsSignUpPage] = useState(ROUTES.SIGN_UP === pathname);
|
||||||
ROUTES.SIGN_UP === location.pathname,
|
|
||||||
);
|
const { children } = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
@ -36,7 +38,7 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
|||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface AppLayoutProps {
|
interface AppLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
@ -1,10 +1,22 @@
|
|||||||
export interface SlackChannel {
|
export interface Channel {
|
||||||
send_resolved: boolean;
|
send_resolved?: boolean;
|
||||||
api_url: string;
|
|
||||||
channel: string;
|
|
||||||
title: string;
|
|
||||||
text: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChannelType = 'slack' | 'email';
|
export interface SlackChannel extends Channel {
|
||||||
|
api_url?: string;
|
||||||
|
channel?: string;
|
||||||
|
title?: string;
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebhookChannel extends Channel {
|
||||||
|
api_url?: string;
|
||||||
|
// basic auth
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChannelType = 'slack' | 'email' | 'webhook';
|
||||||
|
export const SlackType: ChannelType = 'slack';
|
||||||
|
export const WebhookType: ChannelType = 'webhook';
|
||||||
|
@ -1,17 +1,26 @@
|
|||||||
import { Form, notification } from 'antd';
|
import { Form, notification } from 'antd';
|
||||||
import createSlackApi from 'api/channels/createSlack';
|
import createSlackApi from 'api/channels/createSlack';
|
||||||
|
import createWebhookApi from 'api/channels/createWebhook';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import FormAlertChannels from 'container/FormAlertChannels';
|
import FormAlertChannels from 'container/FormAlertChannels';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { ChannelType, SlackChannel } from './config';
|
import {
|
||||||
|
ChannelType,
|
||||||
|
SlackChannel,
|
||||||
|
SlackType,
|
||||||
|
WebhookChannel,
|
||||||
|
WebhookType,
|
||||||
|
} from './config';
|
||||||
|
|
||||||
function CreateAlertChannels({
|
function CreateAlertChannels({
|
||||||
preType = 'slack',
|
preType = 'slack',
|
||||||
}: CreateAlertChannelsProps): JSX.Element {
|
}: CreateAlertChannelsProps): JSX.Element {
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
const [selectedConfig, setSelectedConfig] = useState<Partial<SlackChannel>>({
|
const [selectedConfig, setSelectedConfig] = useState<
|
||||||
|
Partial<SlackChannel & WebhookChannel>
|
||||||
|
>({
|
||||||
text: ` {{ range .Alerts -}}
|
text: ` {{ range .Alerts -}}
|
||||||
*Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
*Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
||||||
|
|
||||||
@ -73,17 +82,93 @@ function CreateAlertChannels({
|
|||||||
}
|
}
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description:
|
||||||
|
'An unexpected error occurred while creating this channel, please try again',
|
||||||
|
});
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}
|
}
|
||||||
}, [notifications, selectedConfig]);
|
}, [notifications, selectedConfig]);
|
||||||
|
|
||||||
|
const onWebhookHandler = useCallback(async () => {
|
||||||
|
// initial api request without auth params
|
||||||
|
let request: WebhookChannel = {
|
||||||
|
api_url: selectedConfig?.api_url || '',
|
||||||
|
name: selectedConfig?.name || '',
|
||||||
|
send_resolved: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
setSavingState(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (selectedConfig?.username !== '' || selectedConfig?.password !== '') {
|
||||||
|
if (selectedConfig?.username !== '') {
|
||||||
|
// if username is not null then password must be passed
|
||||||
|
if (selectedConfig?.password !== '') {
|
||||||
|
request = {
|
||||||
|
...request,
|
||||||
|
username: selectedConfig.username,
|
||||||
|
password: selectedConfig.password,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: 'A Password must be provided with user name',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (selectedConfig?.password !== '') {
|
||||||
|
// only password entered, set bearer token
|
||||||
|
request = {
|
||||||
|
...request,
|
||||||
|
username: '',
|
||||||
|
password: selectedConfig.password,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await createWebhookApi(request);
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
notifications.success({
|
||||||
|
message: 'Success',
|
||||||
|
description: 'Successfully created the channel',
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
history.replace(ROUTES.SETTINGS);
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: response.error || 'Error while creating the channel',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description:
|
||||||
|
'An unexpected error occurred while creating this channel, please try again',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSavingState(false);
|
||||||
|
}, [notifications, selectedConfig]);
|
||||||
|
|
||||||
const onSaveHandler = useCallback(
|
const onSaveHandler = useCallback(
|
||||||
async (value: ChannelType) => {
|
async (value: ChannelType) => {
|
||||||
if (value == 'slack') {
|
switch (value) {
|
||||||
onSlackHandler();
|
case SlackType:
|
||||||
|
onSlackHandler();
|
||||||
|
break;
|
||||||
|
case WebhookType:
|
||||||
|
onWebhookHandler();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: 'channel type selected is invalid',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onSlackHandler],
|
[onSlackHandler, onWebhookHandler, notifications],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -108,7 +193,7 @@ function CreateAlertChannels({
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface CreateAlertChannelsProps {
|
interface CreateAlertChannelsProps {
|
||||||
preType?: ChannelType;
|
preType: ChannelType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CreateAlertChannels;
|
export default CreateAlertChannels;
|
||||||
|
@ -1,28 +1,35 @@
|
|||||||
import { Form, notification } from 'antd';
|
import { Form, notification } from 'antd';
|
||||||
import editSlackApi from 'api/channels/editSlack';
|
import editSlackApi from 'api/channels/editSlack';
|
||||||
|
import editWebhookApi from 'api/channels/editWebhook';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
SlackChannel,
|
SlackChannel,
|
||||||
|
SlackType,
|
||||||
|
WebhookChannel,
|
||||||
|
WebhookType,
|
||||||
} from 'container/CreateAlertChannels/config';
|
} from 'container/CreateAlertChannels/config';
|
||||||
import FormAlertChannels from 'container/FormAlertChannels';
|
import FormAlertChannels from 'container/FormAlertChannels';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { Store } from 'rc-field-form/lib/interface';
|
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
function EditAlertChannels({
|
function EditAlertChannels({
|
||||||
initialValue,
|
initialValue,
|
||||||
}: EditAlertChannelsProps): JSX.Element {
|
}: EditAlertChannelsProps): JSX.Element {
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
const [selectedConfig, setSelectedConfig] = useState<Partial<SlackChannel>>({
|
const [selectedConfig, setSelectedConfig] = useState<
|
||||||
|
Partial<SlackChannel & WebhookChannel>
|
||||||
|
>({
|
||||||
...initialValue,
|
...initialValue,
|
||||||
});
|
});
|
||||||
const [savingState, setSavingState] = useState<boolean>(false);
|
const [savingState, setSavingState] = useState<boolean>(false);
|
||||||
const [notifications, NotificationElement] = notification.useNotification();
|
const [notifications, NotificationElement] = notification.useNotification();
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
|
||||||
const [type, setType] = useState<ChannelType>('slack');
|
const [type, setType] = useState<ChannelType>(
|
||||||
|
initialValue?.type ? (initialValue.type as ChannelType) : SlackType,
|
||||||
|
);
|
||||||
|
|
||||||
const onTypeChangeHandler = useCallback((value: string) => {
|
const onTypeChangeHandler = useCallback((value: string) => {
|
||||||
setType(value as ChannelType);
|
setType(value as ChannelType);
|
||||||
@ -58,13 +65,62 @@ function EditAlertChannels({
|
|||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}, [selectedConfig, notifications, id]);
|
}, [selectedConfig, notifications, id]);
|
||||||
|
|
||||||
|
const onWebhookEditHandler = useCallback(async () => {
|
||||||
|
setSavingState(true);
|
||||||
|
const { name, username, password } = selectedConfig;
|
||||||
|
|
||||||
|
const showError = (msg: string): void => {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: msg,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (selectedConfig?.api_url === '') {
|
||||||
|
showError('Webhook URL is mandatory');
|
||||||
|
setSavingState(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username && (!password || password === '')) {
|
||||||
|
showError('Please enter a password');
|
||||||
|
setSavingState(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await editWebhookApi({
|
||||||
|
api_url: selectedConfig?.api_url || '',
|
||||||
|
name: name || '',
|
||||||
|
send_resolved: true,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
notifications.success({
|
||||||
|
message: 'Success',
|
||||||
|
description: 'Channels Edited Successfully',
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
history.replace(ROUTES.SETTINGS);
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
showError(response.error || 'error while updating the Channels');
|
||||||
|
}
|
||||||
|
setSavingState(false);
|
||||||
|
}, [selectedConfig, notifications, id]);
|
||||||
|
|
||||||
const onSaveHandler = useCallback(
|
const onSaveHandler = useCallback(
|
||||||
(value: ChannelType) => {
|
(value: ChannelType) => {
|
||||||
if (value === 'slack') {
|
if (value === SlackType) {
|
||||||
onSlackEditHandler();
|
onSlackEditHandler();
|
||||||
|
} else if (value === WebhookType) {
|
||||||
|
onWebhookEditHandler();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onSlackEditHandler],
|
[onSlackEditHandler, onWebhookEditHandler],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onTestHandler = useCallback(() => {
|
const onTestHandler = useCallback(() => {
|
||||||
@ -91,7 +147,9 @@ function EditAlertChannels({
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface EditAlertChannelsProps {
|
interface EditAlertChannelsProps {
|
||||||
initialValue: Store;
|
initialValue: {
|
||||||
|
[x: string]: unknown;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditAlertChannels;
|
export default EditAlertChannels;
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
import { Input } from 'antd';
|
||||||
|
import FormItem from 'antd/lib/form/FormItem';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { WebhookChannel } from '../../CreateAlertChannels/config';
|
||||||
|
|
||||||
|
function WebhookSettings({ setSelectedConfig }: WebhookProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FormItem name="api_url" label="Webhook URL">
|
||||||
|
<Input
|
||||||
|
onChange={(event): void => {
|
||||||
|
setSelectedConfig((value) => ({
|
||||||
|
...value,
|
||||||
|
api_url: event.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
name="username"
|
||||||
|
label="User Name (optional)"
|
||||||
|
help="Leave empty for bearer auth or when authentication is not necessary."
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
onChange={(event): void => {
|
||||||
|
setSelectedConfig((value) => ({
|
||||||
|
...value,
|
||||||
|
username: event.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
name="password"
|
||||||
|
label="Password (optional)"
|
||||||
|
help="Specify a password or bearer token"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
onChange={(event): void => {
|
||||||
|
setSelectedConfig((value) => ({
|
||||||
|
...value,
|
||||||
|
password: event.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WebhookProps {
|
||||||
|
setSelectedConfig: React.Dispatch<
|
||||||
|
React.SetStateAction<Partial<WebhookChannel>>
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WebhookSettings;
|
@ -1,15 +1,18 @@
|
|||||||
import { Form, FormInstance, Input, Select, Typography } from 'antd';
|
import { Form, FormInstance, Input, Select, Typography } from 'antd';
|
||||||
import FormItem from 'antd/lib/form/FormItem';
|
import FormItem from 'antd/lib/form/FormItem';
|
||||||
|
import { Store } from 'antd/lib/form/interface';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
SlackChannel,
|
SlackChannel,
|
||||||
|
SlackType,
|
||||||
|
WebhookType,
|
||||||
} from 'container/CreateAlertChannels/config';
|
} from 'container/CreateAlertChannels/config';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { Store } from 'rc-field-form/lib/interface';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import SlackSettings from './Settings/Slack';
|
import SlackSettings from './Settings/Slack';
|
||||||
|
import WebhookSettings from './Settings/Webhook';
|
||||||
import { Button } from './styles';
|
import { Button } from './styles';
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
@ -28,6 +31,16 @@ function FormAlertChannels({
|
|||||||
initialValue,
|
initialValue,
|
||||||
nameDisable = false,
|
nameDisable = false,
|
||||||
}: FormAlertChannelsProps): JSX.Element {
|
}: FormAlertChannelsProps): JSX.Element {
|
||||||
|
const renderSettings = (): React.ReactElement | null => {
|
||||||
|
switch (type) {
|
||||||
|
case SlackType:
|
||||||
|
return <SlackSettings setSelectedConfig={setSelectedConfig} />;
|
||||||
|
case WebhookType:
|
||||||
|
return <WebhookSettings setSelectedConfig={setSelectedConfig} />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{NotificationElement}
|
{NotificationElement}
|
||||||
@ -52,14 +65,13 @@ function FormAlertChannels({
|
|||||||
<Option value="slack" key="slack">
|
<Option value="slack" key="slack">
|
||||||
Slack
|
Slack
|
||||||
</Option>
|
</Option>
|
||||||
|
<Option value="webhook" key="webhook">
|
||||||
|
Webhook
|
||||||
|
</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem>
|
<FormItem>{renderSettings()}</FormItem>
|
||||||
{type === 'slack' && (
|
|
||||||
<SlackSettings setSelectedConfig={setSelectedConfig} />
|
|
||||||
)}
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<Button
|
<Button
|
||||||
@ -89,7 +101,6 @@ interface FormAlertChannelsProps {
|
|||||||
type: ChannelType;
|
type: ChannelType;
|
||||||
setSelectedConfig: React.Dispatch<React.SetStateAction<Partial<SlackChannel>>>;
|
setSelectedConfig: React.Dispatch<React.SetStateAction<Partial<SlackChannel>>>;
|
||||||
onTypeChangeHandler: (value: ChannelType) => void;
|
onTypeChangeHandler: (value: ChannelType) => void;
|
||||||
onTestHandler: () => void;
|
|
||||||
onSaveHandler: (props: ChannelType) => void;
|
onSaveHandler: (props: ChannelType) => void;
|
||||||
savingState: boolean;
|
savingState: boolean;
|
||||||
NotificationElement: React.ReactElement<
|
NotificationElement: React.ReactElement<
|
||||||
@ -101,4 +112,8 @@ interface FormAlertChannelsProps {
|
|||||||
nameDisable?: boolean;
|
nameDisable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FormAlertChannels.defaultProps = {
|
||||||
|
nameDisable: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default FormAlertChannels;
|
export default FormAlertChannels;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { Tooltip, Typography } from 'antd';
|
|
||||||
import {
|
import {
|
||||||
IIntervalUnit,
|
IIntervalUnit,
|
||||||
resolveTimeFromInterval,
|
resolveTimeFromInterval,
|
||||||
@ -13,21 +12,29 @@ interface SpanLengthProps {
|
|||||||
width: string;
|
width: string;
|
||||||
leftOffset: string;
|
leftOffset: string;
|
||||||
bgColor: string;
|
bgColor: string;
|
||||||
toolTipText: string;
|
|
||||||
id: string;
|
|
||||||
inMsCount: number;
|
inMsCount: number;
|
||||||
intervalUnit: IIntervalUnit;
|
intervalUnit: IIntervalUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SpanLength(props: SpanLengthProps): JSX.Element {
|
function SpanLength(props: SpanLengthProps): JSX.Element {
|
||||||
const { width, leftOffset, bgColor, intervalUnit } = props;
|
const { width, leftOffset, bgColor, intervalUnit, inMsCount } = props;
|
||||||
const { isDarkMode } = useThemeMode();
|
const { isDarkMode } = useThemeMode();
|
||||||
return (
|
return (
|
||||||
<SpanWrapper>
|
<SpanWrapper>
|
||||||
<SpanLine leftOffset={leftOffset} isDarkMode={isDarkMode} />
|
<SpanLine
|
||||||
<SpanBorder bgColor={bgColor} leftOffset={leftOffset} width={width} />
|
isDarkMode={isDarkMode}
|
||||||
<SpanText leftOffset={leftOffset} isDarkMode={isDarkMode}>{`${toFixed(
|
bgColor={bgColor}
|
||||||
resolveTimeFromInterval(props.inMsCount, intervalUnit),
|
leftOffset={leftOffset}
|
||||||
|
width={width}
|
||||||
|
/>
|
||||||
|
<SpanBorder
|
||||||
|
isDarkMode={isDarkMode}
|
||||||
|
bgColor={bgColor}
|
||||||
|
leftOffset={leftOffset}
|
||||||
|
width={width}
|
||||||
|
/>
|
||||||
|
<SpanText isDarkMode={isDarkMode} leftOffset={leftOffset}>{`${toFixed(
|
||||||
|
resolveTimeFromInterval(inMsCount, intervalUnit),
|
||||||
2,
|
2,
|
||||||
)} ${intervalUnit.name}`}</SpanText>
|
)} ${intervalUnit.name}`}</SpanText>
|
||||||
</SpanWrapper>
|
</SpanWrapper>
|
||||||
|
@ -9,19 +9,19 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SpanLine = styled.div<Props>`
|
export const SpanLine = styled.div<Props>`
|
||||||
width: ${({ leftOffset }) => `${leftOffset}%`};
|
width: ${({ leftOffset }): string => `${leftOffset}%`};
|
||||||
height: 0px;
|
height: 0px;
|
||||||
border-bottom: 0.1px solid
|
border-bottom: 0.1px solid
|
||||||
${({ isDarkMode }) => (isDarkMode ? '#303030' : '#c0c0c0')};
|
${({ isDarkMode }): string => (isDarkMode ? '#303030' : '#c0c0c0')};
|
||||||
top: 50%;
|
top: 50%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
`;
|
`;
|
||||||
export const SpanBorder = styled.div<Props>`
|
export const SpanBorder = styled.div<Props>`
|
||||||
background: ${({ bgColor }) => bgColor};
|
background: ${({ bgColor }): string => bgColor};
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
height: 0.625rem;
|
height: 0.625rem;
|
||||||
width: ${({ width }) => `${width}%`};
|
width: ${({ width }): string => `${width}%`};
|
||||||
left: ${({ leftOffset }) => `${leftOffset}%`};
|
left: ${({ leftOffset }): string => `${leftOffset}%`};
|
||||||
top: 35%;
|
top: 35%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
`;
|
`;
|
||||||
@ -45,13 +45,16 @@ export const SpanWrapper = styled.div`
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
} */
|
} */
|
||||||
`;
|
`;
|
||||||
|
interface SpanTextProps extends Pick<Props, 'leftOffset'> {
|
||||||
|
isDarkMode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const SpanText = styled(Typography)<Pick<Props, 'leftOffset'>>`
|
export const SpanText = styled(Typography)<SpanTextProps>`
|
||||||
&&& {
|
&&& {
|
||||||
left: ${({ leftOffset }) => `${leftOffset}%`};
|
left: ${({ leftOffset }): string => `${leftOffset}%`};
|
||||||
top: 65%;
|
top: 65%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
color: ${({ isDarkMode }) => (isDarkMode ? '##ACACAC' : '#666')};
|
color: ${({ isDarkMode }): string => (isDarkMode ? '##ACACAC' : '#666')};
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {
|
import { Container, Service, Span, SpanWrapper } from './styles';
|
||||||
Container,
|
|
||||||
Service,
|
|
||||||
Span,
|
|
||||||
SpanConnector,
|
|
||||||
SpanName,
|
|
||||||
SpanWrapper,
|
|
||||||
} from './styles';
|
|
||||||
|
|
||||||
function SpanNameComponent({
|
function SpanNameComponent({
|
||||||
name,
|
name,
|
||||||
serviceName,
|
serviceName,
|
||||||
}: SpanNameComponent): JSX.Element {
|
}: SpanNameComponentProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Container title={`${name} ${serviceName}`}>
|
<Container title={`${name} ${serviceName}`}>
|
||||||
<SpanWrapper>
|
<SpanWrapper>
|
||||||
@ -23,7 +16,7 @@ function SpanNameComponent({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SpanNameComponent {
|
interface SpanNameComponentProps {
|
||||||
name: string;
|
name: string;
|
||||||
serviceName: string;
|
serviceName: string;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { IIntervalUnit } from 'container/TraceDetail/utils';
|
|||||||
import useThemeMode from 'hooks/useThemeMode';
|
import useThemeMode from 'hooks/useThemeMode';
|
||||||
import { SPAN_DETAILS_LEFT_COL_WIDTH } from 'pages/TraceDetail/constants';
|
import { SPAN_DETAILS_LEFT_COL_WIDTH } from 'pages/TraceDetail/constants';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { pushDStree } from 'store/actions';
|
import { ITraceTree } from 'types/api/trace/getTraceItem';
|
||||||
|
|
||||||
import { ITraceMetaData } from '..';
|
import { ITraceMetaData } from '..';
|
||||||
import SpanLength from '../SpanLength';
|
import SpanLength from '../SpanLength';
|
||||||
@ -38,6 +38,7 @@ function Trace(props: TraceProps): JSX.Element {
|
|||||||
activeSpanPath,
|
activeSpanPath,
|
||||||
isExpandAll,
|
isExpandAll,
|
||||||
intervalUnit,
|
intervalUnit,
|
||||||
|
children,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { isDarkMode } = useThemeMode();
|
const { isDarkMode } = useThemeMode();
|
||||||
@ -52,7 +53,7 @@ function Trace(props: TraceProps): JSX.Element {
|
|||||||
} else if (!isOpen) {
|
} else if (!isOpen) {
|
||||||
setOpen(activeSpanPath[level] === id);
|
setOpen(activeSpanPath[level] === id);
|
||||||
}
|
}
|
||||||
}, [activeSpanPath, isOpen]);
|
}, [activeSpanPath, isOpen, id, level]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isExpandAll) {
|
if (isExpandAll) {
|
||||||
@ -60,9 +61,9 @@ function Trace(props: TraceProps): JSX.Element {
|
|||||||
} else {
|
} else {
|
||||||
setOpen(activeSpanPath[level] === id);
|
setOpen(activeSpanPath[level] === id);
|
||||||
}
|
}
|
||||||
}, [isExpandAll]);
|
}, [isExpandAll, activeSpanPath, id, level]);
|
||||||
|
|
||||||
const isOnlyChild = props.children.length === 1;
|
const isOnlyChild = children.length === 1;
|
||||||
const [top, setTop] = useState<number>(0);
|
const [top, setTop] = useState<number>(0);
|
||||||
|
|
||||||
const ref = useRef<HTMLUListElement>(null);
|
const ref = useRef<HTMLUListElement>(null);
|
||||||
@ -75,25 +76,27 @@ function Trace(props: TraceProps): JSX.Element {
|
|||||||
inline: 'nearest',
|
inline: 'nearest',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [activeSelectedId]);
|
}, [activeSelectedId, id]);
|
||||||
|
|
||||||
const onMouseEnterHandler = () => {
|
const onMouseEnterHandler = (): void => {
|
||||||
setActiveHoverId(props.id);
|
setActiveHoverId(id);
|
||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
const { top } = getTopLeftFromBody(ref.current);
|
const { top } = getTopLeftFromBody(ref.current);
|
||||||
setTop(top);
|
setTop(top);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseLeaveHandler = () => {
|
const onMouseLeaveHandler = (): void => {
|
||||||
setActiveHoverId('');
|
setActiveHoverId('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = (): void => {
|
||||||
setActiveSelectedId(id);
|
setActiveSelectedId(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickTreeExpansion = (event) => {
|
const onClickTreeExpansion: React.MouseEventHandler<HTMLDivElement> = (
|
||||||
|
event,
|
||||||
|
): void => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
setOpen((state) => {
|
setOpen((state) => {
|
||||||
localTreeExpandInteraction.current = !isOpen;
|
localTreeExpandInteraction.current = !isOpen;
|
||||||
@ -113,6 +116,7 @@ function Trace(props: TraceProps): JSX.Element {
|
|||||||
onMouseLeave={onMouseLeaveHandler}
|
onMouseLeave={onMouseLeaveHandler}
|
||||||
isOnlyChild={isOnlyChild}
|
isOnlyChild={isOnlyChild}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
isDarkMode={isDarkMode}
|
||||||
>
|
>
|
||||||
<HoverCard
|
<HoverCard
|
||||||
top={top}
|
top={top}
|
||||||
@ -126,7 +130,11 @@ function Trace(props: TraceProps): JSX.Element {
|
|||||||
<StyledRow styledclass={[styles.flexNoWrap]}>
|
<StyledRow styledclass={[styles.flexNoWrap]}>
|
||||||
<Col>
|
<Col>
|
||||||
{totalSpans !== 1 && (
|
{totalSpans !== 1 && (
|
||||||
<CardComponent isDarkMode={isDarkMode} onClick={onClickTreeExpansion}>
|
<CardComponent
|
||||||
|
isOnlyChild={isOnlyChild}
|
||||||
|
isDarkMode={isDarkMode}
|
||||||
|
onClick={onClickTreeExpansion}
|
||||||
|
>
|
||||||
{totalSpans}
|
{totalSpans}
|
||||||
<CaretContainer>
|
<CaretContainer>
|
||||||
{isOpen ? <CaretDownFilled /> : <CaretRightFilled />}
|
{isOpen ? <CaretDownFilled /> : <CaretRightFilled />}
|
||||||
@ -144,7 +152,6 @@ function Trace(props: TraceProps): JSX.Element {
|
|||||||
leftOffset={nodeLeftOffset.toString()}
|
leftOffset={nodeLeftOffset.toString()}
|
||||||
width={width.toString()}
|
width={width.toString()}
|
||||||
bgColor={serviceColour}
|
bgColor={serviceColour}
|
||||||
id={id}
|
|
||||||
inMsCount={inMsCount / 1e6}
|
inMsCount={inMsCount / 1e6}
|
||||||
intervalUnit={intervalUnit}
|
intervalUnit={intervalUnit}
|
||||||
/>
|
/>
|
||||||
@ -153,11 +160,12 @@ function Trace(props: TraceProps): JSX.Element {
|
|||||||
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<>
|
<>
|
||||||
{props.children.map((child) => (
|
{children.map((child) => (
|
||||||
<Trace
|
<Trace
|
||||||
key={child.id}
|
key={child.id}
|
||||||
activeHoverId={props.activeHoverId}
|
activeHoverId={activeHoverId}
|
||||||
setActiveHoverId={props.setActiveHoverId}
|
setActiveHoverId={setActiveHoverId}
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...child}
|
{...child}
|
||||||
globalSpread={globalSpread}
|
globalSpread={globalSpread}
|
||||||
globalStart={globalStart}
|
globalStart={globalStart}
|
||||||
@ -180,7 +188,7 @@ interface ITraceGlobal {
|
|||||||
globalStart: ITraceMetaData['globalStart'];
|
globalStart: ITraceMetaData['globalStart'];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TraceProps extends pushDStree, ITraceGlobal {
|
interface TraceProps extends ITraceTree, ITraceGlobal {
|
||||||
activeHoverId: string;
|
activeHoverId: string;
|
||||||
setActiveHoverId: React.Dispatch<React.SetStateAction<string>>;
|
setActiveHoverId: React.Dispatch<React.SetStateAction<string>>;
|
||||||
setActiveSelectedId: React.Dispatch<React.SetStateAction<string>>;
|
setActiveSelectedId: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, {
|
||||||
|
css,
|
||||||
|
DefaultTheme,
|
||||||
|
ThemedCssFunction,
|
||||||
|
} from 'styled-components';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isOnlyChild: boolean;
|
isOnlyChild: boolean;
|
||||||
@ -13,9 +17,10 @@ export const Wrapper = styled.ul<Props>`
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
border-left: ${({ isOnlyChild }) => isOnlyChild && 'none'} !important;
|
border-left: ${({ isOnlyChild }): StyledCSS =>
|
||||||
|
isOnlyChild && 'none'} !important;
|
||||||
|
|
||||||
${({ isOnlyChild }) =>
|
${({ isOnlyChild }): StyledCSS =>
|
||||||
isOnlyChild &&
|
isOnlyChild &&
|
||||||
css`
|
css`
|
||||||
&:before {
|
&:before {
|
||||||
@ -37,15 +42,27 @@ export const CardContainer = styled.li`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CardComponent = styled.div`
|
interface Props {
|
||||||
border: 1px solid ${({ isDarkMode }) => (isDarkMode ? '#434343' : '#333')};
|
isDarkMode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StyledCSS =
|
||||||
|
| ReturnType<ThemedCssFunction<DefaultTheme>>
|
||||||
|
| string
|
||||||
|
| false
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
export const CardComponent = styled.div<Props>`
|
||||||
|
border: 1px solid
|
||||||
|
${({ isDarkMode }): StyledCSS => (isDarkMode ? '#434343' : '#333')};
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 1px 8px;
|
padding: 1px 8px;
|
||||||
background: ${({ isDarkMode }) => (isDarkMode ? '#1d1d1d' : '#ddd')};
|
background: ${({ isDarkMode }): StyledCSS =>
|
||||||
|
isDarkMode ? '#1d1d1d' : '#ddd'};
|
||||||
height: 22px;
|
height: 22px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -61,13 +78,15 @@ interface HoverCardProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const HoverCard = styled.div<HoverCardProps>`
|
export const HoverCard = styled.div<HoverCardProps>`
|
||||||
display: ${({ isSelected, isHovered }) =>
|
display: ${({ isSelected, isHovered }): string =>
|
||||||
isSelected || isHovered ? 'block' : 'none'};
|
isSelected || isHovered ? 'block' : 'none'};
|
||||||
width: 200%;
|
width: 200%;
|
||||||
background-color: ${({ isHovered, isDarkMode }) =>
|
background-color: ${({ isHovered, isDarkMode }): string => {
|
||||||
isHovered && (isDarkMode ? '#262626' : '#ddd')};
|
if (isHovered) {
|
||||||
background-color: ${({ isSelected, isDarkMode }) =>
|
return isDarkMode ? '#262626' : '#ddd';
|
||||||
isSelected && (isDarkMode ? '#4f4f4f' : '#bbb')};
|
}
|
||||||
|
return isDarkMode ? '#4f4f4f' : '#bbb';
|
||||||
|
}};
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: -100%;
|
left: -100%;
|
||||||
|
@ -26,13 +26,13 @@ function GanttChart(props: GanttChartProps): JSX.Element {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActiveSpanPath(getSpanPath(data, spanId));
|
setActiveSpanPath(getSpanPath(data, spanId));
|
||||||
}, [spanId]);
|
}, [spanId, data]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActiveSpanPath(getSpanPath(data, activeSelectedId));
|
setActiveSpanPath(getSpanPath(data, activeSelectedId));
|
||||||
}, [activeSelectedId]);
|
}, [activeSelectedId, data]);
|
||||||
|
|
||||||
const handleCollapse = () => {
|
const handleCollapse = (): void => {
|
||||||
setIsExpandAll((prev) => !prev);
|
setIsExpandAll((prev) => !prev);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
@ -50,6 +50,7 @@ function GanttChart(props: GanttChartProps): JSX.Element {
|
|||||||
activeSpanPath={activeSpanPath}
|
activeSpanPath={activeSpanPath}
|
||||||
setActiveHoverId={setActiveHoverId}
|
setActiveHoverId={setActiveHoverId}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...{
|
{...{
|
||||||
...data,
|
...data,
|
||||||
globalSpread,
|
globalSpread,
|
||||||
|
@ -1,24 +1,33 @@
|
|||||||
import { ITraceTree } from 'types/api/trace/getTraceItem';
|
import { ITraceTree } from 'types/api/trace/getTraceItem';
|
||||||
|
|
||||||
export const getMetaDataFromSpanTree = (treeData: ITraceTree) => {
|
interface GetTraceMetaData {
|
||||||
|
globalStart: number;
|
||||||
|
globalEnd: number;
|
||||||
|
spread: number;
|
||||||
|
totalSpans: number;
|
||||||
|
levels: number;
|
||||||
|
}
|
||||||
|
export const getMetaDataFromSpanTree = (
|
||||||
|
treeData: ITraceTree,
|
||||||
|
): GetTraceMetaData => {
|
||||||
let globalStart = Number.POSITIVE_INFINITY;
|
let globalStart = Number.POSITIVE_INFINITY;
|
||||||
let globalEnd = Number.NEGATIVE_INFINITY;
|
let globalEnd = Number.NEGATIVE_INFINITY;
|
||||||
let totalSpans = 0;
|
let totalSpans = 0;
|
||||||
let levels = 1;
|
let levels = 1;
|
||||||
const traverse = (treeNode: ITraceTree, level = 0) => {
|
const traverse = (treeNode: ITraceTree, level = 0): void => {
|
||||||
if (!treeNode) {
|
if (!treeNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
totalSpans++;
|
totalSpans += 1;
|
||||||
levels = Math.max(levels, level);
|
levels = Math.max(levels, level);
|
||||||
const { startTime } = treeNode;
|
const { startTime } = treeNode;
|
||||||
const endTime = startTime + treeNode.value;
|
const endTime = startTime + treeNode.value;
|
||||||
globalStart = Math.min(globalStart, startTime);
|
globalStart = Math.min(globalStart, startTime);
|
||||||
globalEnd = Math.max(globalEnd, endTime);
|
globalEnd = Math.max(globalEnd, endTime);
|
||||||
|
|
||||||
for (const childNode of treeNode.children) {
|
treeNode.children.forEach((childNode) => {
|
||||||
traverse(childNode, level + 1);
|
traverse(childNode, level + 1);
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
traverse(treeData, 1);
|
traverse(treeData, 1);
|
||||||
|
|
||||||
@ -34,7 +43,9 @@ export const getMetaDataFromSpanTree = (treeData: ITraceTree) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getTopLeftFromBody(elem: HTMLElement) {
|
export function getTopLeftFromBody(
|
||||||
|
elem: HTMLElement,
|
||||||
|
): { top: number; left: number } {
|
||||||
const box = elem.getBoundingClientRect();
|
const box = elem.getBoundingClientRect();
|
||||||
|
|
||||||
const { body } = document;
|
const { body } = document;
|
||||||
@ -57,18 +68,18 @@ export const getNodeById = (
|
|||||||
treeData: ITraceTree,
|
treeData: ITraceTree,
|
||||||
): ITraceTree | undefined => {
|
): ITraceTree | undefined => {
|
||||||
let foundNode: ITraceTree | undefined;
|
let foundNode: ITraceTree | undefined;
|
||||||
const traverse = (treeNode: ITraceTree, level = 0) => {
|
const traverse = (treeNode: ITraceTree, level = 0): void => {
|
||||||
if (!treeNode) {
|
if (!treeNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchingId == treeNode.id) {
|
if (searchingId === treeNode.id) {
|
||||||
foundNode = treeNode;
|
foundNode = treeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const childNode of treeNode.children) {
|
treeNode.children.forEach((childNode) => {
|
||||||
traverse(childNode, level + 1);
|
traverse(childNode, level + 1);
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
traverse(treeData, 1);
|
traverse(treeData, 1);
|
||||||
|
|
||||||
@ -88,7 +99,7 @@ const getSpanWithoutChildren = (
|
|||||||
tags: span.tags,
|
tags: span.tags,
|
||||||
time: span.time,
|
time: span.time,
|
||||||
value: span.value,
|
value: span.value,
|
||||||
error: span.error,
|
event: span.event,
|
||||||
hasError: span.hasError,
|
hasError: span.hasError,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -101,10 +112,7 @@ export const isSpanPresentInSearchString = (
|
|||||||
|
|
||||||
const stringifyTree = JSON.stringify(parsedTree);
|
const stringifyTree = JSON.stringify(parsedTree);
|
||||||
|
|
||||||
if (stringifyTree.includes(searchedString)) {
|
return stringifyTree.includes(searchedString);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isSpanPresent = (
|
export const isSpanPresent = (
|
||||||
@ -117,7 +125,7 @@ export const isSpanPresent = (
|
|||||||
treeNode: ITraceTree,
|
treeNode: ITraceTree,
|
||||||
level = 0,
|
level = 0,
|
||||||
foundNode: ITraceTree[],
|
foundNode: ITraceTree[],
|
||||||
) => {
|
): void => {
|
||||||
if (!treeNode) {
|
if (!treeNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -128,9 +136,9 @@ export const isSpanPresent = (
|
|||||||
foundNode.push(treeNode);
|
foundNode.push(treeNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const childNode of treeNode.children) {
|
treeNode.children.forEach((childNode) => {
|
||||||
traverse(childNode, level + 1, foundNode);
|
traverse(childNode, level + 1, foundNode);
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
traverse(tree, 1, foundNode);
|
traverse(tree, 1, foundNode);
|
||||||
|
|
||||||
@ -140,7 +148,7 @@ export const isSpanPresent = (
|
|||||||
export const getSpanPath = (tree: ITraceTree, spanId: string): string[] => {
|
export const getSpanPath = (tree: ITraceTree, spanId: string): string[] => {
|
||||||
const spanPath: string[] = [];
|
const spanPath: string[] = [];
|
||||||
|
|
||||||
const traverse = (treeNode: ITraceTree) => {
|
const traverse = (treeNode: ITraceTree): boolean => {
|
||||||
if (!treeNode) {
|
if (!treeNode) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -152,9 +160,9 @@ export const getSpanPath = (tree: ITraceTree, spanId: string): string[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let foundInChild = false;
|
let foundInChild = false;
|
||||||
for (const childNode of treeNode.children) {
|
treeNode.children.forEach((childNode) => {
|
||||||
if (traverse(childNode)) foundInChild = true;
|
if (traverse(childNode)) foundInChild = true;
|
||||||
}
|
});
|
||||||
if (!foundInChild) {
|
if (!foundInChild) {
|
||||||
spanPath.pop();
|
spanPath.pop();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { DownOutlined } from '@ant-design/icons';
|
import { DownOutlined } from '@ant-design/icons';
|
||||||
import { Button, Menu } from 'antd';
|
import { Button, Menu } from 'antd';
|
||||||
import { MenuInfo } from 'rc-menu/lib/interface';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { SettingPeroid } from '.';
|
import { SettingPeroid } from '.';
|
||||||
@ -35,7 +34,7 @@ function Retention({
|
|||||||
];
|
];
|
||||||
|
|
||||||
const onClickHandler = (
|
const onClickHandler = (
|
||||||
e: MenuInfo,
|
e: { key: string },
|
||||||
func: React.Dispatch<React.SetStateAction<SettingPeroid>>,
|
func: React.Dispatch<React.SetStateAction<SettingPeroid>>,
|
||||||
): void => {
|
): void => {
|
||||||
const selected = e.key as SettingPeroid;
|
const selected = e.key as SettingPeroid;
|
||||||
|
@ -39,6 +39,10 @@ function GeneralSettings(): JSX.Element {
|
|||||||
const [isDefaultMetrics, setIsDefaultMetrics] = useState<boolean>(false);
|
const [isDefaultMetrics, setIsDefaultMetrics] = useState<boolean>(false);
|
||||||
const [isDefaultTrace, setIsDefaultTrace] = useState<boolean>(false);
|
const [isDefaultTrace, setIsDefaultTrace] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const onModalToggleHandler = (): void => {
|
||||||
|
setModal((modal) => !modal);
|
||||||
|
};
|
||||||
|
|
||||||
const onClickSaveHandler = useCallback(() => {
|
const onClickSaveHandler = useCallback(() => {
|
||||||
onModalToggleHandler();
|
onModalToggleHandler();
|
||||||
}, []);
|
}, []);
|
||||||
@ -48,10 +52,6 @@ function GeneralSettings(): JSX.Element {
|
|||||||
undefined
|
undefined
|
||||||
>(getRetentionperoidApi, undefined);
|
>(getRetentionperoidApi, undefined);
|
||||||
|
|
||||||
const onModalToggleHandler = (): void => {
|
|
||||||
setModal((modal) => !modal);
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkMetricTraceDefault = (trace: number, metric: number): void => {
|
const checkMetricTraceDefault = (trace: number, metric: number): void => {
|
||||||
if (metric === -1) {
|
if (metric === -1) {
|
||||||
setIsDefaultMetrics(true);
|
setIsDefaultMetrics(true);
|
||||||
@ -68,12 +68,15 @@ function GeneralSettings(): JSX.Element {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && payload !== undefined) {
|
if (!loading && payload !== undefined) {
|
||||||
const { metrics_ttl_duration_hrs, traces_ttl_duration_hrs } = payload;
|
const {
|
||||||
|
metrics_ttl_duration_hrs: metricTllDuration,
|
||||||
|
traces_ttl_duration_hrs: traceTllDuration,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
checkMetricTraceDefault(traces_ttl_duration_hrs, metrics_ttl_duration_hrs);
|
checkMetricTraceDefault(traceTllDuration, metricTllDuration);
|
||||||
|
|
||||||
const traceValue = getSettingsPeroid(traces_ttl_duration_hrs);
|
const traceValue = getSettingsPeroid(traceTllDuration);
|
||||||
const metricsValue = getSettingsPeroid(metrics_ttl_duration_hrs);
|
const metricsValue = getSettingsPeroid(metricTllDuration);
|
||||||
|
|
||||||
setRetentionPeroidTrace(traceValue.value.toString());
|
setRetentionPeroidTrace(traceValue.value.toString());
|
||||||
setSelectedTracePeroid(traceValue.peroid);
|
setSelectedTracePeroid(traceValue.peroid);
|
||||||
@ -171,11 +174,7 @@ function GeneralSettings(): JSX.Element {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isDisabledHandler = (): boolean => {
|
const isDisabledHandler = (): boolean => {
|
||||||
if (retentionPeroidTrace === '' || retentionPeroidMetrics === '') {
|
return !!(retentionPeroidTrace === '' || retentionPeroidMetrics === '');
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const errorText = getErrorText();
|
const errorText = getErrorText();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { ChartData } from 'chart.js';
|
import { ChartData } from 'chart.js';
|
||||||
import Graph, { graphOnClickHandler } from 'components/Graph';
|
import Graph, { GraphOnClickHandler } from 'components/Graph';
|
||||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||||
import ValueGraph from 'components/ValueGraph';
|
import ValueGraph from 'components/ValueGraph';
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
@ -77,9 +77,17 @@ export interface GridGraphComponentProps {
|
|||||||
title?: string;
|
title?: string;
|
||||||
opacity?: string;
|
opacity?: string;
|
||||||
isStacked?: boolean;
|
isStacked?: boolean;
|
||||||
onClickHandler?: graphOnClickHandler;
|
onClickHandler?: GraphOnClickHandler;
|
||||||
name: string;
|
name: string;
|
||||||
yAxisUnit?: string;
|
yAxisUnit?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GridGraphComponent.defaultProps = {
|
||||||
|
title: undefined,
|
||||||
|
opacity: undefined,
|
||||||
|
isStacked: undefined,
|
||||||
|
onClickHandler: undefined,
|
||||||
|
yAxisUnit: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default GridGraphComponent;
|
export default GridGraphComponent;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Graph, { graphOnClickHandler } from 'components/Graph';
|
import Graph, { GraphOnClickHandler } from 'components/Graph';
|
||||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
||||||
import GetMaxMinTime from 'lib/getMaxMinTime';
|
import GetMaxMinTime from 'lib/getMaxMinTime';
|
||||||
import { colors } from 'lib/getRandomColor';
|
import { colors } from 'lib/getRandomColor';
|
||||||
@ -60,6 +60,7 @@ function EmptyGraph({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Graph
|
<Graph
|
||||||
|
name=""
|
||||||
{...{
|
{...{
|
||||||
type: 'line',
|
type: 'line',
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
@ -84,7 +85,7 @@ function EmptyGraph({
|
|||||||
interface EmptyGraphProps {
|
interface EmptyGraphProps {
|
||||||
selectedTime: timePreferance;
|
selectedTime: timePreferance;
|
||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
onClickHandler: graphOnClickHandler | undefined;
|
onClickHandler: GraphOnClickHandler | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EmptyGraph;
|
export default EmptyGraph;
|
||||||
|
@ -2,7 +2,7 @@ import { Button, Typography } from 'antd';
|
|||||||
import getQueryResult from 'api/widgets/getQuery';
|
import getQueryResult from 'api/widgets/getQuery';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ChartData } from 'chart.js';
|
import { ChartData } from 'chart.js';
|
||||||
import { graphOnClickHandler } from 'components/Graph';
|
import { GraphOnClickHandler } from 'components/Graph';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import TimePreference from 'components/TimePreferenceDropDown';
|
import TimePreference from 'components/TimePreferenceDropDown';
|
||||||
import GridGraphComponent from 'container/GridGraphComponent';
|
import GridGraphComponent from 'container/GridGraphComponent';
|
||||||
@ -65,7 +65,9 @@ function FullView({
|
|||||||
minTime,
|
minTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getMinMax = (time: timePreferenceType) => {
|
const getMinMax = (
|
||||||
|
time: timePreferenceType,
|
||||||
|
): { min: string | number; max: string | number } => {
|
||||||
if (time === 'GLOBAL_TIME') {
|
if (time === 'GLOBAL_TIME') {
|
||||||
const minMax = GetMinMax(globalSelectedTime);
|
const minMax = GetMinMax(globalSelectedTime);
|
||||||
return {
|
return {
|
||||||
@ -142,7 +144,7 @@ function FullView({
|
|||||||
loading: false,
|
loading: false,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [widget, maxTime, minTime, selectedTime.enum]);
|
}, [widget, maxTime, minTime, selectedTime.enum, globalSelectedTime]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onFetchDataHandler();
|
onFetchDataHandler();
|
||||||
@ -240,10 +242,17 @@ interface FullViewState {
|
|||||||
interface FullViewProps {
|
interface FullViewProps {
|
||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
fullViewOptions?: boolean;
|
fullViewOptions?: boolean;
|
||||||
onClickHandler?: graphOnClickHandler;
|
onClickHandler?: GraphOnClickHandler;
|
||||||
noDataGraph?: boolean;
|
noDataGraph?: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
yAxisUnit?: string;
|
yAxisUnit?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FullView.defaultProps = {
|
||||||
|
fullViewOptions: undefined,
|
||||||
|
onClickHandler: undefined,
|
||||||
|
noDataGraph: undefined,
|
||||||
|
yAxisUnit: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default FullView;
|
export default FullView;
|
||||||
|
@ -124,6 +124,13 @@ function GridCardGraph({
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onDeleteHandler = useCallback(() => {
|
||||||
|
deleteWidget({ widgetId: widget.id });
|
||||||
|
onToggleModal(setDeletModal);
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
isDeleted.current = true;
|
||||||
|
}, [deleteWidget, widget, onToggleModal, isDeleted]);
|
||||||
|
|
||||||
const getModals = (): JSX.Element => {
|
const getModals = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -160,12 +167,6 @@ function GridCardGraph({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDeleteHandler = useCallback(() => {
|
|
||||||
deleteWidget({ widgetId: widget.id });
|
|
||||||
onToggleModal(setDeletModal);
|
|
||||||
isDeleted.current = true;
|
|
||||||
}, [deleteWidget, widget, onToggleModal, isDeleted]);
|
|
||||||
|
|
||||||
if (state.error) {
|
if (state.error) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -6,11 +6,9 @@ import { notification } from 'antd';
|
|||||||
import updateDashboardApi from 'api/dashboard/update';
|
import updateDashboardApi from 'api/dashboard/update';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
import history from 'lib/history';
|
|
||||||
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { Layout } from 'react-grid-layout';
|
import { Layout } from 'react-grid-layout';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router-dom';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import DashboardReducer from 'types/reducer/dashboards';
|
import DashboardReducer from 'types/reducer/dashboards';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
@ -156,7 +154,8 @@ function GridGraph(): JSX.Element {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: error.toString() || 'Something went wrong',
|
message:
|
||||||
|
error instanceof Error ? error.toString() : 'Something went wrong',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { Breadcrumb } from 'antd';
|
import { Breadcrumb } from 'antd';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { RouteComponentProps, withRouter } from 'react-router';
|
import { Link, RouteComponentProps, withRouter } from 'react-router-dom';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
const breadcrumbNameMap = {
|
const breadcrumbNameMap = {
|
||||||
[ROUTES.APPLICATION]: 'Application',
|
[ROUTES.APPLICATION]: 'Application',
|
||||||
[ROUTES.TRACES]: 'Traces',
|
[ROUTES.TRACE]: 'Traces',
|
||||||
[ROUTES.SERVICE_MAP]: 'Service Map',
|
[ROUTES.SERVICE_MAP]: 'Service Map',
|
||||||
[ROUTES.USAGE_EXPLORER]: 'Usage Explorer',
|
[ROUTES.USAGE_EXPLORER]: 'Usage Explorer',
|
||||||
[ROUTES.INSTRUMENTATION]: 'Add instrumentation',
|
[ROUTES.INSTRUMENTATION]: 'Add instrumentation',
|
||||||
@ -15,7 +14,9 @@ const breadcrumbNameMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {
|
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {
|
||||||
const pathArray = props.location.pathname.split('/').filter((i) => i);
|
const { location } = props;
|
||||||
|
|
||||||
|
const pathArray = location.pathname.split('/').filter((i) => i);
|
||||||
|
|
||||||
const extraBreadcrumbItems = pathArray.map((_, index) => {
|
const extraBreadcrumbItems = pathArray.map((_, index) => {
|
||||||
const url = `/${pathArray.slice(0, index + 1).join('/')}`;
|
const url = `/${pathArray.slice(0, index + 1).join('/')}`;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react/jsx-no-bind */
|
||||||
import { Modal } from 'antd';
|
import { Modal } from 'antd';
|
||||||
import DatePicker from 'components/DatePicker';
|
import DatePicker from 'components/DatePicker';
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
@ -22,10 +23,7 @@ function CustomDateTimeModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function disabledDate(current: Dayjs): boolean {
|
function disabledDate(current: Dayjs): boolean {
|
||||||
if (current > dayjs()) {
|
return current > dayjs();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
|
|
||||||
type fiveMin = '5min';
|
type FiveMin = '5min';
|
||||||
type fifteenMin = '15min';
|
type FifteenMin = '15min';
|
||||||
type thrityMin = '30min';
|
type ThirtyMin = '30min';
|
||||||
type oneMin = '1min';
|
type OneMin = '1min';
|
||||||
type sixHour = '6hr';
|
type SixHour = '6hr';
|
||||||
type oneHour = '1hr';
|
type OneHour = '1hr';
|
||||||
type oneDay = '1day';
|
type OneDay = '1day';
|
||||||
type oneWeek = '1week';
|
type OneWeek = '1week';
|
||||||
type custom = 'custom';
|
type Custom = 'custom';
|
||||||
|
|
||||||
export type Time =
|
export type Time =
|
||||||
| fiveMin
|
| FiveMin
|
||||||
| fifteenMin
|
| FifteenMin
|
||||||
| thrityMin
|
| ThirtyMin
|
||||||
| oneMin
|
| OneMin
|
||||||
| sixHour
|
| SixHour
|
||||||
| oneHour
|
| OneHour
|
||||||
| custom
|
| Custom
|
||||||
| oneWeek
|
| OneWeek
|
||||||
| oneDay;
|
| OneDay;
|
||||||
|
|
||||||
export const Options: Option[] = [
|
export const Options: Option[] = [
|
||||||
{ value: '5min', label: 'Last 5 min' },
|
{ value: '5min', label: 'Last 5 min' },
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { Button, Select as DefaultSelect } from 'antd';
|
import { Button, Select as DefaultSelect } from 'antd';
|
||||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||||
import setLocalStorageKey from 'api/browser/localstorage/set';
|
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||||
import { LOCAL_STORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import getTimeString from 'lib/getTimeString';
|
import getTimeString from 'lib/getTimeString';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { RouteComponentProps, withRouter } from 'react-router';
|
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { GlobalTimeLoading, UpdateTimeInterval } from 'store/actions';
|
import { GlobalTimeLoading, UpdateTimeInterval } from 'store/actions';
|
||||||
@ -26,7 +26,7 @@ function DateTimeSelection({
|
|||||||
updateTimeInterval,
|
updateTimeInterval,
|
||||||
globalTimeLoading,
|
globalTimeLoading,
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const [form_dtselector] = Form.useForm();
|
const [formSelector] = Form.useForm();
|
||||||
|
|
||||||
const params = new URLSearchParams(location.search);
|
const params = new URLSearchParams(location.search);
|
||||||
const searchStartTime = params.get('startTime');
|
const searchStartTime = params.get('startTime');
|
||||||
@ -72,10 +72,27 @@ function DateTimeSelection({
|
|||||||
GlobalReducer
|
GlobalReducer
|
||||||
>((state) => state.globalTime);
|
>((state) => state.globalTime);
|
||||||
|
|
||||||
|
const getInputLabel = (
|
||||||
|
startTime?: Dayjs,
|
||||||
|
endTime?: Dayjs,
|
||||||
|
timeInterval: Time = '15min',
|
||||||
|
): string | Time => {
|
||||||
|
if (startTime && endTime && timeInterval === 'custom') {
|
||||||
|
const format = 'YYYY/MM/DD HH:mm';
|
||||||
|
|
||||||
|
const startString = startTime.format(format);
|
||||||
|
const endString = endTime.format(format);
|
||||||
|
|
||||||
|
return `${startString} - ${endString}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeInterval;
|
||||||
|
};
|
||||||
|
|
||||||
const getDefaultTime = (pathName: string): Time => {
|
const getDefaultTime = (pathName: string): Time => {
|
||||||
const defaultSelectedOption = getDefaultOption(pathName);
|
const defaultSelectedOption = getDefaultOption(pathName);
|
||||||
|
|
||||||
const routes = getLocalStorageKey(LOCAL_STORAGE.METRICS_TIME_IN_DURATION);
|
const routes = getLocalStorageKey(LOCALSTORAGE.METRICS_TIME_IN_DURATION);
|
||||||
|
|
||||||
if (routes !== null) {
|
if (routes !== null) {
|
||||||
const routesObject = JSON.parse(routes || '{}');
|
const routesObject = JSON.parse(routes || '{}');
|
||||||
@ -94,7 +111,7 @@ function DateTimeSelection({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const updateLocalStorageForRoutes = (value: Time): void => {
|
const updateLocalStorageForRoutes = (value: Time): void => {
|
||||||
const preRoutes = getLocalStorageKey(LOCAL_STORAGE.METRICS_TIME_IN_DURATION);
|
const preRoutes = getLocalStorageKey(LOCALSTORAGE.METRICS_TIME_IN_DURATION);
|
||||||
if (preRoutes !== null) {
|
if (preRoutes !== null) {
|
||||||
const preRoutesObject = JSON.parse(preRoutes);
|
const preRoutesObject = JSON.parse(preRoutes);
|
||||||
|
|
||||||
@ -104,46 +121,12 @@ function DateTimeSelection({
|
|||||||
preRoute[location.pathname] = value;
|
preRoute[location.pathname] = value;
|
||||||
|
|
||||||
setLocalStorageKey(
|
setLocalStorageKey(
|
||||||
LOCAL_STORAGE.METRICS_TIME_IN_DURATION,
|
LOCALSTORAGE.METRICS_TIME_IN_DURATION,
|
||||||
JSON.stringify(preRoute),
|
JSON.stringify(preRoute),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSelectHandler = (value: Time): void => {
|
|
||||||
if (value !== 'custom') {
|
|
||||||
updateTimeInterval(value);
|
|
||||||
const selectedLabel = getInputLabel(undefined, undefined, value);
|
|
||||||
setSelectedTimeInterval(selectedLabel as Time);
|
|
||||||
updateLocalStorageForRoutes(value);
|
|
||||||
} else {
|
|
||||||
setRefreshButtonHidden(true);
|
|
||||||
setCustomDTPickerVisible(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onRefreshHandler = (): void => {
|
|
||||||
onSelectHandler(selectedTimeInterval);
|
|
||||||
onLastRefreshHandler();
|
|
||||||
};
|
|
||||||
|
|
||||||
const getInputLabel = (
|
|
||||||
startTime?: Dayjs,
|
|
||||||
endTime?: Dayjs,
|
|
||||||
timeInterval: Time = '15min',
|
|
||||||
): string | Time => {
|
|
||||||
if (startTime && endTime && timeInterval === 'custom') {
|
|
||||||
const format = 'YYYY/MM/DD HH:mm';
|
|
||||||
|
|
||||||
const startString = startTime.format(format);
|
|
||||||
const endString = endTime.format(format);
|
|
||||||
|
|
||||||
return `${startString} - ${endString}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return timeInterval;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onLastRefreshHandler = useCallback(() => {
|
const onLastRefreshHandler = useCallback(() => {
|
||||||
const currentTime = dayjs();
|
const currentTime = dayjs();
|
||||||
|
|
||||||
@ -177,6 +160,23 @@ function DateTimeSelection({
|
|||||||
return `Last refresh - ${secondsDiff} sec ago`;
|
return `Last refresh - ${secondsDiff} sec ago`;
|
||||||
}, [maxTime, minTime, selectedTimeInterval]);
|
}, [maxTime, minTime, selectedTimeInterval]);
|
||||||
|
|
||||||
|
const onSelectHandler = (value: Time): void => {
|
||||||
|
if (value !== 'custom') {
|
||||||
|
updateTimeInterval(value);
|
||||||
|
const selectedLabel = getInputLabel(undefined, undefined, value);
|
||||||
|
setSelectedTimeInterval(selectedLabel as Time);
|
||||||
|
updateLocalStorageForRoutes(value);
|
||||||
|
} else {
|
||||||
|
setRefreshButtonHidden(true);
|
||||||
|
setCustomDTPickerVisible(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRefreshHandler = (): void => {
|
||||||
|
onSelectHandler(selectedTimeInterval);
|
||||||
|
onLastRefreshHandler();
|
||||||
|
};
|
||||||
|
|
||||||
const onCustomDateHandler = (dateTimeRange: DateTimeRangeType): void => {
|
const onCustomDateHandler = (dateTimeRange: DateTimeRangeType): void => {
|
||||||
if (dateTimeRange !== null) {
|
if (dateTimeRange !== null) {
|
||||||
const [startTimeMoment, endTimeMoment] = dateTimeRange;
|
const [startTimeMoment, endTimeMoment] = dateTimeRange;
|
||||||
@ -199,12 +199,12 @@ function DateTimeSelection({
|
|||||||
// this is triggred when we change the routes and based on that we are changing the default options
|
// this is triggred when we change the routes and based on that we are changing the default options
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const metricsTimeDuration = getLocalStorageKey(
|
const metricsTimeDuration = getLocalStorageKey(
|
||||||
LOCAL_STORAGE.METRICS_TIME_IN_DURATION,
|
LOCALSTORAGE.METRICS_TIME_IN_DURATION,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (metricsTimeDuration === null) {
|
if (metricsTimeDuration === null) {
|
||||||
setLocalStorageKey(
|
setLocalStorageKey(
|
||||||
LOCAL_STORAGE.METRICS_TIME_IN_DURATION,
|
LOCALSTORAGE.METRICS_TIME_IN_DURATION,
|
||||||
JSON.stringify({}),
|
JSON.stringify({}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -252,12 +252,12 @@ function DateTimeSelection({
|
|||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Form
|
<Form
|
||||||
form={form_dtselector}
|
form={formSelector}
|
||||||
layout="inline"
|
layout="inline"
|
||||||
initialValues={{ interval: selectedTime }}
|
initialValues={{ interval: selectedTime }}
|
||||||
>
|
>
|
||||||
<DefaultSelect
|
<DefaultSelect
|
||||||
onSelect={(value): void => onSelectHandler(value as Time)}
|
onSelect={(value: unknown): void => onSelectHandler(value as Time)}
|
||||||
value={getInputLabel(startTime, endTime, selectedTime)}
|
value={getInputLabel(startTime, endTime, selectedTime)}
|
||||||
data-testid="dropDown"
|
data-testid="dropDown"
|
||||||
>
|
>
|
||||||
|
@ -2,7 +2,7 @@ import { Col } from 'antd';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { matchPath, useLocation } from 'react-router-dom';
|
import { matchPath } from 'react-router-dom';
|
||||||
|
|
||||||
import ShowBreadcrumbs from './Breadcrumbs';
|
import ShowBreadcrumbs from './Breadcrumbs';
|
||||||
import DateTimeSelector from './DateTimeSelection';
|
import DateTimeSelector from './DateTimeSelection';
|
||||||
@ -21,7 +21,7 @@ function TopNav(): JSX.Element | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const checkRouteExists = (currentPath: string): boolean => {
|
const checkRouteExists = (currentPath: string): boolean => {
|
||||||
for (let i = 0; i < routesToSkip.length; ++i) {
|
for (let i = 0; i < routesToSkip.length; i += 1) {
|
||||||
if (
|
if (
|
||||||
matchPath(currentPath, { path: routesToSkip[i], exact: true, strict: true })
|
matchPath(currentPath, { path: routesToSkip[i], exact: true, strict: true })
|
||||||
) {
|
) {
|
||||||
|
@ -21,6 +21,8 @@ function DeleteAlert({
|
|||||||
payload: undefined,
|
payload: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const defaultErrorMessage = 'Something went wrong';
|
||||||
|
|
||||||
const onDeleteHandler = async (id: number): Promise<void> => {
|
const onDeleteHandler = async (id: number): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
setDeleteAlertState((state) => ({
|
setDeleteAlertState((state) => ({
|
||||||
@ -48,11 +50,11 @@ function DeleteAlert({
|
|||||||
...state,
|
...state,
|
||||||
loading: false,
|
loading: false,
|
||||||
error: true,
|
error: true,
|
||||||
errorMessage: response.error || 'Something went wrong',
|
errorMessage: response.error || defaultErrorMessage,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: response.error || 'Something went wrong',
|
message: response.error || defaultErrorMessage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -60,11 +62,11 @@ function DeleteAlert({
|
|||||||
...state,
|
...state,
|
||||||
loading: false,
|
loading: false,
|
||||||
error: true,
|
error: true,
|
||||||
errorMessage: 'Something went wrong',
|
errorMessage: defaultErrorMessage,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Something went wrong',
|
message: defaultErrorMessage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import useInterval from 'hooks/useInterval';
|
import useInterval from 'hooks/useInterval';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { generatePath } from 'react-router';
|
import { generatePath } from 'react-router-dom';
|
||||||
import { Alerts } from 'types/api/alerts/getAll';
|
import { Alerts } from 'types/api/alerts/getAll';
|
||||||
|
|
||||||
import DeleteAlert from './DeleteAlert';
|
import DeleteAlert from './DeleteAlert';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { Button } from 'antd';
|
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
@ -7,6 +6,7 @@ import { DeleteDashboard, DeleteDashboardProps } from 'store/actions';
|
|||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
|
|
||||||
import { Data } from '../index';
|
import { Data } from '../index';
|
||||||
|
import { TableLinkText } from './styles';
|
||||||
|
|
||||||
function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element {
|
function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element {
|
||||||
const onClickHandler = useCallback(() => {
|
const onClickHandler = useCallback(() => {
|
||||||
@ -15,11 +15,7 @@ function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element {
|
|||||||
});
|
});
|
||||||
}, [id, deleteDashboard]);
|
}, [id, deleteDashboard]);
|
||||||
|
|
||||||
return (
|
return <TableLinkText onClick={onClickHandler}>Delete</TableLinkText>;
|
||||||
<Button onClick={onClickHandler} type="link">
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
@ -40,10 +36,18 @@ const WrapperDeleteButton = connect(null, mapDispatchToProps)(DeleteButton);
|
|||||||
|
|
||||||
// This is to avoid the type collision
|
// This is to avoid the type collision
|
||||||
function Wrapper(props: Data): JSX.Element {
|
function Wrapper(props: Data): JSX.Element {
|
||||||
|
const { createdBy, description, id, key, lastUpdatedTime, name, tags } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WrapperDeleteButton
|
<WrapperDeleteButton
|
||||||
{...{
|
{...{
|
||||||
...props,
|
createdBy,
|
||||||
|
description,
|
||||||
|
id,
|
||||||
|
key,
|
||||||
|
lastUpdatedTime,
|
||||||
|
name,
|
||||||
|
tags,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
import { Button } from 'antd';
|
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { generatePath } from 'react-router-dom';
|
import { generatePath } from 'react-router-dom';
|
||||||
|
|
||||||
import { Data } from '..';
|
import { Data } from '..';
|
||||||
|
import { TableLinkText } from './styles';
|
||||||
|
|
||||||
function Name(name: Data['name'], data: Data): JSX.Element {
|
function Name(name: Data['name'], data: Data): JSX.Element {
|
||||||
const onClickHandler = () => {
|
const onClickHandler = (): void => {
|
||||||
|
const { id: DashboardId } = data;
|
||||||
|
|
||||||
history.push(
|
history.push(
|
||||||
generatePath(ROUTES.DASHBOARD, {
|
generatePath(ROUTES.DASHBOARD, {
|
||||||
dashboardId: data.id,
|
dashboardId: DashboardId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <TableLinkText onClick={onClickHandler}>{name}</TableLinkText>;
|
||||||
<Button onClick={onClickHandler} type="link">
|
|
||||||
{name}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Name;
|
export default Name;
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
|
/* eslint-disable react/destructuring-assignment */
|
||||||
import { Tag } from 'antd';
|
import { Tag } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Data } from '../index';
|
import { Data } from '../index';
|
||||||
|
|
||||||
function Tags(props: Data['tags']): JSX.Element {
|
function Tags(data: Data['tags']): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.map((e) => (
|
{data.map((e) => (
|
||||||
<Tag key={e}>{e}</Tag>
|
<Tag key={e}>{e}</Tag>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
import { blue } from '@ant-design/colors';
|
||||||
|
import { Typography } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const TableLinkText = styled(Typography.Text)`
|
||||||
|
color: ${blue.primary} !important;
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
@ -187,7 +187,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `sum(rate(signoz_latency_count{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"}[2m]))`,
|
query: `sum(rate(signoz_latency_count{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"}[2m]))`,
|
||||||
legend: 'Request per second',
|
legend: 'Requests',
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
yAxisUnit="reqps"
|
yAxisUnit="reqps"
|
||||||
@ -222,7 +222,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `max(sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", http_status_code=~"5.."}[1m]))*100/sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"}[1m]))) < 1000 OR vector(0)`,
|
query: `max(sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", http_status_code=~"5.."}[1m]))*100/sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"}[1m]))) < 1000 OR vector(0)`,
|
||||||
legend: 'Error Percentage (%)',
|
legend: 'Error Percentage',
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
yAxisUnit="%"
|
yAxisUnit="%"
|
||||||
|
@ -9,6 +9,8 @@ import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
|||||||
function External({ getWidget }: ExternalProps): JSX.Element {
|
function External({ getWidget }: ExternalProps): JSX.Element {
|
||||||
const { servicename } = useParams<{ servicename?: string }>();
|
const { servicename } = useParams<{ servicename?: string }>();
|
||||||
|
|
||||||
|
const legend = '{{http_url}}';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
@ -23,7 +25,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[1m])) by (http_url)) < 1000 OR vector(0)`,
|
query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[1m])) by (http_url)) < 1000 OR vector(0)`,
|
||||||
legend: '{{http_url}}',
|
legend,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
yAxisUnit="%"
|
yAxisUnit="%"
|
||||||
@ -65,7 +67,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[5m])) by (http_url)`,
|
query: `sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[5m])) by (http_url)`,
|
||||||
legend: '{{http_url}}',
|
legend,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
yAxisUnit="reqps"
|
yAxisUnit="reqps"
|
||||||
@ -85,7 +87,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `(sum(rate(signoz_external_call_latency_sum{service_name="${servicename}"}[5m])) by (http_url))/(sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[5m])) by (http_url))`,
|
query: `(sum(rate(signoz_external_call_latency_sum{service_name="${servicename}"}[5m])) by (http_url))/(sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[5m])) by (http_url))`,
|
||||||
legend: '{{http_url}}',
|
legend,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
yAxisUnit="ms"
|
yAxisUnit="ms"
|
||||||
|
@ -2,19 +2,20 @@ import { Button, Table, Tooltip } from 'antd';
|
|||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
|
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { topEndpointListItem } from 'store/actions/MetricsActions';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import history from 'lib/history';
|
|
||||||
|
|
||||||
function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
||||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { data } = props;
|
||||||
|
|
||||||
const params = useParams<{ servicename: string }>();
|
const params = useParams<{ servicename: string }>();
|
||||||
|
|
||||||
const handleOnClick = (operation: string): void => {
|
const handleOnClick = (operation: string): void => {
|
||||||
@ -80,7 +81,7 @@ function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
|||||||
title: 'Number of Calls',
|
title: 'Number of Calls',
|
||||||
dataIndex: 'numCalls',
|
dataIndex: 'numCalls',
|
||||||
key: 'numCalls',
|
key: 'numCalls',
|
||||||
sorter: (a: topEndpointListItem, b: topEndpointListItem): number =>
|
sorter: (a: TopEndpointListItem, b: TopEndpointListItem): number =>
|
||||||
a.numCalls - b.numCalls,
|
a.numCalls - b.numCalls,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -91,7 +92,7 @@ function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
|||||||
title={(): string => {
|
title={(): string => {
|
||||||
return 'Top Endpoints';
|
return 'Top Endpoints';
|
||||||
}}
|
}}
|
||||||
dataSource={props.data}
|
dataSource={data}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
rowKey="name"
|
rowKey="name"
|
||||||
@ -99,10 +100,18 @@ function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataProps = topEndpointListItem;
|
interface TopEndpointListItem {
|
||||||
|
p50: number;
|
||||||
|
p95: number;
|
||||||
|
p99: number;
|
||||||
|
numCalls: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataProps = TopEndpointListItem;
|
||||||
|
|
||||||
interface TopEndpointsTableProps {
|
interface TopEndpointsTableProps {
|
||||||
data: topEndpointListItem[];
|
data: TopEndpointListItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TopEndpointsTable;
|
export default TopEndpointsTable;
|
||||||
|
@ -22,6 +22,7 @@ function SkipOnBoardingModal({ onContinueClick }: Props): JSX.Element {
|
|||||||
frameBorder="0"
|
frameBorder="0"
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
allowFullScreen
|
allowFullScreen
|
||||||
|
title="youtube_video"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<Typography>No instrumentation data.</Typography>
|
<Typography>No instrumentation data.</Typography>
|
||||||
|
@ -6,8 +6,8 @@ import ROUTES from 'constants/routes';
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { servicesListItem } from 'store/actions/MetricsActions/metricsInterfaces';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
import { ServicesList } from 'types/api/metrics/getService';
|
||||||
import MetricReducer from 'types/reducer/metrics';
|
import MetricReducer from 'types/reducer/metrics';
|
||||||
|
|
||||||
import SkipBoardModal from './SkipOnBoardModal';
|
import SkipBoardModal from './SkipOnBoardModal';
|
||||||
@ -27,10 +27,6 @@ function Metrics(): JSX.Element {
|
|||||||
setSkipOnboarding(true);
|
setSkipOnboarding(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickHandler = (to: string): void => {
|
|
||||||
window.open(to, '_blank');
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
services.length === 0 &&
|
services.length === 0 &&
|
||||||
loading === false &&
|
loading === false &&
|
||||||
@ -88,6 +84,6 @@ function Metrics(): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataProps = servicesListItem;
|
type DataProps = ServicesList;
|
||||||
|
|
||||||
export default Metrics;
|
export default Metrics;
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import TimeSeries, {
|
import TimeSeries from 'assets/Dashboard/TimeSeries';
|
||||||
TimeSeriesProps as IconProps,
|
|
||||||
} from 'assets/Dashboard/TimeSeries';
|
|
||||||
import ValueIcon from 'assets/Dashboard/Value';
|
import ValueIcon from 'assets/Dashboard/Value';
|
||||||
|
|
||||||
const Items: ItemsProps[] = [
|
const Items: ItemsProps[] = [
|
||||||
@ -24,4 +22,8 @@ interface ItemsProps {
|
|||||||
display: string;
|
display: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
fillColor: React.CSSProperties['color'];
|
||||||
|
}
|
||||||
|
|
||||||
export default Items;
|
export default Items;
|
||||||
|
@ -2,9 +2,9 @@ import { Button, Divider } from 'antd';
|
|||||||
import Input from 'components/Input';
|
import Input from 'components/Input';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { DeleteQuery } from 'store/actions';
|
import { DeleteQuery } from 'store/actions';
|
||||||
@ -12,8 +12,11 @@ import {
|
|||||||
UpdateQuery,
|
UpdateQuery,
|
||||||
UpdateQueryProps,
|
UpdateQueryProps,
|
||||||
} from 'store/actions/dashboard/updateQuery';
|
} from 'store/actions/dashboard/updateQuery';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { DeleteQueryProps } from 'types/actions/dashboard';
|
import { DeleteQueryProps } from 'types/actions/dashboard';
|
||||||
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
|
import DashboardReducer from 'types/reducer/dashboards';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ButtonContainer,
|
ButtonContainer,
|
||||||
@ -32,10 +35,27 @@ function Query({
|
|||||||
const [promqlQuery, setPromqlQuery] = useState(preQuery);
|
const [promqlQuery, setPromqlQuery] = useState(preQuery);
|
||||||
const [legendFormat, setLegendFormat] = useState(preLegend);
|
const [legendFormat, setLegendFormat] = useState(preLegend);
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
|
const { dashboards } = useSelector<AppState, DashboardReducer>(
|
||||||
|
(state) => state.dashboards,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [selectedDashboards] = dashboards;
|
||||||
|
const { widgets } = selectedDashboards.data;
|
||||||
|
|
||||||
const query = new URLSearchParams(search);
|
const query = new URLSearchParams(search);
|
||||||
const widgetId = query.get('widgetId') || '';
|
const widgetId = query.get('widgetId') || '';
|
||||||
|
|
||||||
|
const urlQuery = useMemo(() => {
|
||||||
|
return new URLSearchParams(search);
|
||||||
|
}, [search]);
|
||||||
|
|
||||||
|
const getWidget = useCallback(() => {
|
||||||
|
const widgetId = urlQuery.get('widgetId');
|
||||||
|
return widgets?.find((e) => e.id === widgetId);
|
||||||
|
}, [widgets, urlQuery]);
|
||||||
|
|
||||||
|
const selectedWidget = getWidget() as Widgets;
|
||||||
|
|
||||||
const onChangeHandler = useCallback(
|
const onChangeHandler = useCallback(
|
||||||
(setFunc: React.Dispatch<React.SetStateAction<string>>, value: string) => {
|
(setFunc: React.Dispatch<React.SetStateAction<string>>, value: string) => {
|
||||||
setFunc(value);
|
setFunc(value);
|
||||||
@ -49,6 +69,7 @@ function Query({
|
|||||||
legend: legendFormat,
|
legend: legendFormat,
|
||||||
query: promqlQuery,
|
query: promqlQuery,
|
||||||
widgetId,
|
widgetId,
|
||||||
|
yAxisUnit: selectedWidget.yAxisUnit,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { PlusOutlined } from '@ant-design/icons';
|
|||||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { CreateQuery, CreateQueryProps } from 'store/actions';
|
import { CreateQuery, CreateQueryProps } from 'store/actions';
|
||||||
|
@ -4,7 +4,7 @@ import { NewWidgetProps } from 'container/NewWidget';
|
|||||||
import getChartData from 'lib/getChartData';
|
import getChartData from 'lib/getChartData';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import DashboardReducer from 'types/reducer/dashboards';
|
import DashboardReducer from 'types/reducer/dashboards';
|
||||||
|
|
||||||
|
@ -4,14 +4,26 @@ import React from 'react';
|
|||||||
|
|
||||||
import { flattenedCategories } from './dataFormatCategories';
|
import { flattenedCategories } from './dataFormatCategories';
|
||||||
|
|
||||||
const findCategoryById = (searchValue) =>
|
const findCategoryById = (
|
||||||
find(flattenedCategories, (option) => option.id == searchValue);
|
searchValue: string,
|
||||||
const findCategoryByName = (searchValue) =>
|
): Record<string, string> | undefined =>
|
||||||
find(flattenedCategories, (option) => option.name == searchValue);
|
find(flattenedCategories, (option) => option.id === searchValue);
|
||||||
|
const findCategoryByName = (
|
||||||
|
searchValue: string,
|
||||||
|
): Record<string, string> | undefined =>
|
||||||
|
find(flattenedCategories, (option) => option.name === searchValue);
|
||||||
|
|
||||||
function YAxisUnitSelector({ defaultValue, onSelect, fieldLabel }): JSX.Element {
|
function YAxisUnitSelector({
|
||||||
|
defaultValue,
|
||||||
|
onSelect,
|
||||||
|
fieldLabel,
|
||||||
|
}: {
|
||||||
|
defaultValue: string;
|
||||||
|
onSelect: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
fieldLabel: string;
|
||||||
|
}): JSX.Element {
|
||||||
const onSelectHandler = (selectedValue: string): void => {
|
const onSelectHandler = (selectedValue: string): void => {
|
||||||
onSelect(findCategoryByName(selectedValue)?.id);
|
onSelect(findCategoryByName(selectedValue)?.id || '');
|
||||||
};
|
};
|
||||||
const options = flattenedCategories.map((options) => ({
|
const options = flattenedCategories.map((options) => ({
|
||||||
value: options.name,
|
value: options.name,
|
||||||
@ -26,9 +38,14 @@ function YAxisUnitSelector({ defaultValue, onSelect, fieldLabel }): JSX.Element
|
|||||||
options={options}
|
options={options}
|
||||||
defaultValue={findCategoryById(defaultValue)?.name}
|
defaultValue={findCategoryById(defaultValue)?.name}
|
||||||
onSelect={onSelectHandler}
|
onSelect={onSelectHandler}
|
||||||
filterOption={(inputValue, option): boolean =>
|
filterOption={(inputValue, option): boolean => {
|
||||||
option!.value.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
|
if (option) {
|
||||||
}
|
return (
|
||||||
|
option.value.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Input size="large" placeholder="Unit" allowClear />
|
<Input size="large" placeholder="Unit" allowClear />
|
||||||
</AutoComplete>
|
</AutoComplete>
|
||||||
|
@ -1,23 +1,11 @@
|
|||||||
import {
|
import { Input } from 'antd';
|
||||||
// Button,
|
|
||||||
Input,
|
|
||||||
// Slider,
|
|
||||||
// Switch,
|
|
||||||
// Typography,
|
|
||||||
} from 'antd';
|
|
||||||
import InputComponent from 'components/Input';
|
import InputComponent from 'components/Input';
|
||||||
import TimePreference from 'components/TimePreferenceDropDown';
|
import TimePreference from 'components/TimePreferenceDropDown';
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
import GraphTypes from 'container/NewDashboard/ComponentsSlider/menuItems';
|
import GraphTypes from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
import { dataTypeCategories } from './dataFormatCategories';
|
import { Container, Title } from './styles';
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
// NullButtonContainer, TextContainer,
|
|
||||||
Title,
|
|
||||||
} from './styles';
|
|
||||||
// import {ca} from '@grafana/data'
|
|
||||||
import { timePreferance } from './timeItems';
|
import { timePreferance } from './timeItems';
|
||||||
import YAxisUnitSelector from './YAxisUnitSelector';
|
import YAxisUnitSelector from './YAxisUnitSelector';
|
||||||
|
|
||||||
@ -25,14 +13,8 @@ const { TextArea } = Input;
|
|||||||
|
|
||||||
function RightContainer({
|
function RightContainer({
|
||||||
description,
|
description,
|
||||||
// opacity,
|
|
||||||
// selectedNullZeroValue,
|
|
||||||
setDescription,
|
setDescription,
|
||||||
// setOpacity,
|
|
||||||
// setSelectedNullZeroValue,
|
|
||||||
// setStacked,
|
|
||||||
setTitle,
|
setTitle,
|
||||||
// stacked,
|
|
||||||
title,
|
title,
|
||||||
selectedGraph,
|
selectedGraph,
|
||||||
setSelectedTime,
|
setSelectedTime,
|
||||||
@ -47,21 +29,6 @@ function RightContainer({
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
// const nullValueButtons = [
|
|
||||||
// {
|
|
||||||
// check: 'zero',
|
|
||||||
// name: 'Zero',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// check: 'interpolate',
|
|
||||||
// name: 'Interpolate',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// check: 'blank',
|
|
||||||
// name: 'Blank',
|
|
||||||
// },
|
|
||||||
// ];
|
|
||||||
|
|
||||||
const selectedGraphType =
|
const selectedGraphType =
|
||||||
GraphTypes.find((e) => e.name === selectedGraph)?.display || '';
|
GraphTypes.find((e) => e.name === selectedGraph)?.display || '';
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
export const timeItems: timePreferance[] = [
|
export const timeItems: timePreferance[] = [
|
||||||
{
|
{
|
||||||
name: 'Global Time',
|
name: 'Global Time',
|
||||||
|
@ -5,8 +5,7 @@ import history from 'lib/history';
|
|||||||
import { DashboardWidgetPageParams } from 'pages/DashboardWidget';
|
import { DashboardWidgetPageParams } from 'pages/DashboardWidget';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { useLocation, useParams } from 'react-router';
|
import { generatePath, useLocation, useParams } from 'react-router-dom';
|
||||||
import { generatePath } from 'react-router-dom';
|
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { ApplySettingsToPanel, ApplySettingsToPanelProps } from 'store/actions';
|
import { ApplySettingsToPanel, ApplySettingsToPanelProps } from 'store/actions';
|
||||||
@ -30,7 +29,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
|
|
||||||
import LeftContainer from './LeftContainer';
|
import LeftContainer from './LeftContainer';
|
||||||
import RightContainer from './RightContainer';
|
import RightContainer from './RightContainer';
|
||||||
import timeItems, { timePreferance } from './RightContainer/timeItems';
|
import TimeItems, { timePreferance } from './RightContainer/timeItems';
|
||||||
import {
|
import {
|
||||||
ButtonContainer,
|
ButtonContainer,
|
||||||
Container,
|
Container,
|
||||||
@ -91,7 +90,7 @@ function NewWidget({
|
|||||||
|
|
||||||
const getSelectedTime = useCallback(
|
const getSelectedTime = useCallback(
|
||||||
() =>
|
() =>
|
||||||
timeItems.find(
|
TimeItems.find(
|
||||||
(e) => e.enum === (selectedWidget?.timePreferance || 'GLOBAL_TIME'),
|
(e) => e.enum === (selectedWidget?.timePreferance || 'GLOBAL_TIME'),
|
||||||
),
|
),
|
||||||
[selectedWidget],
|
[selectedWidget],
|
||||||
|
@ -3,7 +3,7 @@ import getLocalStorageKey from 'api/browser/localstorage/get';
|
|||||||
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import setTheme from 'lib/theme/setTheme';
|
import setTheme, { AppMode } from 'lib/theme/setTheme';
|
||||||
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||||
import { NavLink, useLocation } from 'react-router-dom';
|
import { NavLink, useLocation } from 'react-router-dom';
|
||||||
@ -36,10 +36,10 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
const toggleTheme = useCallback(() => {
|
const toggleTheme = useCallback(() => {
|
||||||
const preMode: appMode = isDarkMode ? 'lightMode' : 'darkMode';
|
const preMode: AppMode = isDarkMode ? 'lightMode' : 'darkMode';
|
||||||
setTheme(preMode);
|
setTheme(preMode);
|
||||||
|
|
||||||
const id: appMode = preMode;
|
const id: AppMode = preMode;
|
||||||
const { head } = document;
|
const { head } = document;
|
||||||
const link = document.createElement('link');
|
const link = document.createElement('link');
|
||||||
link.rel = 'stylesheet';
|
link.rel = 'stylesheet';
|
||||||
@ -115,8 +115,6 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type appMode = 'darkMode' | 'lightMode';
|
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
toggleDarkMode: () => void;
|
toggleDarkMode: () => void;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Layout, Menu, Switch, Typography } from 'antd';
|
import { Layout, Switch, Typography } from 'antd';
|
||||||
|
import { StyledCSS } from 'container/GantChart/Trace/styles';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
const { Sider: SiderComponent } = Layout;
|
const { Sider: SiderComponent } = Layout;
|
||||||
@ -37,7 +38,7 @@ interface DarkModeProps {
|
|||||||
|
|
||||||
export const ToggleButton = styled(Switch)<DarkModeProps>`
|
export const ToggleButton = styled(Switch)<DarkModeProps>`
|
||||||
&&& {
|
&&& {
|
||||||
background: ${({ checked }) => checked === false && 'grey'};
|
background: ${({ checked }): string => (checked === false ? 'grey' : '')};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -51,11 +52,11 @@ export const SlackMenuItemContainer = styled.div<LogoProps>`
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 48px;
|
bottom: 48px;
|
||||||
background: #262626;
|
background: #262626;
|
||||||
width: ${({ collapsed }) => (!collapsed ? '200px' : '80px')};
|
width: ${({ collapsed }): string => (!collapsed ? '200px' : '80px')};
|
||||||
|
|
||||||
&&& {
|
&&& {
|
||||||
li {
|
li {
|
||||||
${({ collapsed }) =>
|
${({ collapsed }): StyledCSS =>
|
||||||
collapsed &&
|
collapsed &&
|
||||||
css`
|
css`
|
||||||
padding-left: 24px;
|
padding-left: 24px;
|
||||||
@ -63,9 +64,9 @@ export const SlackMenuItemContainer = styled.div<LogoProps>`
|
|||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin-left: ${({ collapsed }) => (collapsed ? '0' : '24px')};
|
margin-left: ${({ collapsed }): string => (collapsed ? '0' : '24px')};
|
||||||
|
|
||||||
${({ collapsed }) =>
|
${({ collapsed }): StyledCSS =>
|
||||||
collapsed &&
|
collapsed &&
|
||||||
css`
|
css`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import { StyledDiv } from 'components/Styled';
|
import { StyledDiv } from 'components/Styled';
|
||||||
|
import { ITraceMetaData } from 'container/GantChart';
|
||||||
import { IIntervalUnit, INTERVAL_UNITS } from 'container/TraceDetail/utils';
|
import { IIntervalUnit, INTERVAL_UNITS } from 'container/TraceDetail/utils';
|
||||||
import useThemeMode from 'hooks/useThemeMode';
|
import useThemeMode from 'hooks/useThemeMode';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useMeasure } from 'react-use';
|
import { useMeasure } from 'react-use';
|
||||||
|
|
||||||
import { styles, Svg, TimelineInterval } from './styles';
|
import { styles, Svg, TimelineInterval } from './styles';
|
||||||
import { Interval } from './types';
|
import { Interval } from './types';
|
||||||
import { getIntervals, getIntervalSpread } from './utils';
|
import { getIntervals, getIntervalSpread } from './utils';
|
||||||
|
|
||||||
const Timeline_Height = 22;
|
const TimelineHeight = 22;
|
||||||
const Timeline_H_Spacing = 0;
|
const TimelineHSpacing = 0;
|
||||||
|
|
||||||
function Timeline({
|
function Timeline({
|
||||||
traceMetaData,
|
traceMetaData,
|
||||||
@ -19,6 +20,10 @@ function Timeline({
|
|||||||
const [ref, { width }] = useMeasure<HTMLDivElement>();
|
const [ref, { width }] = useMeasure<HTMLDivElement>();
|
||||||
const { isDarkMode } = useThemeMode();
|
const { isDarkMode } = useThemeMode();
|
||||||
|
|
||||||
|
const asd = useRef('');
|
||||||
|
|
||||||
|
asd.current = '1';
|
||||||
|
|
||||||
const [intervals, setIntervals] = useState<Interval[] | null>(null);
|
const [intervals, setIntervals] = useState<Interval[] | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -32,10 +37,10 @@ function Timeline({
|
|||||||
});
|
});
|
||||||
|
|
||||||
let intervalUnit = INTERVAL_UNITS[0];
|
let intervalUnit = INTERVAL_UNITS[0];
|
||||||
for (let idx = 0; idx < INTERVAL_UNITS.length; idx++) {
|
for (let idx = 0; idx < INTERVAL_UNITS.length; idx += 1) {
|
||||||
const standard_interval = INTERVAL_UNITS[idx];
|
const standardInterval = INTERVAL_UNITS[idx];
|
||||||
if (baseSpread * standard_interval.multiplier < 1) {
|
if (baseSpread * standardInterval.multiplier < 1) {
|
||||||
const index = parseInt(idx, 10);
|
const index = idx;
|
||||||
if (index > 1) intervalUnit = INTERVAL_UNITS[index - 1];
|
if (index > 1) intervalUnit = INTERVAL_UNITS[index - 1];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -54,16 +59,16 @@ function Timeline({
|
|||||||
}, [traceMetaData, globalTraceMetadata, setIntervalUnit]);
|
}, [traceMetaData, globalTraceMetadata, setIntervalUnit]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledDiv ref={ref} styledclass={[styles.timelineContainer]}>
|
<StyledDiv ref={ref as never} styledclass={[styles.timelineContainer]}>
|
||||||
<Svg
|
<Svg
|
||||||
viewBox={`0 0 ${width} ${Timeline_Height}`}
|
viewBox={`0 0 ${width} ${TimelineHeight}`}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<line
|
<line
|
||||||
x1={Timeline_H_Spacing}
|
x1={TimelineHSpacing}
|
||||||
y1={Timeline_Height}
|
y1={TimelineHeight}
|
||||||
x2={width - Timeline_H_Spacing}
|
x2={width - TimelineHSpacing}
|
||||||
y2={Timeline_Height}
|
y2={TimelineHeight}
|
||||||
stroke={isDarkMode ? 'white' : 'black'}
|
stroke={isDarkMode ? 'white' : 'black'}
|
||||||
strokeWidth="1"
|
strokeWidth="1"
|
||||||
/>
|
/>
|
||||||
@ -71,8 +76,8 @@ function Timeline({
|
|||||||
intervals.map((interval, index) => (
|
intervals.map((interval, index) => (
|
||||||
<TimelineInterval
|
<TimelineInterval
|
||||||
transform={`translate(${
|
transform={`translate(${
|
||||||
Timeline_H_Spacing +
|
TimelineHSpacing +
|
||||||
(interval.percentage * (width - 2 * Timeline_H_Spacing)) / 100
|
(interval.percentage * (width - 2 * TimelineHSpacing)) / 100
|
||||||
},0)`}
|
},0)`}
|
||||||
key={`${interval.label + interval.percentage + index}`}
|
key={`${interval.label + interval.percentage + index}`}
|
||||||
>
|
>
|
||||||
@ -80,8 +85,8 @@ function Timeline({
|
|||||||
{interval.label}
|
{interval.label}
|
||||||
</text>
|
</text>
|
||||||
<line
|
<line
|
||||||
y1={Timeline_Height - 5}
|
y1={TimelineHeight - 5}
|
||||||
y2={Timeline_Height + 0.5}
|
y2={TimelineHeight + 0.5}
|
||||||
stroke={isDarkMode ? 'white' : 'black'}
|
stroke={isDarkMode ? 'white' : 'black'}
|
||||||
strokeWidth="1"
|
strokeWidth="1"
|
||||||
/>
|
/>
|
||||||
@ -100,8 +105,7 @@ interface TimelineProps {
|
|||||||
totalSpans: number;
|
totalSpans: number;
|
||||||
levels: number;
|
levels: number;
|
||||||
};
|
};
|
||||||
globalTraceMetadata: Record<string, number>;
|
globalTraceMetadata: ITraceMetaData;
|
||||||
intervalUnit: IIntervalUnit;
|
|
||||||
setIntervalUnit: React.Dispatch<React.SetStateAction<IIntervalUnit>>;
|
setIntervalUnit: React.Dispatch<React.SetStateAction<IIntervalUnit>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import { ITraceMetaData } from 'container/GantChart';
|
||||||
import {
|
import {
|
||||||
INTERVAL_UNITS,
|
IIntervalUnit,
|
||||||
resolveTimeFromInterval,
|
resolveTimeFromInterval,
|
||||||
} from 'container/TraceDetail/utils';
|
} from 'container/TraceDetail/utils';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
@ -10,6 +11,9 @@ import { Interval } from './types';
|
|||||||
export const getIntervalSpread = ({
|
export const getIntervalSpread = ({
|
||||||
localTraceMetaData,
|
localTraceMetaData,
|
||||||
globalTraceMetadata,
|
globalTraceMetadata,
|
||||||
|
}: {
|
||||||
|
localTraceMetaData: ITraceMetaData;
|
||||||
|
globalTraceMetadata: ITraceMetaData;
|
||||||
}): {
|
}): {
|
||||||
baseInterval: number;
|
baseInterval: number;
|
||||||
baseSpread: number;
|
baseSpread: number;
|
||||||
@ -46,6 +50,11 @@ export const getIntervals = ({
|
|||||||
baseSpread,
|
baseSpread,
|
||||||
intervalSpreadNormalized,
|
intervalSpreadNormalized,
|
||||||
intervalUnit,
|
intervalUnit,
|
||||||
|
}: {
|
||||||
|
baseInterval: number;
|
||||||
|
baseSpread: number;
|
||||||
|
intervalSpreadNormalized: number;
|
||||||
|
intervalUnit: IIntervalUnit;
|
||||||
}): Interval[] => {
|
}): Interval[] => {
|
||||||
const intervals: Interval[] = [
|
const intervals: Interval[] = [
|
||||||
{
|
{
|
||||||
@ -60,21 +69,21 @@ export const getIntervals = ({
|
|||||||
let elapsedIntervals = 0;
|
let elapsedIntervals = 0;
|
||||||
|
|
||||||
while (tempBaseSpread && intervals.length < 20) {
|
while (tempBaseSpread && intervals.length < 20) {
|
||||||
let interval_time;
|
let intervalTime;
|
||||||
if (tempBaseSpread <= 1.5 * intervalSpreadNormalized) {
|
if (tempBaseSpread <= 1.5 * intervalSpreadNormalized) {
|
||||||
interval_time = elapsedIntervals + tempBaseSpread;
|
intervalTime = elapsedIntervals + tempBaseSpread;
|
||||||
tempBaseSpread = 0;
|
tempBaseSpread = 0;
|
||||||
} else {
|
} else {
|
||||||
interval_time = elapsedIntervals + intervalSpreadNormalized;
|
intervalTime = elapsedIntervals + intervalSpreadNormalized;
|
||||||
tempBaseSpread -= intervalSpreadNormalized;
|
tempBaseSpread -= intervalSpreadNormalized;
|
||||||
}
|
}
|
||||||
elapsedIntervals = interval_time;
|
elapsedIntervals = intervalTime;
|
||||||
const interval: Interval = {
|
const interval: Interval = {
|
||||||
label: `${toFixed(
|
label: `${toFixed(
|
||||||
resolveTimeFromInterval(interval_time + baseInterval, intervalUnit),
|
resolveTimeFromInterval(intervalTime + baseInterval, intervalUnit),
|
||||||
2,
|
2,
|
||||||
)}${intervalUnit.name}`,
|
)}${intervalUnit.name}`,
|
||||||
percentage: (interval_time / baseSpread) * 100,
|
percentage: (intervalTime / baseSpread) * 100,
|
||||||
};
|
};
|
||||||
intervals.push(interval);
|
intervals.push(interval);
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,8 @@ import { Checkbox, notification, Typography } from 'antd';
|
|||||||
import getFilters from 'api/trace/getFilters';
|
import getFilters from 'api/trace/getFilters';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
|
||||||
import { SelectedTraceFilter } from 'store/actions/trace/selectTraceFilter';
|
|
||||||
import { getFilter, updateURL } from 'store/actions/trace/util';
|
import { getFilter, updateURL } from 'store/actions/trace/util';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
@ -30,17 +28,18 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
const globalTime = useSelector<AppState, GlobalReducer>(
|
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
const { keyValue, name, value } = props;
|
||||||
|
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
const isUserSelected =
|
const isUserSelected =
|
||||||
(userSelectedFilter.get(props.name) || []).find(
|
(userSelectedFilter.get(name) || []).find((e) => e === keyValue) !==
|
||||||
(e) => e === props.keyValue,
|
undefined;
|
||||||
) !== undefined;
|
|
||||||
|
|
||||||
const onCheckHandler = async () => {
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
|
const onCheckHandler = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
@ -48,48 +47,46 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
const preUserSelectedMap = new Map(userSelectedFilter);
|
const preUserSelectedMap = new Map(userSelectedFilter);
|
||||||
const preIsFilterExclude = new Map(isFilterExclude);
|
const preIsFilterExclude = new Map(isFilterExclude);
|
||||||
|
|
||||||
const isTopicPresent = preUserSelectedMap.get(props.name);
|
const isTopicPresent = preUserSelectedMap.get(name);
|
||||||
|
|
||||||
// append the value
|
// append the value
|
||||||
if (!isTopicPresent) {
|
if (!isTopicPresent) {
|
||||||
preUserSelectedMap.set(props.name, [props.keyValue]);
|
preUserSelectedMap.set(name, [keyValue]);
|
||||||
} else {
|
} else {
|
||||||
const isValuePresent =
|
const isValuePresent =
|
||||||
isTopicPresent.find((e) => e === props.keyValue) !== undefined;
|
isTopicPresent.find((e) => e === keyValue) !== undefined;
|
||||||
|
|
||||||
// check the value if present then remove the value or isChecked
|
// check the value if present then remove the value or isChecked
|
||||||
if (isValuePresent) {
|
if (isValuePresent) {
|
||||||
preUserSelectedMap.set(
|
preUserSelectedMap.set(
|
||||||
props.name,
|
name,
|
||||||
isTopicPresent.filter((e) => e !== props.keyValue),
|
isTopicPresent.filter((e) => e !== keyValue),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// if not present add into the array of string
|
// if not present add into the array of string
|
||||||
preUserSelectedMap.set(props.name, [...isTopicPresent, props.keyValue]);
|
preUserSelectedMap.set(name, [...isTopicPresent, keyValue]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newSelectedMap.get(props.name)?.find((e) => e === props.keyValue)) {
|
if (newSelectedMap.get(name)?.find((e) => e === keyValue)) {
|
||||||
newSelectedMap.set(props.name, [
|
newSelectedMap.set(name, [
|
||||||
...(newSelectedMap.get(props.name) || []).filter(
|
...(newSelectedMap.get(name) || []).filter((e) => e !== keyValue),
|
||||||
(e) => e !== props.keyValue,
|
|
||||||
),
|
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
newSelectedMap.set(props.name, [
|
newSelectedMap.set(name, [
|
||||||
...new Set([...(newSelectedMap.get(props.name) || []), props.keyValue]),
|
...new Set([...(newSelectedMap.get(name) || []), keyValue]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preIsFilterExclude.get(props.name) !== false) {
|
if (preIsFilterExclude.get(name) !== false) {
|
||||||
preIsFilterExclude.set(props.name, true);
|
preIsFilterExclude.set(name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await getFilters({
|
const response = await getFilters({
|
||||||
other: Object.fromEntries(newSelectedMap),
|
other: Object.fromEntries(newSelectedMap),
|
||||||
end: String(globalTime.maxTime),
|
end: String(globalTime.maxTime),
|
||||||
start: String(globalTime.minTime),
|
start: String(globalTime.minTime),
|
||||||
getFilters: filterToFetchData.filter((e) => e !== props.name),
|
getFilters: filterToFetchData.filter((e) => e !== name),
|
||||||
isFilterExclude: preIsFilterExclude,
|
isFilterExclude: preIsFilterExclude,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -97,15 +94,15 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
const updatedFilter = getFilter(response.payload);
|
const updatedFilter = getFilter(response.payload);
|
||||||
|
|
||||||
updatedFilter.forEach((value, key) => {
|
updatedFilter.forEach((value, key) => {
|
||||||
if (key !== 'duration' && props.name !== key) {
|
if (key !== 'duration' && name !== key) {
|
||||||
preUserSelectedMap.set(key, Object.keys(value));
|
preUserSelectedMap.set(key, Object.keys(value));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
updatedFilter.set(props.name, {
|
updatedFilter.set(name, {
|
||||||
[`${props.keyValue}`]: '-1',
|
[`${keyValue}`]: '-1',
|
||||||
...(filter.get(props.name) || {}),
|
...(filter.get(name) || {}),
|
||||||
...(updatedFilter.get(props.name) || {}),
|
...(updatedFilter.get(name) || {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -156,12 +153,12 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
onClick={onCheckHandler}
|
onClick={onCheckHandler}
|
||||||
checked={isCheckBoxSelected}
|
checked={isCheckBoxSelected}
|
||||||
defaultChecked
|
defaultChecked
|
||||||
key={props.keyValue}
|
key={keyValue}
|
||||||
>
|
>
|
||||||
{props.keyValue}
|
{keyValue}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
{isCheckBoxSelected ? (
|
{isCheckBoxSelected ? (
|
||||||
<Typography>{props.value}</Typography>
|
<Typography>{value}</Typography>
|
||||||
) : (
|
) : (
|
||||||
<Typography>-</Typography>
|
<Typography>-</Typography>
|
||||||
)}
|
)}
|
||||||
@ -169,23 +166,10 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface CheckBoxProps {
|
||||||
selectedTraceFilter: (props: {
|
|
||||||
topic: TraceFilterEnum;
|
|
||||||
value: string;
|
|
||||||
}) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CheckBoxProps extends DispatchProps {
|
|
||||||
keyValue: string;
|
keyValue: string;
|
||||||
value: string;
|
|
||||||
name: TraceFilterEnum;
|
name: TraceFilterEnum;
|
||||||
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
export default CheckBoxComponent;
|
||||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
|
||||||
): DispatchProps => ({
|
|
||||||
selectedTraceFilter: bindActionCreators(SelectedTraceFilter, dispatch),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(CheckBoxComponent);
|
|
||||||
|
@ -10,7 +10,9 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
|
|||||||
(state) => state.traces,
|
(state) => state.traces,
|
||||||
);
|
);
|
||||||
|
|
||||||
const status = filter.get(props.name) || {};
|
const { name } = props;
|
||||||
|
|
||||||
|
const status = filter.get(name) || {};
|
||||||
|
|
||||||
const statusObj = Object.keys(status);
|
const statusObj = Object.keys(status);
|
||||||
|
|
||||||
@ -20,7 +22,7 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
|
|||||||
<CheckBoxComponent
|
<CheckBoxComponent
|
||||||
key={e}
|
key={e}
|
||||||
{...{
|
{...{
|
||||||
name: props.name,
|
name,
|
||||||
keyValue: e,
|
keyValue: e,
|
||||||
value: status[e],
|
value: status[e],
|
||||||
}}
|
}}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react/no-unstable-nested-components */
|
||||||
import { Input, Slider } from 'antd';
|
import { Input, Slider } from 'antd';
|
||||||
import { SliderRangeProps } from 'antd/lib/slider';
|
import { SliderRangeProps } from 'antd/lib/slider';
|
||||||
import getFilters from 'api/trace/getFilters';
|
import getFilters from 'api/trace/getFilters';
|
||||||
@ -18,7 +19,7 @@ import { Container, InputContainer, Text } from './styles';
|
|||||||
|
|
||||||
dayjs.extend(durationPlugin);
|
dayjs.extend(durationPlugin);
|
||||||
|
|
||||||
const getMs = (value: string) => {
|
const getMs = (value: string): string => {
|
||||||
return dayjs
|
return dayjs
|
||||||
.duration({
|
.duration({
|
||||||
milliseconds: parseInt(value, 10) / 1000000,
|
milliseconds: parseInt(value, 10) / 1000000,
|
||||||
@ -42,7 +43,9 @@ function Duration(): JSX.Element {
|
|||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
const getDuration = () => {
|
const getDuration = ():
|
||||||
|
| { maxDuration: string; minDuration: string }
|
||||||
|
| Record<string, string> => {
|
||||||
const selectedDuration = selectedFilter.get('duration');
|
const selectedDuration = selectedFilter.get('duration');
|
||||||
|
|
||||||
if (selectedDuration) {
|
if (selectedDuration) {
|
||||||
@ -65,7 +68,7 @@ function Duration(): JSX.Element {
|
|||||||
|
|
||||||
const defaultValue = [parseFloat(minDuration), parseFloat(maxDuration)];
|
const defaultValue = [parseFloat(minDuration), parseFloat(maxDuration)];
|
||||||
|
|
||||||
const updatedUrl = async (min: number, max: number) => {
|
const updatedUrl = async (min: number, max: number): Promise<void> => {
|
||||||
const preSelectedFilter = new Map(selectedFilter);
|
const preSelectedFilter = new Map(selectedFilter);
|
||||||
const preUserSelected = new Map(userSelectedFilter);
|
const preUserSelected = new Map(userSelectedFilter);
|
||||||
|
|
||||||
@ -114,7 +117,7 @@ function Duration(): JSX.Element {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRangeSliderHandler = (number: [number, number]) => {
|
const onRangeSliderHandler = (number: [number, number]): void => {
|
||||||
const [min, max] = number;
|
const [min, max] = number;
|
||||||
|
|
||||||
setLocalMin(min.toString());
|
setLocalMin(min.toString());
|
||||||
@ -124,11 +127,10 @@ function Duration(): JSX.Element {
|
|||||||
const debouncedFunction = useDebouncedFn(
|
const debouncedFunction = useDebouncedFn(
|
||||||
(min, max) => {
|
(min, max) => {
|
||||||
console.log('debounce function');
|
console.log('debounce function');
|
||||||
updatedUrl(min, max);
|
updatedUrl(min as number, max as number);
|
||||||
},
|
},
|
||||||
500,
|
500,
|
||||||
undefined,
|
undefined,
|
||||||
[],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeMaxHandler: React.ChangeEventHandler<HTMLInputElement> = (
|
const onChangeMaxHandler: React.ChangeEventHandler<HTMLInputElement> = (
|
||||||
@ -187,21 +189,16 @@ function Duration(): JSX.Element {
|
|||||||
min={parseFloat((filter.get('duration') || {}).minDuration)}
|
min={parseFloat((filter.get('duration') || {}).minDuration)}
|
||||||
max={parseFloat((filter.get('duration') || {}).maxDuration)}
|
max={parseFloat((filter.get('duration') || {}).maxDuration)}
|
||||||
range
|
range
|
||||||
tipFormatter={(value) => {
|
tipFormatter={(value): JSX.Element => {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return '';
|
return <div />;
|
||||||
}
|
}
|
||||||
return <div>{`${getMs(value.toString())}ms`}</div>;
|
return <div>{`${getMs(value.toString())}ms`}</div>;
|
||||||
}}
|
}}
|
||||||
onChange={([min, max]) => {
|
onChange={([min, max]): void => {
|
||||||
onRangeSliderHandler([min, max]);
|
onRangeSliderHandler([min, max]);
|
||||||
}}
|
}}
|
||||||
onAfterChange={onRangeHandler}
|
onAfterChange={onRangeHandler}
|
||||||
// onAfterChange={([min, max]) => {
|
|
||||||
// const returnFunction = debounce((min, max) => updatedUrl(min, max));
|
|
||||||
|
|
||||||
// returnFunction(min, max);
|
|
||||||
// }}
|
|
||||||
value={[parseFloat(localMin), parseFloat(localMax)]}
|
value={[parseFloat(localMin), parseFloat(localMax)]}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -38,8 +38,10 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
userSelectedFilter,
|
userSelectedFilter,
|
||||||
} = useSelector<AppState, TraceReducer>((state) => state.traces);
|
} = useSelector<AppState, TraceReducer>((state) => state.traces);
|
||||||
|
|
||||||
|
const { name: PanelName, isOpen: IsPanelOpen } = props;
|
||||||
|
|
||||||
const isDefaultOpen =
|
const isDefaultOpen =
|
||||||
filterToFetchData.find((e) => e === props.name) !== undefined;
|
filterToFetchData.find((e) => e === PanelName) !== undefined;
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
@ -49,6 +51,9 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
|
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
|
const defaultErrorMessage = 'Something went wrong';
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
const onExpandHandler: React.MouseEventHandler<HTMLDivElement> = async (e) => {
|
const onExpandHandler: React.MouseEventHandler<HTMLDivElement> = async (e) => {
|
||||||
try {
|
try {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -60,14 +65,14 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
const getPreUserSelected = new Map(userSelectedFilter);
|
const getPreUserSelected = new Map(userSelectedFilter);
|
||||||
|
|
||||||
if (!isDefaultOpen) {
|
if (!isDefaultOpen) {
|
||||||
updatedFilterData = [props.name];
|
updatedFilterData = [PanelName];
|
||||||
} else {
|
} else {
|
||||||
// removing the selected filter
|
// removing the selected filter
|
||||||
updatedFilterData = [
|
updatedFilterData = [
|
||||||
...filterToFetchData.filter((name) => name !== props.name),
|
...filterToFetchData.filter((name) => name !== PanelName),
|
||||||
];
|
];
|
||||||
getprepdatedSelectedFilter.delete(props.name);
|
getprepdatedSelectedFilter.delete(PanelName);
|
||||||
getPreUserSelected.delete(props.name);
|
getPreUserSelected.delete(PanelName);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await getFilters({
|
const response = await getFilters({
|
||||||
@ -89,11 +94,11 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
// );
|
// );
|
||||||
|
|
||||||
getPreUserSelected.set(
|
getPreUserSelected.set(
|
||||||
props.name,
|
PanelName,
|
||||||
Object.keys(updatedFilter.get(props.name) || {}),
|
Object.keys(updatedFilter.get(PanelName) || {}),
|
||||||
);
|
);
|
||||||
|
|
||||||
updatedFilterData = [...filterToFetchData, props.name];
|
updatedFilterData = [...filterToFetchData, PanelName];
|
||||||
}
|
}
|
||||||
|
|
||||||
// now append the non prop.name trace filter enum over the list
|
// now append the non prop.name trace filter enum over the list
|
||||||
@ -104,12 +109,12 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
getPreUserSelected.forEach((value, key) => {
|
getPreUserSelected.forEach((value, key) => {
|
||||||
if (key !== props.name) {
|
if (key !== PanelName) {
|
||||||
getPreUserSelected.set(key, value);
|
getPreUserSelected.set(key, value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
filter.forEach((value, key) => {
|
filter.forEach((value, key) => {
|
||||||
if (key !== props.name) {
|
if (key !== PanelName) {
|
||||||
updatedFilter.set(key, value);
|
updatedFilter.set(key, value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -138,30 +143,30 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: response.error || 'Something went wrong',
|
message: response.error || defaultErrorMessage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: (error as AxiosError).toString() || 'Something went wrong',
|
message: (error as AxiosError).toString() || defaultErrorMessage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClearAllHandler = async () => {
|
const onClearAllHandler = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const updatedFilter = new Map(selectedFilter);
|
const updatedFilter = new Map(selectedFilter);
|
||||||
const preUserSelected = new Map(userSelectedFilter);
|
const preUserSelected = new Map(userSelectedFilter);
|
||||||
|
|
||||||
updatedFilter.delete(props.name);
|
updatedFilter.delete(PanelName);
|
||||||
preUserSelected.delete(props.name);
|
preUserSelected.delete(PanelName);
|
||||||
|
|
||||||
const postIsFilterExclude = new Map(isFilterExclude);
|
const postIsFilterExclude = new Map(isFilterExclude);
|
||||||
|
|
||||||
postIsFilterExclude.set(props.name, false);
|
postIsFilterExclude.set(PanelName, false);
|
||||||
|
|
||||||
const response = await getFilters({
|
const response = await getFilters({
|
||||||
end: String(global.maxTime),
|
end: String(global.maxTime),
|
||||||
@ -267,25 +272,25 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.name !== 'duration' && <Divider plain style={{ margin: 0 }} />}
|
{PanelName !== 'duration' && <Divider plain style={{ margin: 0 }} />}
|
||||||
|
|
||||||
<Card bordered={false}>
|
<Card bordered={false}>
|
||||||
<Container
|
<Container
|
||||||
disabled={filterLoading || isLoading}
|
disabled={filterLoading || isLoading}
|
||||||
aria-disabled={filterLoading || isLoading}
|
aria-disabled={filterLoading || isLoading}
|
||||||
aria-expanded={props.isOpen}
|
aria-expanded={IsPanelOpen}
|
||||||
>
|
>
|
||||||
<TextCotainer onClick={onExpandHandler}>
|
<TextCotainer onClick={onExpandHandler}>
|
||||||
<IconContainer>
|
<IconContainer>
|
||||||
{!props.isOpen ? <RightOutlined /> : <DownOutlined />}
|
{!IsPanelOpen ? <RightOutlined /> : <DownOutlined />}
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
|
|
||||||
<Text style={{ textTransform: 'capitalize' }} ellipsis>
|
<Text style={{ textTransform: 'capitalize' }} ellipsis>
|
||||||
{AllPanelHeading.find((e) => e.key === props.name)?.displayValue || ''}
|
{AllPanelHeading.find((e) => e.key === PanelName)?.displayValue || ''}
|
||||||
</Text>
|
</Text>
|
||||||
</TextCotainer>
|
</TextCotainer>
|
||||||
|
|
||||||
{props.name !== 'duration' && (
|
{PanelName !== 'duration' && (
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
{/* <ButtonComponent
|
{/* <ButtonComponent
|
||||||
aria-disabled={isLoading || filterLoading}
|
aria-disabled={isLoading || filterLoading}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
|
import { StyledCSS } from 'container/GantChart/Trace/styles';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -13,9 +14,9 @@ export const Container = styled.div<Props>`
|
|||||||
padding-left: 0.5rem;
|
padding-left: 0.5rem;
|
||||||
min-height: 5vh;
|
min-height: 5vh;
|
||||||
|
|
||||||
cursor: ${({ disabled }) => disabled && 'not-allowed'};
|
cursor: ${({ disabled }): string => (disabled ? 'not-allowed' : '')};
|
||||||
|
|
||||||
${({ disabled }) =>
|
${({ disabled }): StyledCSS =>
|
||||||
disabled &&
|
disabled &&
|
||||||
css`
|
css`
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
@ -9,14 +9,16 @@ import PanelHeading from './PanelHeading';
|
|||||||
function Panel(props: PanelProps): JSX.Element {
|
function Panel(props: PanelProps): JSX.Element {
|
||||||
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
||||||
|
|
||||||
|
const { name } = props;
|
||||||
|
|
||||||
const isDefaultOpen =
|
const isDefaultOpen =
|
||||||
traces.filterToFetchData.find((e) => e === props.name) !== undefined;
|
traces.filterToFetchData.find((e) => e === name) !== undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PanelHeading name={props.name} isOpen={isDefaultOpen} />
|
<PanelHeading name={name} isOpen={isDefaultOpen} />
|
||||||
|
|
||||||
{isDefaultOpen && <PanelBody type={props.name} />}
|
{isDefaultOpen && <PanelBody type={name} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button, Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const DurationContainer = styled.div`
|
export const DurationContainer = styled.div`
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { ChartData, ChartDataset, ChartDatasetProperties } from 'chart.js';
|
import { ChartData, ChartDatasetProperties } from 'chart.js';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { colors } from 'lib/getRandomColor';
|
import { colors } from 'lib/getRandomColor';
|
||||||
import { keys } from 'lodash-es';
|
import { keys } from 'lodash-es';
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
function transposeArray(array: number[][], arrayLength: number) {
|
function transposeArray(array: number[][], arrayLength: number): number[][] {
|
||||||
const newArray: number[][] = [];
|
const newArray: number[][] = [];
|
||||||
for (let i = 0; i < array.length; i++) {
|
for (let i = 0; i < array.length; i += 1) {
|
||||||
newArray.push([]);
|
newArray.push([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < array.length; i++) {
|
for (let i = 0; i < array.length; i += 1) {
|
||||||
for (let j = 0; j < arrayLength; j++) {
|
for (let j = 0; j < arrayLength; j += 1) {
|
||||||
newArray[j]?.push(array[i][j]);
|
newArray[j]?.push(array[i][j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,6 +69,8 @@ export const getChartDataforGroupBy = (
|
|||||||
const allGroupBy = Object.keys(items).map((e) => items[e].groupBy);
|
const allGroupBy = Object.keys(items).map((e) => items[e].groupBy);
|
||||||
|
|
||||||
keys(allGroupBy).forEach((e: string): void => {
|
keys(allGroupBy).forEach((e: string): void => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
const { length } = keys(allGroupBy[e]);
|
const { length } = keys(allGroupBy[e]);
|
||||||
|
|
||||||
if (length >= max) {
|
if (length >= max) {
|
||||||
|
@ -43,7 +43,7 @@ function TraceGraph(): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container ref={ref}>
|
<Container ref={ref as never}>
|
||||||
<Graph
|
<Graph
|
||||||
animate={false}
|
animate={false}
|
||||||
data={ChartData}
|
data={ChartData}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { StyledCSS } from 'container/GantChart/Trace/styles';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ export const Container = styled.div<Props>`
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
|
|
||||||
${({ center }) =>
|
${({ center }): StyledCSS =>
|
||||||
center &&
|
center &&
|
||||||
css`
|
css`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -12,7 +12,9 @@ function TagsKey(props: TagsKeysProps): JSX.Element {
|
|||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [selectedKey, setSelectedKey] = useState<string>(props.tag.Key[0] || '');
|
const { index, setLocalSelectedTags, tag } = props;
|
||||||
|
|
||||||
|
const [selectedKey, setSelectedKey] = useState<string>(tag.Key[0] || '');
|
||||||
|
|
||||||
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
||||||
|
|
||||||
@ -77,14 +79,14 @@ function TagsKey(props: TagsKeysProps): JSX.Element {
|
|||||||
if (options && options.find((option) => option.value === value)) {
|
if (options && options.find((option) => option.value === value)) {
|
||||||
setSelectedKey(value);
|
setSelectedKey(value);
|
||||||
|
|
||||||
props.setLocalSelectedTags((tags) => [
|
setLocalSelectedTags((tags) => [
|
||||||
...tags.slice(0, props.index),
|
...tags.slice(0, index),
|
||||||
{
|
{
|
||||||
Key: [value],
|
Key: [value],
|
||||||
Operator: props.tag.Operator,
|
Operator: tag.Operator,
|
||||||
Values: props.tag.Values,
|
Values: tag.Values,
|
||||||
},
|
},
|
||||||
...tags.slice(props.index + 1, tags.length),
|
...tags.slice(index + 1, tags.length),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
setSelectedKey('');
|
setSelectedKey('');
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import { SelectValue } from 'antd/lib/select';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
|
||||||
import { UpdateSelectedTags } from 'store/actions/trace/updateTagsSelected';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -22,7 +17,12 @@ const { Option } = Select;
|
|||||||
|
|
||||||
type Tags = FlatArray<TraceReducer['selectedTags'], 1>['Operator'];
|
type Tags = FlatArray<TraceReducer['selectedTags'], 1>['Operator'];
|
||||||
|
|
||||||
const AllMenu: AllMenu[] = [
|
interface AllMenuProps {
|
||||||
|
key: Tags | '';
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AllMenu: AllMenuProps[] = [
|
||||||
{
|
{
|
||||||
key: 'in',
|
key: 'in',
|
||||||
value: 'IN',
|
value: 'IN',
|
||||||
@ -33,41 +33,40 @@ const AllMenu: AllMenu[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
interface AllMenu {
|
|
||||||
key: Tags | '';
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SingleTags(props: AllTagsProps): JSX.Element {
|
function SingleTags(props: AllTagsProps): JSX.Element {
|
||||||
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
||||||
|
|
||||||
|
const { tag, onCloseHandler, setLocalSelectedTags, index } = props;
|
||||||
const {
|
const {
|
||||||
Key: selectedKey,
|
Key: selectedKey,
|
||||||
Operator: selectedOperator,
|
Operator: selectedOperator,
|
||||||
Values: selectedValues,
|
Values: selectedValues,
|
||||||
} = props.tag;
|
} = tag;
|
||||||
|
|
||||||
const onDeleteTagHandler = (index: number): void => {
|
const onDeleteTagHandler = (index: number): void => {
|
||||||
props.onCloseHandler(index);
|
onCloseHandler(index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChangeOperatorHandler = (key: SelectValue): void => {
|
const onChangeOperatorHandler = (key: unknown): void => {
|
||||||
props.setLocalSelectedTags([
|
if (typeof key === 'string') {
|
||||||
...traces.selectedTags.slice(0, props.index),
|
setLocalSelectedTags([
|
||||||
{
|
...traces.selectedTags.slice(0, index),
|
||||||
Key: selectedKey,
|
{
|
||||||
Values: selectedValues,
|
Key: selectedKey,
|
||||||
Operator: key as Tags,
|
Values: selectedValues,
|
||||||
},
|
Operator: key as Tags,
|
||||||
...traces.selectedTags.slice(props.index + 1, traces.selectedTags.length),
|
},
|
||||||
]);
|
...traces.selectedTags.slice(index + 1, traces.selectedTags.length),
|
||||||
|
]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<TagsKey
|
<TagsKey
|
||||||
index={props.index}
|
index={index}
|
||||||
tag={props.tag}
|
tag={tag}
|
||||||
setLocalSelectedTags={props.setLocalSelectedTags}
|
setLocalSelectedTags={setLocalSelectedTags}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
@ -84,40 +83,27 @@ function SingleTags(props: AllTagsProps): JSX.Element {
|
|||||||
<ValueSelect
|
<ValueSelect
|
||||||
value={selectedValues}
|
value={selectedValues}
|
||||||
onChange={(value): void => {
|
onChange={(value): void => {
|
||||||
props.setLocalSelectedTags((tags) => [
|
setLocalSelectedTags((tags) => [
|
||||||
...tags.slice(0, props.index),
|
...tags.slice(0, index),
|
||||||
{
|
{
|
||||||
Key: selectedKey,
|
Key: selectedKey,
|
||||||
Operator: selectedOperator,
|
Operator: selectedOperator,
|
||||||
Values: value as string[],
|
Values: value as string[],
|
||||||
},
|
},
|
||||||
...tags.slice(props.index + 1, tags.length),
|
...tags.slice(index + 1, tags.length),
|
||||||
]);
|
]);
|
||||||
}}
|
}}
|
||||||
mode="tags"
|
mode="tags"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IconContainer
|
<IconContainer role="button" onClick={(): void => onDeleteTagHandler(index)}>
|
||||||
role="button"
|
|
||||||
onClick={(): void => onDeleteTagHandler(props.index)}
|
|
||||||
>
|
|
||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface AllTagsProps {
|
||||||
updateSelectedTags: (props: TraceReducer['selectedTags']) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
|
||||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
|
||||||
): DispatchProps => ({
|
|
||||||
updateSelectedTags: bindActionCreators(UpdateSelectedTags, dispatch),
|
|
||||||
});
|
|
||||||
|
|
||||||
interface AllTagsProps extends DispatchProps {
|
|
||||||
onCloseHandler: (index: number) => void;
|
onCloseHandler: (index: number) => void;
|
||||||
index: number;
|
index: number;
|
||||||
tag: FlatArray<TraceReducer['selectedTags'], 1>;
|
tag: FlatArray<TraceReducer['selectedTags'], 1>;
|
||||||
@ -126,4 +112,4 @@ interface AllTagsProps extends DispatchProps {
|
|||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(SingleTags);
|
export default SingleTags;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { CaretRightFilled, PlusOutlined } from '@ant-design/icons';
|
import { CaretRightFilled, PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, Space, Typography } from 'antd';
|
import { Button, Space, Typography } from 'antd';
|
||||||
import { isEqual } from 'lodash-es';
|
import React, { useState } from 'react';
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
@ -48,12 +47,6 @@ function AllTags({
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isEqual(traces.selectedTags, localSelectedTags)) {
|
|
||||||
setLocalSelectedTags(traces.selectedTags);
|
|
||||||
}
|
|
||||||
}, [traces.selectedTags, localSelectedTags]);
|
|
||||||
|
|
||||||
const onCloseHandler = (index: number): void => {
|
const onCloseHandler = (index: number): void => {
|
||||||
setLocalSelectedTags([
|
setLocalSelectedTags([
|
||||||
...localSelectedTags.slice(0, index),
|
...localSelectedTags.slice(0, index),
|
||||||
|
@ -20,7 +20,9 @@ export const parseQueryToTags = (query: string): PayloadProps<Tags> => {
|
|||||||
isError = true;
|
isError = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const splitBy = isNotInPresent ? 'not in' : isInPresent ? 'in' : '';
|
const isPresentSplit = isInPresent ? 'in' : '';
|
||||||
|
|
||||||
|
const splitBy = isNotInPresent ? 'not in' : isPresentSplit;
|
||||||
|
|
||||||
if (splitBy.length === 0) {
|
if (splitBy.length === 0) {
|
||||||
isError = true;
|
isError = true;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { SelectProps, Space } from 'antd';
|
import { Space } from 'antd';
|
||||||
import { SelectValue } from 'antd/lib/select';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
@ -23,42 +22,43 @@ function TraceGraphFilter(): JSX.Element {
|
|||||||
>((state) => state.traces);
|
>((state) => state.traces);
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
const onClickSelectedFunctionHandler: SelectProps<SelectValue>['onChange'] = (
|
const onClickSelectedFunctionHandler = (ev: unknown): void => {
|
||||||
ev,
|
if (typeof ev === 'string') {
|
||||||
) => {
|
const selected = functions.find((e) => e.key === ev);
|
||||||
const selected = functions.find((e) => e.key === ev);
|
if (selected) {
|
||||||
if (selected) {
|
dispatch({
|
||||||
dispatch({
|
type: UPDATE_SELECTED_FUNCTION,
|
||||||
type: UPDATE_SELECTED_FUNCTION,
|
payload: {
|
||||||
payload: {
|
selectedFunction: selected.key,
|
||||||
selectedFunction: selected.key,
|
yAxisUnit: selected.yAxisUnit,
|
||||||
yAxisUnit: selected.yAxisUnit,
|
},
|
||||||
},
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickSelectedGroupByHandler: SelectProps<SelectValue>['onChange'] = (
|
const onClickSelectedGroupByHandler = (ev: unknown): void => {
|
||||||
ev,
|
if (typeof ev === 'string') {
|
||||||
) => {
|
const selected = groupBy.find((e) => e.key === ev);
|
||||||
const selected = groupBy.find((e) => e.key === ev);
|
if (selected) {
|
||||||
if (selected) {
|
dispatch({
|
||||||
dispatch({
|
type: UPDATE_SELECTED_GROUP_BY,
|
||||||
type: UPDATE_SELECTED_GROUP_BY,
|
payload: {
|
||||||
payload: {
|
selectedGroupBy: selected.key,
|
||||||
selectedGroupBy: selected.key,
|
},
|
||||||
},
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
<label>Function</label>
|
<label htmlFor="selectedFunction">Function</label>
|
||||||
|
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
dropdownMatchSelectWidth
|
dropdownMatchSelectWidth
|
||||||
data-testid="selectedFunction"
|
data-testid="selectedFunction"
|
||||||
|
id="selectedFunction"
|
||||||
value={functions.find((e) => selectedFunction === e.key)?.displayValue}
|
value={functions.find((e) => selectedFunction === e.key)?.displayValue}
|
||||||
onChange={onClickSelectedFunctionHandler}
|
onChange={onClickSelectedFunctionHandler}
|
||||||
>
|
>
|
||||||
@ -69,9 +69,10 @@ function TraceGraphFilter(): JSX.Element {
|
|||||||
))}
|
))}
|
||||||
</SelectComponent>
|
</SelectComponent>
|
||||||
|
|
||||||
<label>Group By</label>
|
<label htmlFor="selectedGroupBy">Group By</label>
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
dropdownMatchSelectWidth
|
dropdownMatchSelectWidth
|
||||||
|
id="selectedGroupBy"
|
||||||
data-testid="selectedGroupBy"
|
data-testid="selectedGroupBy"
|
||||||
value={groupBy.find((e) => selectedGroupBy === e.key)?.displayValue}
|
value={groupBy.find((e) => selectedGroupBy === e.key)?.displayValue}
|
||||||
onChange={onClickSelectedGroupByHandler}
|
onChange={onClickSelectedGroupByHandler}
|
||||||
|
@ -39,6 +39,32 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
return `${ROUTES.TRACE}/${record.traceID}?spanId=${record.spanID}`;
|
return `${ROUTES.TRACE}/${record.traceID}?spanId=${record.spanID}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getValue = (value: string, record: TableType): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<Link to={getLink(record)}>
|
||||||
|
<Typography>{value}</Typography>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getHttpMethodOrStatus = (
|
||||||
|
value: TableType['httpMethod'],
|
||||||
|
record: TableType,
|
||||||
|
): JSX.Element => {
|
||||||
|
if (value.length === 0) {
|
||||||
|
return (
|
||||||
|
<Link to={getLink(record)}>
|
||||||
|
<Typography>-</Typography>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Link to={getLink(record)}>
|
||||||
|
<Tag color="magenta">{value}</Tag>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const columns: ColumnsType<TableType> = [
|
const columns: ColumnsType<TableType> = [
|
||||||
{
|
{
|
||||||
title: 'Date',
|
title: 'Date',
|
||||||
@ -58,25 +84,13 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
title: 'Service',
|
title: 'Service',
|
||||||
dataIndex: 'serviceName',
|
dataIndex: 'serviceName',
|
||||||
key: 'serviceName',
|
key: 'serviceName',
|
||||||
render: (value, record): JSX.Element => {
|
render: getValue,
|
||||||
return (
|
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Typography>{value}</Typography>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Operation',
|
title: 'Operation',
|
||||||
dataIndex: 'operation',
|
dataIndex: 'operation',
|
||||||
key: 'operation',
|
key: 'operation',
|
||||||
render: (value, record): JSX.Element => {
|
render: getValue,
|
||||||
return (
|
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Typography>{value}</Typography>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Duration',
|
title: 'Duration',
|
||||||
@ -96,39 +110,13 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
title: 'Method',
|
title: 'Method',
|
||||||
dataIndex: 'httpMethod',
|
dataIndex: 'httpMethod',
|
||||||
key: 'httpMethod',
|
key: 'httpMethod',
|
||||||
render: (value: TableType['httpMethod'], record): JSX.Element => {
|
render: getHttpMethodOrStatus,
|
||||||
if (value.length === 0) {
|
|
||||||
return (
|
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Typography>-</Typography>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Tag color="magenta">{value}</Tag>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Status Code',
|
title: 'Status Code',
|
||||||
dataIndex: 'httpCode',
|
dataIndex: 'httpCode',
|
||||||
key: 'httpCode',
|
key: 'httpCode',
|
||||||
render: (value: TableType['httpMethod'], record): JSX.Element => {
|
render: getHttpMethodOrStatus,
|
||||||
if (value.length === 0) {
|
|
||||||
return (
|
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Typography>-</Typography>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Link to={getLink(record)}>
|
|
||||||
<Tag color="magenta">{value}</Tag>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -137,18 +125,19 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
|||||||
_,
|
_,
|
||||||
sort,
|
sort,
|
||||||
) => {
|
) => {
|
||||||
const { order = 'ascend' } = sort;
|
if (!Array.isArray(sort)) {
|
||||||
|
const { order = 'ascend' } = sort;
|
||||||
if (props.current && props.pageSize) {
|
if (props.current && props.pageSize) {
|
||||||
getSpansAggregate({
|
getSpansAggregate({
|
||||||
maxTime: globalTime.maxTime,
|
maxTime: globalTime.maxTime,
|
||||||
minTime: globalTime.minTime,
|
minTime: globalTime.minTime,
|
||||||
selectedFilter,
|
selectedFilter,
|
||||||
current: props.current,
|
current: props.current,
|
||||||
pageSize: props.pageSize,
|
pageSize: props.pageSize,
|
||||||
selectedTags,
|
selectedTags,
|
||||||
order: order === 'ascend' ? 'ascending' : 'descending',
|
order: order === 'ascend' ? 'ascending' : 'descending',
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@ function ErrorTag({ event }: ErrorTagProps): JSX.Element {
|
|||||||
key={`${name}${JSON.stringify(attributeMap)}`}
|
key={`${name}${JSON.stringify(attributeMap)}`}
|
||||||
defaultActiveKey={[name || attributeMap.event]}
|
defaultActiveKey={[name || attributeMap.event]}
|
||||||
expandIconPosition="right"
|
expandIconPosition="right"
|
||||||
key={name}
|
|
||||||
>
|
>
|
||||||
<Panel
|
<Panel
|
||||||
header={name || attributeMap?.event}
|
header={name || attributeMap?.event}
|
||||||
|
@ -20,7 +20,7 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
|
|||||||
const { tree } = props;
|
const { tree } = props;
|
||||||
const { isDarkMode } = useThemeMode();
|
const { isDarkMode } = useThemeMode();
|
||||||
if (!tree) {
|
if (!tree) {
|
||||||
return <></>;
|
return <div />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name, tags, serviceName } = tree;
|
const { name, tags, serviceName } = tree;
|
||||||
@ -79,4 +79,8 @@ interface SelectedSpanDetailsProps {
|
|||||||
tree?: ITraceTree;
|
tree?: ITraceTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectedSpanDetails.defaultProps = {
|
||||||
|
tree: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default SelectedSpanDetails;
|
export default SelectedSpanDetails;
|
||||||
|
@ -29,7 +29,7 @@ interface CustomSubTextProps {
|
|||||||
|
|
||||||
export const CustomSubText = styled(Paragraph)<CustomSubTextProps>`
|
export const CustomSubText = styled(Paragraph)<CustomSubTextProps>`
|
||||||
&&& {
|
&&& {
|
||||||
background: ${({ isDarkMode }) => (isDarkMode ? '#444' : '#ddd')};
|
background: ${({ isDarkMode }): string => (isDarkMode ? '#444' : '#ddd')};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
StyledTypography,
|
StyledTypography,
|
||||||
} from 'components/Styled';
|
} from 'components/Styled';
|
||||||
import * as StyledStyles from 'components/Styled/styles';
|
import * as StyledStyles from 'components/Styled/styles';
|
||||||
import GanttChart from 'container/GantChart';
|
import GanttChart, { ITraceMetaData } from 'container/GantChart';
|
||||||
import { getNodeById } from 'container/GantChart/utils';
|
import { getNodeById } from 'container/GantChart/utils';
|
||||||
import Timeline from 'container/Timeline';
|
import Timeline from 'container/Timeline';
|
||||||
import TraceFlameGraph from 'container/TraceFlameGraph';
|
import TraceFlameGraph from 'container/TraceFlameGraph';
|
||||||
@ -49,10 +49,13 @@ function TraceDetail({ response }: TraceDetailProps): JSX.Element {
|
|||||||
|
|
||||||
const { treeData: tree, ...traceMetaData } = useMemo(() => {
|
const { treeData: tree, ...traceMetaData } = useMemo(() => {
|
||||||
const tree = getSortedData(treeData);
|
const tree = getSortedData(treeData);
|
||||||
return getSpanTreeMetadata(tree, spanServiceColors);
|
// Note: Handle undefined
|
||||||
|
/*eslint-disable */
|
||||||
|
return getSpanTreeMetadata(tree as ITraceTree, spanServiceColors);
|
||||||
|
/* eslint-enable */
|
||||||
}, [treeData, spanServiceColors]);
|
}, [treeData, spanServiceColors]);
|
||||||
|
|
||||||
const [globalTraceMetadata] = useState<Record<string, number>>({
|
const [globalTraceMetadata] = useState<ITraceMetaData>({
|
||||||
...traceMetaData,
|
...traceMetaData,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -73,7 +76,7 @@ function TraceDetail({ response }: TraceDetailProps): JSX.Element {
|
|||||||
// setSearchSpanString(value);
|
// setSearchSpanString(value);
|
||||||
// setTreeData(spanToTreeUtil(response[0].events));
|
// setTreeData(spanToTreeUtil(response[0].events));
|
||||||
// };
|
// };
|
||||||
const onFocusSelectedSpanHandler = () => {
|
const onFocusSelectedSpanHandler = (): void => {
|
||||||
const treeNode = getNodeById(activeSelectedId, tree);
|
const treeNode = getNodeById(activeSelectedId, tree);
|
||||||
if (treeNode) {
|
if (treeNode) {
|
||||||
setTreeData(treeNode);
|
setTreeData(treeNode);
|
||||||
@ -126,7 +129,6 @@ function TraceDetail({ response }: TraceDetailProps): JSX.Element {
|
|||||||
<Timeline
|
<Timeline
|
||||||
globalTraceMetadata={globalTraceMetadata}
|
globalTraceMetadata={globalTraceMetadata}
|
||||||
traceMetaData={traceMetaData}
|
traceMetaData={traceMetaData}
|
||||||
intervalUnit={intervalUnit}
|
|
||||||
setIntervalUnit={setIntervalUnit}
|
setIntervalUnit={setIntervalUnit}
|
||||||
/>
|
/>
|
||||||
</StyledCol>
|
</StyledCol>
|
||||||
|
@ -45,11 +45,13 @@ export const getSortedData = (treeData: ITraceTree): undefined | ITraceTree => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// need this rule to disable
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
treeNode.children = sortBy(treeNode.children, (e) => e.startTime);
|
treeNode.children = sortBy(treeNode.children, (e) => e.startTime);
|
||||||
|
|
||||||
for (const childNode of treeNode.children) {
|
treeNode.children.forEach((childNode) => {
|
||||||
traverse(childNode, level + 1);
|
traverse(childNode, level + 1);
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
traverse(treeData, 1);
|
traverse(treeData, 1);
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user