mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-15 22:45:57 +08:00
commit
865409d725
@ -133,7 +133,7 @@ services:
|
|||||||
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.23.1
|
image: signoz/alertmanager:0.23.2
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
command:
|
command:
|
||||||
@ -146,7 +146,7 @@ services:
|
|||||||
condition: on-failure
|
condition: on-failure
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.25.5
|
image: signoz/query-service:0.26.0
|
||||||
command: [ "-config=/root/config/prometheus.yml" ]
|
command: [ "-config=/root/config/prometheus.yml" ]
|
||||||
# ports:
|
# ports:
|
||||||
# - "6060:6060" # pprof port
|
# - "6060:6060" # pprof port
|
||||||
@ -182,7 +182,7 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.25.5
|
image: signoz/frontend:0.26.0
|
||||||
deploy:
|
deploy:
|
||||||
restart_policy:
|
restart_policy:
|
||||||
condition: on-failure
|
condition: on-failure
|
||||||
|
@ -34,7 +34,7 @@ services:
|
|||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
container_name: signoz-alertmanager
|
container_name: signoz-alertmanager
|
||||||
image: signoz/alertmanager:0.23.1
|
image: signoz/alertmanager:0.23.2
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -148,7 +148,7 @@ services:
|
|||||||
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.1}
|
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.2}
|
||||||
container_name: signoz-alertmanager
|
container_name: signoz-alertmanager
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
@ -163,7 +163,7 @@ services:
|
|||||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
# 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:${DOCKER_TAG:-0.25.5}
|
image: signoz/query-service:${DOCKER_TAG:-0.26.0}
|
||||||
container_name: signoz-query-service
|
container_name: signoz-query-service
|
||||||
command: [ "-config=/root/config/prometheus.yml" ]
|
command: [ "-config=/root/config/prometheus.yml" ]
|
||||||
# ports:
|
# ports:
|
||||||
@ -198,7 +198,7 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:${DOCKER_TAG:-0.25.5}
|
image: signoz/frontend:${DOCKER_TAG:-0.26.0}
|
||||||
container_name: signoz-frontend
|
container_name: signoz-frontend
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
|
@ -39,6 +39,9 @@ COPY --from=builder /go/src/github.com/signoz/signoz/ee/query-service/bin/query-
|
|||||||
# copy prometheus YAML config
|
# copy prometheus YAML config
|
||||||
COPY pkg/query-service/config/prometheus.yml /root/config/prometheus.yml
|
COPY pkg/query-service/config/prometheus.yml /root/config/prometheus.yml
|
||||||
|
|
||||||
|
# Make query-service executable for non-root users
|
||||||
|
RUN chmod 755 /root /root/query-service
|
||||||
|
|
||||||
# run the binary
|
# run the binary
|
||||||
ENTRYPOINT ["./query-service"]
|
ENTRYPOINT ["./query-service"]
|
||||||
|
|
||||||
|
@ -60,6 +60,34 @@ var BasicPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: 5,
|
UsageLimit: 5,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelSlack,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelWebhook,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelPagerduty,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelMsTeams,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.UseSpanMetrics,
|
Name: basemodel.UseSpanMetrics,
|
||||||
Active: false,
|
Active: false,
|
||||||
@ -112,6 +140,34 @@ var ProPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelSlack,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelWebhook,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelPagerduty,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelMsTeams,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.UseSpanMetrics,
|
Name: basemodel.UseSpanMetrics,
|
||||||
Active: false,
|
Active: false,
|
||||||
@ -164,6 +220,34 @@ var EnterprisePlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelSlack,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelWebhook,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelPagerduty,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: basemodel.AlertChannelMsTeams,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.UseSpanMetrics,
|
Name: basemodel.UseSpanMetrics,
|
||||||
Active: false,
|
Active: false,
|
||||||
|
@ -24,7 +24,7 @@ COPY . .
|
|||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
|
|
||||||
FROM nginx:1.18-alpine
|
FROM nginx:1.24.0-alpine
|
||||||
|
|
||||||
COPY conf/default.conf /etc/nginx/conf.d/default.conf
|
COPY conf/default.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
@ -1,112 +1,113 @@
|
|||||||
{
|
{
|
||||||
"target_missing": "Please enter a threshold to proceed",
|
"target_missing": "Please enter a threshold to proceed",
|
||||||
"rule_test_fired": "Test notification sent successfully",
|
"rule_test_fired": "Test notification sent successfully",
|
||||||
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
|
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
|
||||||
"button_testrule": "Test Notification",
|
"button_testrule": "Test Notification",
|
||||||
"label_channel_select": "Notification Channels",
|
"label_channel_select": "Notification Channels",
|
||||||
"placeholder_channel_select": "select one or more channels",
|
"placeholder_channel_select": "select one or more channels",
|
||||||
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
|
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
|
||||||
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
|
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
|
||||||
"preview_chart_threshold_label": "Threshold",
|
"preview_chart_threshold_label": "Threshold",
|
||||||
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
|
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
|
||||||
"button_yes": "Yes",
|
"button_yes": "Yes",
|
||||||
"button_no": "No",
|
"button_no": "No",
|
||||||
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
|
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
|
||||||
"remove_label_success": "Labels cleared",
|
"remove_label_success": "Labels cleared",
|
||||||
"alert_form_step1": "Step 1 - Define the metric",
|
"alert_form_step1": "Step 1 - Define the metric",
|
||||||
"alert_form_step2": "Step 2 - Define Alert Conditions",
|
"alert_form_step2": "Step 2 - Define Alert Conditions",
|
||||||
"alert_form_step3": "Step 3 - Alert Configuration",
|
"alert_form_step3": "Step 3 - Alert Configuration",
|
||||||
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
|
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
|
||||||
"confirm_save_title": "Save Changes",
|
"confirm_save_title": "Save Changes",
|
||||||
"confirm_save_content_part1": "Your alert built with",
|
"confirm_save_content_part1": "Your alert built with",
|
||||||
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
|
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
|
||||||
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
|
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
|
||||||
"rule_created": "Rule created successfully",
|
"rule_created": "Rule created successfully",
|
||||||
"rule_edited": "Rule edited successfully",
|
"rule_edited": "Rule edited successfully",
|
||||||
"expression_missing": "expression is missing in {{where}}",
|
"expression_missing": "expression is missing in {{where}}",
|
||||||
"metricname_missing": "metric name is missing in {{where}}",
|
"metricname_missing": "metric name is missing in {{where}}",
|
||||||
"condition_required": "at least one metric condition is required",
|
"condition_required": "at least one metric condition is required",
|
||||||
"alertname_required": "alert name is required",
|
"alertname_required": "alert name is required",
|
||||||
"promql_required": "promql expression is required when query format is set to PromQL",
|
"promql_required": "promql expression is required when query format is set to PromQL",
|
||||||
"chquery_required": "query is required when query format is set to ClickHouse",
|
"chquery_required": "query is required when query format is set to ClickHouse",
|
||||||
"button_savechanges": "Save Rule",
|
"button_savechanges": "Save Rule",
|
||||||
"button_createrule": "Create Rule",
|
"button_createrule": "Create Rule",
|
||||||
"button_returntorules": "Return to rules",
|
"button_returntorules": "Return to rules",
|
||||||
"button_cancelchanges": "Cancel",
|
"button_cancelchanges": "Cancel",
|
||||||
"button_discard": "Discard",
|
"button_discard": "Discard",
|
||||||
"text_condition1": "Send a notification when the metric is",
|
"text_condition1": "Send a notification when the metric is",
|
||||||
"text_condition2": "the threshold",
|
"text_condition2": "the threshold",
|
||||||
"text_condition3": "during the last",
|
"text_condition3": "during the last",
|
||||||
"option_5min": "5 mins",
|
"option_5min": "5 mins",
|
||||||
"option_10min": "10 mins",
|
"option_10min": "10 mins",
|
||||||
"option_15min": "15 mins",
|
"option_15min": "15 mins",
|
||||||
"option_60min": "60 mins",
|
"option_60min": "60 mins",
|
||||||
"option_4hours": "4 hours",
|
"option_4hours": "4 hours",
|
||||||
"option_24hours": "24 hours",
|
"option_24hours": "24 hours",
|
||||||
"field_threshold": "Alert Threshold",
|
"field_threshold": "Alert Threshold",
|
||||||
"option_allthetimes": "all the times",
|
"option_allthetimes": "all the times",
|
||||||
"option_atleastonce": "at least once",
|
"option_atleastonce": "at least once",
|
||||||
"option_onaverage": "on average",
|
"option_onaverage": "on average",
|
||||||
"option_intotal": "in total",
|
"option_intotal": "in total",
|
||||||
"option_above": "above",
|
"option_above": "above",
|
||||||
"option_below": "below",
|
"option_below": "below",
|
||||||
"option_equal": "is equal to",
|
"option_equal": "is equal to",
|
||||||
"option_notequal": "not equal to",
|
"option_notequal": "not equal to",
|
||||||
"button_query": "Query",
|
"button_query": "Query",
|
||||||
"button_formula": "Formula",
|
"button_formula": "Formula",
|
||||||
"tab_qb": "Query Builder",
|
"tab_qb": "Query Builder",
|
||||||
"tab_promql": "PromQL",
|
"tab_promql": "PromQL",
|
||||||
"tab_chquery": "ClickHouse Query",
|
"tab_chquery": "ClickHouse Query",
|
||||||
"title_confirm": "Confirm",
|
"title_confirm": "Confirm",
|
||||||
"button_ok": "Yes",
|
"button_ok": "Yes",
|
||||||
"button_cancel": "No",
|
"button_cancel": "No",
|
||||||
"field_promql_expr": "PromQL Expression",
|
"field_promql_expr": "PromQL Expression",
|
||||||
"field_alert_name": "Alert Name",
|
"field_alert_name": "Alert Name",
|
||||||
"field_alert_desc": "Alert Description",
|
"field_alert_desc": "Alert Description",
|
||||||
"field_labels": "Labels",
|
"field_labels": "Labels",
|
||||||
"field_severity": "Severity",
|
"field_severity": "Severity",
|
||||||
"option_critical": "Critical",
|
"option_critical": "Critical",
|
||||||
"option_error": "Error",
|
"option_error": "Error",
|
||||||
"option_warning": "Warning",
|
"option_warning": "Warning",
|
||||||
"option_info": "Info",
|
"option_info": "Info",
|
||||||
"user_guide_headline": "Steps to create an Alert",
|
"user_guide_headline": "Steps to create an Alert",
|
||||||
"user_guide_qb_step1": "Step 1 - Define the metric",
|
"user_guide_qb_step1": "Step 1 - Define the metric",
|
||||||
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
|
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
|
||||||
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
|
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
|
||||||
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
|
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
|
||||||
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
|
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
|
||||||
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
|
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
|
||||||
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
|
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
|
||||||
"user_guide_qb_step2b": "Enter the Alert threshold",
|
"user_guide_qb_step2b": "Enter the Alert threshold",
|
||||||
"user_guide_qb_step3": "Step 3 -Alert Configuration",
|
"user_guide_qb_step3": "Step 3 -Alert Configuration",
|
||||||
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
|
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
|
||||||
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
|
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
|
||||||
"user_guide_pql_step1": "Step 1 - Define the metric",
|
"user_guide_pql_step1": "Step 1 - Define the metric",
|
||||||
"user_guide_pql_step1a": "Write a PromQL query for the metric",
|
"user_guide_pql_step1a": "Write a PromQL query for the metric",
|
||||||
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
|
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
|
||||||
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
|
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
|
||||||
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||||
"user_guide_pql_step2b": "Enter the Alert threshold",
|
"user_guide_pql_step2b": "Enter the Alert threshold",
|
||||||
"user_guide_pql_step3": "Step 3 -Alert Configuration",
|
"user_guide_pql_step3": "Step 3 -Alert Configuration",
|
||||||
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
|
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
|
||||||
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
|
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
|
||||||
"user_guide_ch_step1": "Step 1 - Define the metric",
|
"user_guide_ch_step1": "Step 1 - Define the metric",
|
||||||
"user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial</0> to learn about query format and supported vars.",
|
"user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial</0> to learn about query format and supported vars.",
|
||||||
"user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart",
|
"user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart",
|
||||||
"user_guide_ch_step2": "Step 2 - Define Alert Conditions",
|
"user_guide_ch_step2": "Step 2 - Define Alert Conditions",
|
||||||
"user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
"user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||||
"user_guide_ch_step2b": "Enter the Alert threshold",
|
"user_guide_ch_step2b": "Enter the Alert threshold",
|
||||||
"user_guide_ch_step3": "Step 3 -Alert Configuration",
|
"user_guide_ch_step3": "Step 3 -Alert Configuration",
|
||||||
"user_guide_ch_step3a": "Set alert severity, name and descriptions",
|
"user_guide_ch_step3a": "Set alert severity, name and descriptions",
|
||||||
"user_guide_ch_step3b": "Add tags to the alert in the Label field if needed",
|
"user_guide_ch_step3b": "Add tags to the alert in the Label field if needed",
|
||||||
"user_tooltip_more_help": "More details on how to create alerts",
|
"user_tooltip_more_help": "More details on how to create alerts",
|
||||||
"choose_alert_type": "Choose a type for the alert:",
|
"choose_alert_type": "Choose a type for the alert:",
|
||||||
"metric_based_alert": "Metric based Alert",
|
"metric_based_alert": "Metric based Alert",
|
||||||
"metric_based_alert_desc": "Send a notification when a condition occurs in the metric data",
|
"metric_based_alert_desc": "Send a notification when a condition occurs in the metric data",
|
||||||
"log_based_alert": "Log-based Alert",
|
"log_based_alert": "Log-based Alert",
|
||||||
"log_based_alert_desc": "Send a notification when a condition occurs in the logs data.",
|
"log_based_alert_desc": "Send a notification when a condition occurs in the logs data.",
|
||||||
"traces_based_alert": "Trace-based Alert",
|
"traces_based_alert": "Trace-based Alert",
|
||||||
"traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.",
|
"traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.",
|
||||||
"exceptions_based_alert": "Exceptions-based Alert",
|
"exceptions_based_alert": "Exceptions-based Alert",
|
||||||
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data."
|
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.",
|
||||||
|
"field_unit": "Threshold unit"
|
||||||
}
|
}
|
@ -1,112 +1,113 @@
|
|||||||
{
|
{
|
||||||
"target_missing": "Please enter a threshold to proceed",
|
"target_missing": "Please enter a threshold to proceed",
|
||||||
"rule_test_fired": "Test notification sent successfully",
|
"rule_test_fired": "Test notification sent successfully",
|
||||||
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
|
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
|
||||||
"button_testrule": "Test Notification",
|
"button_testrule": "Test Notification",
|
||||||
"label_channel_select": "Notification Channels",
|
"label_channel_select": "Notification Channels",
|
||||||
"placeholder_channel_select": "select one or more channels",
|
"placeholder_channel_select": "select one or more channels",
|
||||||
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
|
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
|
||||||
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
|
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
|
||||||
"preview_chart_threshold_label": "Threshold",
|
"preview_chart_threshold_label": "Threshold",
|
||||||
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
|
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
|
||||||
"button_yes": "Yes",
|
"button_yes": "Yes",
|
||||||
"button_no": "No",
|
"button_no": "No",
|
||||||
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
|
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
|
||||||
"remove_label_success": "Labels cleared",
|
"remove_label_success": "Labels cleared",
|
||||||
"alert_form_step1": "Step 1 - Define the metric",
|
"alert_form_step1": "Step 1 - Define the metric",
|
||||||
"alert_form_step2": "Step 2 - Define Alert Conditions",
|
"alert_form_step2": "Step 2 - Define Alert Conditions",
|
||||||
"alert_form_step3": "Step 3 - Alert Configuration",
|
"alert_form_step3": "Step 3 - Alert Configuration",
|
||||||
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
|
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
|
||||||
"confirm_save_title": "Save Changes",
|
"confirm_save_title": "Save Changes",
|
||||||
"confirm_save_content_part1": "Your alert built with",
|
"confirm_save_content_part1": "Your alert built with",
|
||||||
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
|
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
|
||||||
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
|
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
|
||||||
"rule_created": "Rule created successfully",
|
"rule_created": "Rule created successfully",
|
||||||
"rule_edited": "Rule edited successfully",
|
"rule_edited": "Rule edited successfully",
|
||||||
"expression_missing": "expression is missing in {{where}}",
|
"expression_missing": "expression is missing in {{where}}",
|
||||||
"metricname_missing": "metric name is missing in {{where}}",
|
"metricname_missing": "metric name is missing in {{where}}",
|
||||||
"condition_required": "at least one metric condition is required",
|
"condition_required": "at least one metric condition is required",
|
||||||
"alertname_required": "alert name is required",
|
"alertname_required": "alert name is required",
|
||||||
"promql_required": "promql expression is required when query format is set to PromQL",
|
"promql_required": "promql expression is required when query format is set to PromQL",
|
||||||
"chquery_required": "query is required when query format is set to ClickHouse",
|
"chquery_required": "query is required when query format is set to ClickHouse",
|
||||||
"button_savechanges": "Save Rule",
|
"button_savechanges": "Save Rule",
|
||||||
"button_createrule": "Create Rule",
|
"button_createrule": "Create Rule",
|
||||||
"button_returntorules": "Return to rules",
|
"button_returntorules": "Return to rules",
|
||||||
"button_cancelchanges": "Cancel",
|
"button_cancelchanges": "Cancel",
|
||||||
"button_discard": "Discard",
|
"button_discard": "Discard",
|
||||||
"text_condition1": "Send a notification when the metric is",
|
"text_condition1": "Send a notification when the metric is",
|
||||||
"text_condition2": "the threshold",
|
"text_condition2": "the threshold",
|
||||||
"text_condition3": "during the last",
|
"text_condition3": "during the last",
|
||||||
"option_5min": "5 mins",
|
"option_5min": "5 mins",
|
||||||
"option_10min": "10 mins",
|
"option_10min": "10 mins",
|
||||||
"option_15min": "15 mins",
|
"option_15min": "15 mins",
|
||||||
"option_60min": "60 mins",
|
"option_60min": "60 mins",
|
||||||
"option_4hours": "4 hours",
|
"option_4hours": "4 hours",
|
||||||
"option_24hours": "24 hours",
|
"option_24hours": "24 hours",
|
||||||
"field_threshold": "Alert Threshold",
|
"field_threshold": "Alert Threshold",
|
||||||
"option_allthetimes": "all the times",
|
"option_allthetimes": "all the times",
|
||||||
"option_atleastonce": "at least once",
|
"option_atleastonce": "at least once",
|
||||||
"option_onaverage": "on average",
|
"option_onaverage": "on average",
|
||||||
"option_intotal": "in total",
|
"option_intotal": "in total",
|
||||||
"option_above": "above",
|
"option_above": "above",
|
||||||
"option_below": "below",
|
"option_below": "below",
|
||||||
"option_equal": "is equal to",
|
"option_equal": "is equal to",
|
||||||
"option_notequal": "not equal to",
|
"option_notequal": "not equal to",
|
||||||
"button_query": "Query",
|
"button_query": "Query",
|
||||||
"button_formula": "Formula",
|
"button_formula": "Formula",
|
||||||
"tab_qb": "Query Builder",
|
"tab_qb": "Query Builder",
|
||||||
"tab_promql": "PromQL",
|
"tab_promql": "PromQL",
|
||||||
"tab_chquery": "ClickHouse Query",
|
"tab_chquery": "ClickHouse Query",
|
||||||
"title_confirm": "Confirm",
|
"title_confirm": "Confirm",
|
||||||
"button_ok": "Yes",
|
"button_ok": "Yes",
|
||||||
"button_cancel": "No",
|
"button_cancel": "No",
|
||||||
"field_promql_expr": "PromQL Expression",
|
"field_promql_expr": "PromQL Expression",
|
||||||
"field_alert_name": "Alert Name",
|
"field_alert_name": "Alert Name",
|
||||||
"field_alert_desc": "Alert Description",
|
"field_alert_desc": "Alert Description",
|
||||||
"field_labels": "Labels",
|
"field_labels": "Labels",
|
||||||
"field_severity": "Severity",
|
"field_severity": "Severity",
|
||||||
"option_critical": "Critical",
|
"option_critical": "Critical",
|
||||||
"option_error": "Error",
|
"option_error": "Error",
|
||||||
"option_warning": "Warning",
|
"option_warning": "Warning",
|
||||||
"option_info": "Info",
|
"option_info": "Info",
|
||||||
"user_guide_headline": "Steps to create an Alert",
|
"user_guide_headline": "Steps to create an Alert",
|
||||||
"user_guide_qb_step1": "Step 1 - Define the metric",
|
"user_guide_qb_step1": "Step 1 - Define the metric",
|
||||||
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
|
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
|
||||||
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
|
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
|
||||||
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
|
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
|
||||||
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
|
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
|
||||||
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
|
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
|
||||||
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
|
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
|
||||||
"user_guide_qb_step2b": "Enter the Alert threshold",
|
"user_guide_qb_step2b": "Enter the Alert threshold",
|
||||||
"user_guide_qb_step3": "Step 3 -Alert Configuration",
|
"user_guide_qb_step3": "Step 3 -Alert Configuration",
|
||||||
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
|
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
|
||||||
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
|
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
|
||||||
"user_guide_pql_step1": "Step 1 - Define the metric",
|
"user_guide_pql_step1": "Step 1 - Define the metric",
|
||||||
"user_guide_pql_step1a": "Write a PromQL query for the metric",
|
"user_guide_pql_step1a": "Write a PromQL query for the metric",
|
||||||
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
|
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
|
||||||
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
|
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
|
||||||
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||||
"user_guide_pql_step2b": "Enter the Alert threshold",
|
"user_guide_pql_step2b": "Enter the Alert threshold",
|
||||||
"user_guide_pql_step3": "Step 3 -Alert Configuration",
|
"user_guide_pql_step3": "Step 3 -Alert Configuration",
|
||||||
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
|
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
|
||||||
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
|
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
|
||||||
"user_guide_ch_step1": "Step 1 - Define the metric",
|
"user_guide_ch_step1": "Step 1 - Define the metric",
|
||||||
"user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial</0> to learn about query format and supported vars.",
|
"user_guide_ch_step1a": "Write a Clickhouse query for alert evaluation. Follow <0>this tutorial</0> to learn about query format and supported vars.",
|
||||||
"user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart",
|
"user_guide_ch_step1b": "Format the legends based on labels you want to highlight in the preview chart",
|
||||||
"user_guide_ch_step2": "Step 2 - Define Alert Conditions",
|
"user_guide_ch_step2": "Step 2 - Define Alert Conditions",
|
||||||
"user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
"user_guide_ch_step2a": "Select the threshold type and whether you want to alert above/below a value",
|
||||||
"user_guide_ch_step2b": "Enter the Alert threshold",
|
"user_guide_ch_step2b": "Enter the Alert threshold",
|
||||||
"user_guide_ch_step3": "Step 3 -Alert Configuration",
|
"user_guide_ch_step3": "Step 3 -Alert Configuration",
|
||||||
"user_guide_ch_step3a": "Set alert severity, name and descriptions",
|
"user_guide_ch_step3a": "Set alert severity, name and descriptions",
|
||||||
"user_guide_ch_step3b": "Add tags to the alert in the Label field if needed",
|
"user_guide_ch_step3b": "Add tags to the alert in the Label field if needed",
|
||||||
"user_tooltip_more_help": "More details on how to create alerts",
|
"user_tooltip_more_help": "More details on how to create alerts",
|
||||||
"choose_alert_type": "Choose a type for the alert:",
|
"choose_alert_type": "Choose a type for the alert:",
|
||||||
"metric_based_alert": "Metric based Alert",
|
"metric_based_alert": "Metric based Alert",
|
||||||
"metric_based_alert_desc": "Send a notification when a condition occurs in the metric data",
|
"metric_based_alert_desc": "Send a notification when a condition occurs in the metric data",
|
||||||
"log_based_alert": "Log-based Alert",
|
"log_based_alert": "Log-based Alert",
|
||||||
"log_based_alert_desc": "Send a notification when a condition occurs in the logs data.",
|
"log_based_alert_desc": "Send a notification when a condition occurs in the logs data.",
|
||||||
"traces_based_alert": "Trace-based Alert",
|
"traces_based_alert": "Trace-based Alert",
|
||||||
"traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.",
|
"traces_based_alert_desc": "Send a notification when a condition occurs in the traces data.",
|
||||||
"exceptions_based_alert": "Exceptions-based Alert",
|
"exceptions_based_alert": "Exceptions-based Alert",
|
||||||
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data."
|
"exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.",
|
||||||
|
"field_unit": "Threshold unit"
|
||||||
}
|
}
|
34
frontend/src/api/channels/createMsTeams.ts
Normal file
34
frontend/src/api/channels/createMsTeams.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
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/createMsTeams';
|
||||||
|
|
||||||
|
const create = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/channels', {
|
||||||
|
name: props.name,
|
||||||
|
msteams_configs: [
|
||||||
|
{
|
||||||
|
send_resolved: true,
|
||||||
|
webhook_url: props.webhook_url,
|
||||||
|
title: props.title,
|
||||||
|
text: props.text,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default create;
|
34
frontend/src/api/channels/editMsTeams.ts
Normal file
34
frontend/src/api/channels/editMsTeams.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
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/editMsTeams';
|
||||||
|
|
||||||
|
const editMsTeams = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.put(`/channels/${props.id}`, {
|
||||||
|
name: props.name,
|
||||||
|
msteams_configs: [
|
||||||
|
{
|
||||||
|
send_resolved: true,
|
||||||
|
webhook_url: props.webhook_url,
|
||||||
|
title: props.title,
|
||||||
|
text: props.text,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default editMsTeams;
|
34
frontend/src/api/channels/testMsTeams.ts
Normal file
34
frontend/src/api/channels/testMsTeams.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
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/createMsTeams';
|
||||||
|
|
||||||
|
const testMsTeams = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/testChannel', {
|
||||||
|
name: props.name,
|
||||||
|
msteams_configs: [
|
||||||
|
{
|
||||||
|
send_resolved: true,
|
||||||
|
webhook_url: props.webhook_url,
|
||||||
|
title: props.title,
|
||||||
|
text: props.text,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default testMsTeams;
|
16
frontend/src/api/metrics/ApDex/apDexSettings.ts
Normal file
16
frontend/src/api/metrics/ApDex/apDexSettings.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import {
|
||||||
|
ApDexPayloadAndSettingsProps,
|
||||||
|
SetApDexPayloadProps,
|
||||||
|
} from 'types/api/metrics/getApDex';
|
||||||
|
|
||||||
|
export const setApDexSettings = async ({
|
||||||
|
servicename,
|
||||||
|
threshold,
|
||||||
|
excludeStatusCode,
|
||||||
|
}: ApDexPayloadAndSettingsProps): Promise<SetApDexPayloadProps> =>
|
||||||
|
axios.post('/settings/apdex', {
|
||||||
|
servicename,
|
||||||
|
threshold,
|
||||||
|
excludeStatusCode,
|
||||||
|
});
|
8
frontend/src/api/metrics/ApDex/getApDexSettings.ts
Normal file
8
frontend/src/api/metrics/ApDex/getApDexSettings.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { AxiosResponse } from 'axios';
|
||||||
|
import { ApDexPayloadAndSettingsProps } from 'types/api/metrics/getApDex';
|
||||||
|
|
||||||
|
export const getApDexSettings = (
|
||||||
|
servicename: string,
|
||||||
|
): Promise<AxiosResponse<ApDexPayloadAndSettingsProps[]>> =>
|
||||||
|
axios.get(`/settings/apdex?services=${servicename}`);
|
8
frontend/src/api/metrics/ApDex/getMetricMeta.ts
Normal file
8
frontend/src/api/metrics/ApDex/getMetricMeta.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { AxiosResponse } from 'axios';
|
||||||
|
import { MetricMetaProps } from 'types/api/metrics/getApDex';
|
||||||
|
|
||||||
|
export const getMetricMeta = (
|
||||||
|
metricName: string,
|
||||||
|
): Promise<AxiosResponse<MetricMetaProps>> =>
|
||||||
|
axios.get(`/metric_meta?metricName=${metricName}`);
|
@ -3,7 +3,6 @@ import {
|
|||||||
BarElement,
|
BarElement,
|
||||||
CategoryScale,
|
CategoryScale,
|
||||||
Chart,
|
Chart,
|
||||||
ChartType,
|
|
||||||
Decimation,
|
Decimation,
|
||||||
Filler,
|
Filler,
|
||||||
Legend,
|
Legend,
|
||||||
@ -18,6 +17,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from 'chart.js';
|
} from 'chart.js';
|
||||||
import annotationPlugin from 'chartjs-plugin-annotation';
|
import annotationPlugin from 'chartjs-plugin-annotation';
|
||||||
|
import { generateGridTitle } from 'container/GridPanelSwitch/utils';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import isEqual from 'lodash-es/isEqual';
|
import isEqual from 'lodash-es/isEqual';
|
||||||
import {
|
import {
|
||||||
@ -26,6 +26,7 @@ import {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ const Graph = forwardRef<ToggleGraphProps | undefined, GraphProps>(
|
|||||||
const nearestDatasetIndex = useRef<null | number>(null);
|
const nearestDatasetIndex = useRef<null | number>(null);
|
||||||
const chartRef = useRef<HTMLCanvasElement>(null);
|
const chartRef = useRef<HTMLCanvasElement>(null);
|
||||||
const isDarkMode = useIsDarkMode();
|
const isDarkMode = useIsDarkMode();
|
||||||
|
const gridTitle = useMemo(() => generateGridTitle(title), [title]);
|
||||||
|
|
||||||
const currentTheme = isDarkMode ? 'dark' : 'light';
|
const currentTheme = isDarkMode ? 'dark' : 'light';
|
||||||
const xAxisTimeUnit = useXAxisTimeUnit(data); // Computes the relevant time unit for x axis by analyzing the time stamp data
|
const xAxisTimeUnit = useXAxisTimeUnit(data); // Computes the relevant time unit for x axis by analyzing the time stamp data
|
||||||
@ -119,7 +121,7 @@ const Graph = forwardRef<ToggleGraphProps | undefined, GraphProps>(
|
|||||||
const options: CustomChartOptions = getGraphOptions(
|
const options: CustomChartOptions = getGraphOptions(
|
||||||
animate,
|
animate,
|
||||||
staticLine,
|
staticLine,
|
||||||
title,
|
gridTitle,
|
||||||
nearestDatasetIndex,
|
nearestDatasetIndex,
|
||||||
yAxisUnit,
|
yAxisUnit,
|
||||||
onDragSelect,
|
onDragSelect,
|
||||||
@ -154,7 +156,7 @@ const Graph = forwardRef<ToggleGraphProps | undefined, GraphProps>(
|
|||||||
}, [
|
}, [
|
||||||
animate,
|
animate,
|
||||||
staticLine,
|
staticLine,
|
||||||
title,
|
gridTitle,
|
||||||
yAxisUnit,
|
yAxisUnit,
|
||||||
onDragSelect,
|
onDragSelect,
|
||||||
dragSelectColor,
|
dragSelectColor,
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
ChartType,
|
ChartType,
|
||||||
TimeUnit,
|
TimeUnit,
|
||||||
} from 'chart.js';
|
} from 'chart.js';
|
||||||
import { ForwardedRef } from 'react';
|
import { ForwardedRef, ReactNode } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
dragSelectPluginId,
|
dragSelectPluginId,
|
||||||
@ -49,7 +49,7 @@ export interface GraphProps {
|
|||||||
animate?: boolean;
|
animate?: boolean;
|
||||||
type: ChartType;
|
type: ChartType;
|
||||||
data: Chart['data'];
|
data: Chart['data'];
|
||||||
title?: string;
|
title?: ReactNode;
|
||||||
isStacked?: boolean;
|
isStacked?: boolean;
|
||||||
onClickHandler?: GraphOnClickHandler;
|
onClickHandler?: GraphOnClickHandler;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { grey } from '@ant-design/colors';
|
import { blue, grey } from '@ant-design/colors';
|
||||||
import { QuestionCircleFilled } from '@ant-design/icons';
|
import {
|
||||||
|
QuestionCircleFilled,
|
||||||
|
QuestionCircleOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
import { Tooltip } from 'antd';
|
import { Tooltip } from 'antd';
|
||||||
import { themeColors } from 'constants/theme';
|
import { themeColors } from 'constants/theme';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
@ -7,7 +10,12 @@ import { useMemo } from 'react';
|
|||||||
|
|
||||||
import { style } from './styles';
|
import { style } from './styles';
|
||||||
|
|
||||||
function TextToolTip({ text, url }: TextToolTipProps): JSX.Element {
|
function TextToolTip({
|
||||||
|
text,
|
||||||
|
url,
|
||||||
|
useFilledIcon = true,
|
||||||
|
urlText,
|
||||||
|
}: TextToolTipProps): JSX.Element {
|
||||||
const isDarkMode = useIsDarkMode();
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
const overlay = useMemo(
|
const overlay = useMemo(
|
||||||
@ -16,12 +24,12 @@ function TextToolTip({ text, url }: TextToolTipProps): JSX.Element {
|
|||||||
{`${text} `}
|
{`${text} `}
|
||||||
{url && (
|
{url && (
|
||||||
<a href={url} rel="noopener noreferrer" target="_blank">
|
<a href={url} rel="noopener noreferrer" target="_blank">
|
||||||
here
|
{urlText || 'here'}
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
[text, url],
|
[text, url, urlText],
|
||||||
);
|
);
|
||||||
|
|
||||||
const iconStyle = useMemo(
|
const iconStyle = useMemo(
|
||||||
@ -32,19 +40,35 @@ function TextToolTip({ text, url }: TextToolTipProps): JSX.Element {
|
|||||||
[isDarkMode],
|
[isDarkMode],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const iconOutlinedStyle = useMemo(
|
||||||
|
() => ({
|
||||||
|
...style,
|
||||||
|
color: isDarkMode ? themeColors.navyBlue : blue[0],
|
||||||
|
}),
|
||||||
|
[isDarkMode],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip overlay={overlay}>
|
<Tooltip overlay={overlay}>
|
||||||
<QuestionCircleFilled style={iconStyle} />
|
{useFilledIcon ? (
|
||||||
|
<QuestionCircleFilled style={iconStyle} />
|
||||||
|
) : (
|
||||||
|
<QuestionCircleOutlined style={iconOutlinedStyle} />
|
||||||
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextToolTip.defaultProps = {
|
TextToolTip.defaultProps = {
|
||||||
url: '',
|
url: '',
|
||||||
|
urlText: '',
|
||||||
|
useFilledIcon: true,
|
||||||
};
|
};
|
||||||
interface TextToolTipProps {
|
interface TextToolTipProps {
|
||||||
url?: string;
|
url?: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
useFilledIcon?: boolean;
|
||||||
|
urlText?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TextToolTip;
|
export default TextToolTip;
|
||||||
|
31
frontend/src/components/Upgrade/UpgradePrompt.tsx
Normal file
31
frontend/src/components/Upgrade/UpgradePrompt.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { Alert, Space } from 'antd';
|
||||||
|
import { SIGNOZ_UPGRADE_PLAN_URL } from 'constants/app';
|
||||||
|
|
||||||
|
type UpgradePromptProps = {
|
||||||
|
title?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function UpgradePrompt({ title }: UpgradePromptProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
|
<Alert
|
||||||
|
message={title}
|
||||||
|
description={
|
||||||
|
<div>
|
||||||
|
This feature is available for paid plans only.{' '}
|
||||||
|
<a href={SIGNOZ_UPGRADE_PLAN_URL} target="_blank" rel="noreferrer">
|
||||||
|
Click here
|
||||||
|
</a>{' '}
|
||||||
|
to Upgrade
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
type="warning"
|
||||||
|
/>{' '}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpgradePrompt.defaultProps = {
|
||||||
|
title: 'Upgrade to a Paid Plan',
|
||||||
|
};
|
||||||
|
export default UpgradePrompt;
|
5
frontend/src/constants/apDex.ts
Normal file
5
frontend/src/constants/apDex.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const apDexToolTipText =
|
||||||
|
"Apdex is a way to measure your users' satisfaction with the response time of your web service. It's represented as a score from 0-1.";
|
||||||
|
export const apDexToolTipUrl =
|
||||||
|
'https://signoz.io/docs/userguide/metrics/#apdex?utm_source=product&utm_medium=frontend&utm_campaign=apdex';
|
||||||
|
export const apDexToolTipUrlText = 'Learn more about Apdex.';
|
@ -1,6 +1,12 @@
|
|||||||
// keep this consistent with backend constants.go
|
// keep this consistent with backend constants.go
|
||||||
export enum FeatureKeys {
|
export enum FeatureKeys {
|
||||||
SSO = 'SSO',
|
SSO = 'SSO',
|
||||||
|
ENTERPRISE_PLAN = 'ENTERPRISE_PLAN',
|
||||||
|
BASIC_PLAN = 'BASIC_PLAN',
|
||||||
|
ALERT_CHANNEL_SLACK = 'ALERT_CHANNEL_SLACK',
|
||||||
|
ALERT_CHANNEL_WEBHOOK = 'ALERT_CHANNEL_WEBHOOK',
|
||||||
|
ALERT_CHANNEL_PAGERDUTY = 'ALERT_CHANNEL_PAGERDUTY',
|
||||||
|
ALERT_CHANNEL_MSTEAMS = 'ALERT_CHANNEL_MSTEAMS',
|
||||||
DurationSort = 'DurationSort',
|
DurationSort = 'DurationSort',
|
||||||
TimestampSort = 'TimestampSort',
|
TimestampSort = 'TimestampSort',
|
||||||
SMART_TRACE_DETAIL = 'SMART_TRACE_DETAIL',
|
SMART_TRACE_DETAIL = 'SMART_TRACE_DETAIL',
|
||||||
@ -9,4 +15,5 @@ export enum FeatureKeys {
|
|||||||
QUERY_BUILDER_ALERTS = 'QUERY_BUILDER_ALERTS',
|
QUERY_BUILDER_ALERTS = 'QUERY_BUILDER_ALERTS',
|
||||||
DISABLE_UPSELL = 'DISABLE_UPSELL',
|
DISABLE_UPSELL = 'DISABLE_UPSELL',
|
||||||
USE_SPAN_METRICS = 'USE_SPAN_METRICS',
|
USE_SPAN_METRICS = 'USE_SPAN_METRICS',
|
||||||
|
OSS = 'OSS',
|
||||||
}
|
}
|
||||||
|
@ -63,10 +63,16 @@ export const ValidatePagerChannel = (p: PagerChannel): string => {
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChannelType = 'slack' | 'email' | 'webhook' | 'pagerduty';
|
export type ChannelType =
|
||||||
|
| 'slack'
|
||||||
|
| 'email'
|
||||||
|
| 'webhook'
|
||||||
|
| 'pagerduty'
|
||||||
|
| 'msteams';
|
||||||
export const SlackType: ChannelType = 'slack';
|
export const SlackType: ChannelType = 'slack';
|
||||||
export const WebhookType: ChannelType = 'webhook';
|
export const WebhookType: ChannelType = 'webhook';
|
||||||
export const PagerType: ChannelType = 'pagerduty';
|
export const PagerType: ChannelType = 'pagerduty';
|
||||||
|
export const MsTeamsType: ChannelType = 'msteams';
|
||||||
|
|
||||||
// LabelFilterStatement will be used for preparing filter conditions / matchers
|
// LabelFilterStatement will be used for preparing filter conditions / matchers
|
||||||
export interface LabelFilterStatement {
|
export interface LabelFilterStatement {
|
||||||
@ -81,3 +87,9 @@ export interface LabelFilterStatement {
|
|||||||
// filter value
|
// filter value
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MsTeamsChannel extends Channel {
|
||||||
|
webhook_url?: string;
|
||||||
|
title?: string;
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
|
import createMsTeamsApi from 'api/channels/createMsTeams';
|
||||||
import createPagerApi from 'api/channels/createPager';
|
import createPagerApi from 'api/channels/createPager';
|
||||||
import createSlackApi from 'api/channels/createSlack';
|
import createSlackApi from 'api/channels/createSlack';
|
||||||
import createWebhookApi from 'api/channels/createWebhook';
|
import createWebhookApi from 'api/channels/createWebhook';
|
||||||
|
import testMsTeamsApi from 'api/channels/testMsTeams';
|
||||||
import testPagerApi from 'api/channels/testPager';
|
import testPagerApi from 'api/channels/testPager';
|
||||||
import testSlackApi from 'api/channels/testSlack';
|
import testSlackApi from 'api/channels/testSlack';
|
||||||
import testWebhookApi from 'api/channels/testWebhook';
|
import testWebhookApi from 'api/channels/testWebhook';
|
||||||
@ -14,6 +16,8 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
|
MsTeamsChannel,
|
||||||
|
MsTeamsType,
|
||||||
PagerChannel,
|
PagerChannel,
|
||||||
PagerType,
|
PagerType,
|
||||||
SlackChannel,
|
SlackChannel,
|
||||||
@ -33,7 +37,7 @@ function CreateAlertChannels({
|
|||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
|
|
||||||
const [selectedConfig, setSelectedConfig] = useState<
|
const [selectedConfig, setSelectedConfig] = useState<
|
||||||
Partial<SlackChannel & WebhookChannel & PagerChannel>
|
Partial<SlackChannel & WebhookChannel & PagerChannel & MsTeamsChannel>
|
||||||
>({
|
>({
|
||||||
text: `{{ range .Alerts -}}
|
text: `{{ range .Alerts -}}
|
||||||
*Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
*Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
||||||
@ -102,9 +106,7 @@ function CreateAlertChannels({
|
|||||||
message: 'Success',
|
message: 'Success',
|
||||||
description: t('channel_creation_done'),
|
description: t('channel_creation_done'),
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
history.replace(ROUTES.ALL_CHANNELS);
|
||||||
history.replace(ROUTES.SETTINGS);
|
|
||||||
}, 2000);
|
|
||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
@ -165,9 +167,7 @@ function CreateAlertChannels({
|
|||||||
message: 'Success',
|
message: 'Success',
|
||||||
description: t('channel_creation_done'),
|
description: t('channel_creation_done'),
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
history.replace(ROUTES.ALL_CHANNELS);
|
||||||
history.replace(ROUTES.SETTINGS);
|
|
||||||
}, 2000);
|
|
||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
@ -222,9 +222,7 @@ function CreateAlertChannels({
|
|||||||
message: 'Success',
|
message: 'Success',
|
||||||
description: t('channel_creation_done'),
|
description: t('channel_creation_done'),
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
history.replace(ROUTES.ALL_CHANNELS);
|
||||||
history.replace(ROUTES.SETTINGS);
|
|
||||||
}, 2000);
|
|
||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
@ -241,26 +239,71 @@ function CreateAlertChannels({
|
|||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}, [t, notifications, preparePagerRequest]);
|
}, [t, notifications, preparePagerRequest]);
|
||||||
|
|
||||||
|
const prepareMsTeamsRequest = useCallback(
|
||||||
|
() => ({
|
||||||
|
webhook_url: selectedConfig?.webhook_url || '',
|
||||||
|
name: selectedConfig?.name || '',
|
||||||
|
send_resolved: true,
|
||||||
|
text: selectedConfig?.text || '',
|
||||||
|
title: selectedConfig?.title || '',
|
||||||
|
}),
|
||||||
|
[selectedConfig],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onMsTeamsHandler = useCallback(async () => {
|
||||||
|
setSavingState(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await createMsTeamsApi(prepareMsTeamsRequest());
|
||||||
|
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
notifications.success({
|
||||||
|
message: 'Success',
|
||||||
|
description: t('channel_creation_done'),
|
||||||
|
});
|
||||||
|
history.replace(ROUTES.ALL_CHANNELS);
|
||||||
|
} else {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: response.error || t('channel_creation_failed'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('channel_creation_failed'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSavingState(false);
|
||||||
|
}, [prepareMsTeamsRequest, t, notifications]);
|
||||||
|
|
||||||
const onSaveHandler = useCallback(
|
const onSaveHandler = useCallback(
|
||||||
async (value: ChannelType) => {
|
async (value: ChannelType) => {
|
||||||
switch (value) {
|
const functionMapper = {
|
||||||
case SlackType:
|
[SlackType]: onSlackHandler,
|
||||||
onSlackHandler();
|
[WebhookType]: onWebhookHandler,
|
||||||
break;
|
[PagerType]: onPagerHandler,
|
||||||
case WebhookType:
|
[MsTeamsType]: onMsTeamsHandler,
|
||||||
onWebhookHandler();
|
};
|
||||||
break;
|
const functionToCall = functionMapper[value];
|
||||||
case PagerType:
|
|
||||||
onPagerHandler();
|
if (functionToCall) {
|
||||||
break;
|
functionToCall();
|
||||||
default:
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
description: t('selected_channel_invalid'),
|
description: t('selected_channel_invalid'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onSlackHandler, t, onPagerHandler, onWebhookHandler, notifications],
|
[
|
||||||
|
onSlackHandler,
|
||||||
|
onWebhookHandler,
|
||||||
|
onPagerHandler,
|
||||||
|
onMsTeamsHandler,
|
||||||
|
notifications,
|
||||||
|
t,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const performChannelTest = useCallback(
|
const performChannelTest = useCallback(
|
||||||
@ -282,6 +325,10 @@ function CreateAlertChannels({
|
|||||||
request = preparePagerRequest();
|
request = preparePagerRequest();
|
||||||
if (request) response = await testPagerApi(request);
|
if (request) response = await testPagerApi(request);
|
||||||
break;
|
break;
|
||||||
|
case MsTeamsType:
|
||||||
|
request = prepareMsTeamsRequest();
|
||||||
|
response = await testMsTeamsApi(request);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
@ -315,6 +362,7 @@ function CreateAlertChannels({
|
|||||||
t,
|
t,
|
||||||
preparePagerRequest,
|
preparePagerRequest,
|
||||||
prepareSlackRequest,
|
prepareSlackRequest,
|
||||||
|
prepareMsTeamsRequest,
|
||||||
notifications,
|
notifications,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -40,6 +40,7 @@ export const alertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
queryType: EQueryType.QUERY_BUILDER,
|
queryType: EQueryType.QUERY_BUILDER,
|
||||||
panelType: PANEL_TYPES.TIME_SERIES,
|
panelType: PANEL_TYPES.TIME_SERIES,
|
||||||
|
unit: undefined,
|
||||||
},
|
},
|
||||||
op: defaultCompareOp,
|
op: defaultCompareOp,
|
||||||
matchType: defaultMatchType,
|
matchType: defaultMatchType,
|
||||||
@ -69,6 +70,7 @@ export const logAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
queryType: EQueryType.QUERY_BUILDER,
|
queryType: EQueryType.QUERY_BUILDER,
|
||||||
panelType: PANEL_TYPES.TIME_SERIES,
|
panelType: PANEL_TYPES.TIME_SERIES,
|
||||||
|
unit: undefined,
|
||||||
},
|
},
|
||||||
op: defaultCompareOp,
|
op: defaultCompareOp,
|
||||||
matchType: '4',
|
matchType: '4',
|
||||||
@ -99,6 +101,7 @@ export const traceAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
queryType: EQueryType.QUERY_BUILDER,
|
queryType: EQueryType.QUERY_BUILDER,
|
||||||
panelType: PANEL_TYPES.TIME_SERIES,
|
panelType: PANEL_TYPES.TIME_SERIES,
|
||||||
|
unit: undefined,
|
||||||
},
|
},
|
||||||
op: defaultCompareOp,
|
op: defaultCompareOp,
|
||||||
matchType: '4',
|
matchType: '4',
|
||||||
@ -129,6 +132,7 @@ export const exceptionAlertDefaults: AlertDef = {
|
|||||||
},
|
},
|
||||||
queryType: EQueryType.QUERY_BUILDER,
|
queryType: EQueryType.QUERY_BUILDER,
|
||||||
panelType: PANEL_TYPES.TIME_SERIES,
|
panelType: PANEL_TYPES.TIME_SERIES,
|
||||||
|
unit: undefined,
|
||||||
},
|
},
|
||||||
op: defaultCompareOp,
|
op: defaultCompareOp,
|
||||||
matchType: '4',
|
matchType: '4',
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
|
import editMsTeamsApi from 'api/channels/editMsTeams';
|
||||||
import editPagerApi from 'api/channels/editPager';
|
import editPagerApi from 'api/channels/editPager';
|
||||||
import editSlackApi from 'api/channels/editSlack';
|
import editSlackApi from 'api/channels/editSlack';
|
||||||
import editWebhookApi from 'api/channels/editWebhook';
|
import editWebhookApi from 'api/channels/editWebhook';
|
||||||
|
import testMsTeamsApi from 'api/channels/testMsTeams';
|
||||||
import testPagerApi from 'api/channels/testPager';
|
import testPagerApi from 'api/channels/testPager';
|
||||||
import testSlackApi from 'api/channels/testSlack';
|
import testSlackApi from 'api/channels/testSlack';
|
||||||
import testWebhookApi from 'api/channels/testWebhook';
|
import testWebhookApi from 'api/channels/testWebhook';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
|
MsTeamsChannel,
|
||||||
|
MsTeamsType,
|
||||||
PagerChannel,
|
PagerChannel,
|
||||||
PagerType,
|
PagerType,
|
||||||
SlackChannel,
|
SlackChannel,
|
||||||
@ -31,7 +35,7 @@ function EditAlertChannels({
|
|||||||
|
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
const [selectedConfig, setSelectedConfig] = useState<
|
const [selectedConfig, setSelectedConfig] = useState<
|
||||||
Partial<SlackChannel & WebhookChannel & PagerChannel>
|
Partial<SlackChannel & WebhookChannel & PagerChannel & MsTeamsChannel>
|
||||||
>({
|
>({
|
||||||
...initialValue,
|
...initialValue,
|
||||||
});
|
});
|
||||||
@ -81,9 +85,7 @@ function EditAlertChannels({
|
|||||||
description: t('channel_edit_done'),
|
description: t('channel_edit_done'),
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
history.replace(ROUTES.ALL_CHANNELS);
|
||||||
history.replace(ROUTES.SETTINGS);
|
|
||||||
}, 2000);
|
|
||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
@ -136,9 +138,7 @@ function EditAlertChannels({
|
|||||||
description: t('channel_edit_done'),
|
description: t('channel_edit_done'),
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
history.replace(ROUTES.ALL_CHANNELS);
|
||||||
history.replace(ROUTES.SETTINGS);
|
|
||||||
}, 2000);
|
|
||||||
} else {
|
} else {
|
||||||
showError(response.error || t('channel_edit_failed'));
|
showError(response.error || t('channel_edit_failed'));
|
||||||
}
|
}
|
||||||
@ -183,9 +183,7 @@ function EditAlertChannels({
|
|||||||
description: t('channel_edit_done'),
|
description: t('channel_edit_done'),
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
history.replace(ROUTES.ALL_CHANNELS);
|
||||||
history.replace(ROUTES.SETTINGS);
|
|
||||||
}, 2000);
|
|
||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
@ -195,6 +193,48 @@ function EditAlertChannels({
|
|||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}, [preparePagerRequest, notifications, selectedConfig, t]);
|
}, [preparePagerRequest, notifications, selectedConfig, t]);
|
||||||
|
|
||||||
|
const prepareMsTeamsRequest = useCallback(
|
||||||
|
() => ({
|
||||||
|
webhook_url: selectedConfig?.webhook_url || '',
|
||||||
|
name: selectedConfig?.name || '',
|
||||||
|
send_resolved: true,
|
||||||
|
text: selectedConfig?.text || '',
|
||||||
|
title: selectedConfig?.title || '',
|
||||||
|
id,
|
||||||
|
}),
|
||||||
|
[id, selectedConfig],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onMsTeamsEditHandler = useCallback(async () => {
|
||||||
|
setSavingState(true);
|
||||||
|
|
||||||
|
if (selectedConfig?.webhook_url === '') {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('webhook_url_required'),
|
||||||
|
});
|
||||||
|
setSavingState(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await editMsTeamsApi(prepareMsTeamsRequest());
|
||||||
|
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
notifications.success({
|
||||||
|
message: 'Success',
|
||||||
|
description: t('channel_edit_done'),
|
||||||
|
});
|
||||||
|
|
||||||
|
history.replace(ROUTES.ALL_CHANNELS);
|
||||||
|
} else {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: response.error || t('channel_edit_failed'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSavingState(false);
|
||||||
|
}, [prepareMsTeamsRequest, t, notifications, selectedConfig]);
|
||||||
|
|
||||||
const onSaveHandler = useCallback(
|
const onSaveHandler = useCallback(
|
||||||
(value: ChannelType) => {
|
(value: ChannelType) => {
|
||||||
if (value === SlackType) {
|
if (value === SlackType) {
|
||||||
@ -203,9 +243,16 @@ function EditAlertChannels({
|
|||||||
onWebhookEditHandler();
|
onWebhookEditHandler();
|
||||||
} else if (value === PagerType) {
|
} else if (value === PagerType) {
|
||||||
onPagerEditHandler();
|
onPagerEditHandler();
|
||||||
|
} else if (value === MsTeamsType) {
|
||||||
|
onMsTeamsEditHandler();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onSlackEditHandler, onWebhookEditHandler, onPagerEditHandler],
|
[
|
||||||
|
onSlackEditHandler,
|
||||||
|
onWebhookEditHandler,
|
||||||
|
onPagerEditHandler,
|
||||||
|
onMsTeamsEditHandler,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const performChannelTest = useCallback(
|
const performChannelTest = useCallback(
|
||||||
@ -227,6 +274,10 @@ function EditAlertChannels({
|
|||||||
request = preparePagerRequest();
|
request = preparePagerRequest();
|
||||||
if (request) response = await testPagerApi(request);
|
if (request) response = await testPagerApi(request);
|
||||||
break;
|
break;
|
||||||
|
case MsTeamsType:
|
||||||
|
request = prepareMsTeamsRequest();
|
||||||
|
if (request) response = await testMsTeamsApi(request);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
@ -260,6 +311,7 @@ function EditAlertChannels({
|
|||||||
prepareWebhookRequest,
|
prepareWebhookRequest,
|
||||||
preparePagerRequest,
|
preparePagerRequest,
|
||||||
prepareSlackRequest,
|
prepareSlackRequest,
|
||||||
|
prepareMsTeamsRequest,
|
||||||
notifications,
|
notifications,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
import { Form, Input } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { MsTeamsChannel } from '../../CreateAlertChannels/config';
|
||||||
|
|
||||||
|
function MsTeams({ setSelectedConfig }: MsTeamsProps): JSX.Element {
|
||||||
|
const { t } = useTranslation('channels');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item name="webhook_url" label={t('field_webhook_url')}>
|
||||||
|
<Input
|
||||||
|
onChange={(event): void => {
|
||||||
|
setSelectedConfig((value) => ({
|
||||||
|
...value,
|
||||||
|
webhook_url: event.target.value,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="title" label={t('field_slack_title')}>
|
||||||
|
<Input.TextArea
|
||||||
|
rows={4}
|
||||||
|
// value={`[{{ .Status | toUpper }}{{ if eq .Status \"firing\" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}\n{{- if gt (len .CommonLabels) (len .GroupLabels) -}}\n{{\" \"}}(\n{{- with .CommonLabels.Remove .GroupLabels.Names }}\n {{- range $index, $label := .SortedPairs -}}\n {{ if $index }}, {{ end }}\n {{- $label.Name }}=\"{{ $label.Value -}}\"\n {{- end }}\n{{- end -}}\n)\n{{- end }}`}
|
||||||
|
onChange={(event): void =>
|
||||||
|
setSelectedConfig((value) => ({
|
||||||
|
...value,
|
||||||
|
title: event.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="text" label={t('field_slack_description')}>
|
||||||
|
<Input.TextArea
|
||||||
|
onChange={(event): void =>
|
||||||
|
setSelectedConfig((value) => ({
|
||||||
|
...value,
|
||||||
|
text: event.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
placeholder={t('placeholder_slack_description')}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MsTeamsProps {
|
||||||
|
setSelectedConfig: React.Dispatch<
|
||||||
|
React.SetStateAction<Partial<MsTeamsChannel>>
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MsTeams;
|
@ -1,8 +1,11 @@
|
|||||||
import { Form, FormInstance, Input, Select, Typography } from 'antd';
|
import { Form, FormInstance, Input, Select, Typography } from 'antd';
|
||||||
import { Store } from 'antd/lib/form/interface';
|
import { Store } from 'antd/lib/form/interface';
|
||||||
|
import UpgradePrompt from 'components/Upgrade/UpgradePrompt';
|
||||||
|
import { FeatureKeys } from 'constants/features';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
|
MsTeamsType,
|
||||||
PagerChannel,
|
PagerChannel,
|
||||||
PagerType,
|
PagerType,
|
||||||
SlackChannel,
|
SlackChannel,
|
||||||
@ -10,18 +13,18 @@ import {
|
|||||||
WebhookChannel,
|
WebhookChannel,
|
||||||
WebhookType,
|
WebhookType,
|
||||||
} from 'container/CreateAlertChannels/config';
|
} from 'container/CreateAlertChannels/config';
|
||||||
|
import useFeatureFlags from 'hooks/useFeatureFlag';
|
||||||
|
import { isFeatureKeys } from 'hooks/useFeatureFlag/utils';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { Dispatch, ReactElement, SetStateAction } from 'react';
|
import { Dispatch, ReactElement, SetStateAction } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import MsTeamsSettings from './Settings/MsTeams';
|
||||||
import PagerSettings from './Settings/Pager';
|
import PagerSettings from './Settings/Pager';
|
||||||
import SlackSettings from './Settings/Slack';
|
import SlackSettings from './Settings/Slack';
|
||||||
import WebhookSettings from './Settings/Webhook';
|
import WebhookSettings from './Settings/Webhook';
|
||||||
import { Button } from './styles';
|
import { Button } from './styles';
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
const { Title } = Typography;
|
|
||||||
|
|
||||||
function FormAlertChannels({
|
function FormAlertChannels({
|
||||||
formInstance,
|
formInstance,
|
||||||
type,
|
type,
|
||||||
@ -36,8 +39,27 @@ function FormAlertChannels({
|
|||||||
editing = false,
|
editing = false,
|
||||||
}: FormAlertChannelsProps): JSX.Element {
|
}: FormAlertChannelsProps): JSX.Element {
|
||||||
const { t } = useTranslation('channels');
|
const { t } = useTranslation('channels');
|
||||||
|
const isUserOnEEPlan = useFeatureFlags(FeatureKeys.ENTERPRISE_PLAN);
|
||||||
|
|
||||||
|
const feature = `ALERT_CHANNEL_${type.toUpperCase()}`;
|
||||||
|
|
||||||
|
const hasFeature = useFeatureFlags(
|
||||||
|
isFeatureKeys(feature) ? feature : FeatureKeys.ALERT_CHANNEL_SLACK,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isOssFeature = useFeatureFlags(FeatureKeys.OSS);
|
||||||
|
|
||||||
const renderSettings = (): ReactElement | null => {
|
const renderSettings = (): ReactElement | null => {
|
||||||
|
if (
|
||||||
|
// for ee plan
|
||||||
|
!isOssFeature?.active &&
|
||||||
|
(!hasFeature || !hasFeature.active) &&
|
||||||
|
type === 'msteams'
|
||||||
|
) {
|
||||||
|
// channel type is not available for users plan
|
||||||
|
return <UpgradePrompt />;
|
||||||
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SlackType:
|
case SlackType:
|
||||||
return <SlackSettings setSelectedConfig={setSelectedConfig} />;
|
return <SlackSettings setSelectedConfig={setSelectedConfig} />;
|
||||||
@ -45,14 +67,16 @@ function FormAlertChannels({
|
|||||||
return <WebhookSettings setSelectedConfig={setSelectedConfig} />;
|
return <WebhookSettings setSelectedConfig={setSelectedConfig} />;
|
||||||
case PagerType:
|
case PagerType:
|
||||||
return <PagerSettings setSelectedConfig={setSelectedConfig} />;
|
return <PagerSettings setSelectedConfig={setSelectedConfig} />;
|
||||||
|
case MsTeamsType:
|
||||||
|
return <MsTeamsSettings setSelectedConfig={setSelectedConfig} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title level={3}>{title}</Title>
|
<Typography.Title level={3}>{title}</Typography.Title>
|
||||||
|
|
||||||
<Form initialValues={initialValue} layout="vertical" form={formInstance}>
|
<Form initialValues={initialValue} layout="vertical" form={formInstance}>
|
||||||
<Form.Item label={t('field_channel_name')} labelAlign="left" name="name">
|
<Form.Item label={t('field_channel_name')} labelAlign="left" name="name">
|
||||||
@ -69,15 +93,22 @@ function FormAlertChannels({
|
|||||||
|
|
||||||
<Form.Item label={t('field_channel_type')} labelAlign="left" name="type">
|
<Form.Item label={t('field_channel_type')} labelAlign="left" name="type">
|
||||||
<Select disabled={editing} onChange={onTypeChangeHandler} value={type}>
|
<Select disabled={editing} onChange={onTypeChangeHandler} value={type}>
|
||||||
<Option value="slack" key="slack">
|
<Select.Option value="slack" key="slack">
|
||||||
Slack
|
Slack
|
||||||
</Option>
|
</Select.Option>
|
||||||
<Option value="webhook" key="webhook">
|
<Select.Option value="webhook" key="webhook">
|
||||||
Webhook
|
Webhook
|
||||||
</Option>
|
</Select.Option>
|
||||||
<Option value="pagerduty" key="pagerduty">
|
<Select.Option value="pagerduty" key="pagerduty">
|
||||||
Pagerduty
|
Pagerduty
|
||||||
</Option>
|
</Select.Option>
|
||||||
|
{!isOssFeature?.active && (
|
||||||
|
<Select.Option value="msteams" key="msteams">
|
||||||
|
<div>
|
||||||
|
Microsoft Teams {!isUserOnEEPlan && '(Supported in Paid Plans Only)'}{' '}
|
||||||
|
</div>
|
||||||
|
</Select.Option>
|
||||||
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
@ -85,7 +116,7 @@ function FormAlertChannels({
|
|||||||
|
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button
|
<Button
|
||||||
disabled={savingState}
|
disabled={savingState || !hasFeature}
|
||||||
loading={savingState}
|
loading={savingState}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={(): void => onSaveHandler(type)}
|
onClick={(): void => onSaveHandler(type)}
|
||||||
@ -93,7 +124,7 @@ function FormAlertChannels({
|
|||||||
{t('button_save_channel')}
|
{t('button_save_channel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={testingState}
|
disabled={testingState || !hasFeature}
|
||||||
loading={testingState}
|
loading={testingState}
|
||||||
onClick={(): void => onTestHandler(type)}
|
onClick={(): void => onTestHandler(type)}
|
||||||
>
|
>
|
||||||
|
98
frontend/src/container/FormAlertRules/ChartPreview/config.ts
Normal file
98
frontend/src/container/FormAlertRules/ChartPreview/config.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import {
|
||||||
|
DataFormats,
|
||||||
|
DataRateFormats,
|
||||||
|
MiscellaneousFormats,
|
||||||
|
ThroughputFormats,
|
||||||
|
TimeFormats,
|
||||||
|
} from 'container/NewWidget/RightContainer/types';
|
||||||
|
|
||||||
|
export const dataFormatConfig: Record<DataFormats, number> = {
|
||||||
|
[DataFormats.BytesIEC]: 1,
|
||||||
|
[DataFormats.BytesSI]: 1,
|
||||||
|
[DataFormats.BitsIEC]: 1 / 8,
|
||||||
|
[DataFormats.BitsSI]: 1 / 8,
|
||||||
|
[DataFormats.KibiBytes]: 1024,
|
||||||
|
[DataFormats.KiloBytes]: 1000,
|
||||||
|
[DataFormats.MebiBytes]: 1024 ** 2,
|
||||||
|
[DataFormats.MegaBytes]: 1000 ** 2,
|
||||||
|
[DataFormats.GibiBytes]: 1024 ** 3,
|
||||||
|
[DataFormats.GigaBytes]: 1000 ** 3,
|
||||||
|
[DataFormats.TebiBytes]: 1024 ** 4,
|
||||||
|
[DataFormats.TeraBytes]: 1000 ** 4,
|
||||||
|
[DataFormats.PebiBytes]: 1024 ** 5,
|
||||||
|
[DataFormats.PetaBytes]: 1000 ** 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const throughputConfig: Record<ThroughputFormats, number> = {
|
||||||
|
[ThroughputFormats.CountsPerSec]: 1,
|
||||||
|
[ThroughputFormats.OpsPerSec]: 1,
|
||||||
|
[ThroughputFormats.RequestsPerSec]: 1,
|
||||||
|
[ThroughputFormats.ReadsPerSec]: 1,
|
||||||
|
[ThroughputFormats.WritesPerSec]: 1,
|
||||||
|
[ThroughputFormats.IOOpsPerSec]: 1,
|
||||||
|
[ThroughputFormats.CountsPerMin]: 1 / 60,
|
||||||
|
[ThroughputFormats.OpsPerMin]: 1 / 60,
|
||||||
|
[ThroughputFormats.ReadsPerMin]: 1 / 60,
|
||||||
|
[ThroughputFormats.WritesPerMin]: 1 / 60,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const timeUnitsConfig: Record<TimeFormats, number> = {
|
||||||
|
[TimeFormats.Hertz]: 1,
|
||||||
|
[TimeFormats.Nanoseconds]: 1e-9,
|
||||||
|
[TimeFormats.Microseconds]: 1e-6,
|
||||||
|
[TimeFormats.Milliseconds]: 1e-3,
|
||||||
|
[TimeFormats.Seconds]: 1,
|
||||||
|
[TimeFormats.Minutes]: 60,
|
||||||
|
[TimeFormats.Hours]: 3600,
|
||||||
|
[TimeFormats.Days]: 86400,
|
||||||
|
[TimeFormats.DurationMs]: 1e-3,
|
||||||
|
[TimeFormats.DurationS]: 1,
|
||||||
|
[TimeFormats.DurationHms]: 3600,
|
||||||
|
[TimeFormats.DurationDhms]: 86400,
|
||||||
|
[TimeFormats.Timeticks]: 1e-3,
|
||||||
|
[TimeFormats.ClockMs]: 1e-3,
|
||||||
|
[TimeFormats.ClockS]: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dataRateUnitsConfig: Record<DataRateFormats, number> = {
|
||||||
|
[DataRateFormats.PacketsPerSec]: 1,
|
||||||
|
[DataRateFormats.BytesPerSecIEC]: dataFormatConfig[DataFormats.BytesIEC],
|
||||||
|
[DataRateFormats.BytesPerSecSI]: dataFormatConfig[DataFormats.BytesSI],
|
||||||
|
[DataRateFormats.BitsPerSecIEC]: dataFormatConfig[DataFormats.BitsIEC],
|
||||||
|
[DataRateFormats.BitsPerSecSI]: dataFormatConfig[DataFormats.BitsSI],
|
||||||
|
[DataRateFormats.KibiBytesPerSec]: dataFormatConfig[DataFormats.KibiBytes],
|
||||||
|
[DataRateFormats.KibiBitsPerSec]: dataFormatConfig[DataFormats.KibiBytes] * 8,
|
||||||
|
[DataRateFormats.KiloBytesPerSec]: dataFormatConfig[DataFormats.KiloBytes],
|
||||||
|
[DataRateFormats.KiloBitsPerSec]: dataFormatConfig[DataFormats.KiloBytes] * 8,
|
||||||
|
[DataRateFormats.MebiBytesPerSec]: dataFormatConfig[DataFormats.MebiBytes],
|
||||||
|
[DataRateFormats.MebiBitsPerSec]: dataFormatConfig[DataFormats.MebiBytes] * 8,
|
||||||
|
[DataRateFormats.MegaBytesPerSec]: dataFormatConfig[DataFormats.MegaBytes],
|
||||||
|
[DataRateFormats.MegaBitsPerSec]: dataFormatConfig[DataFormats.MegaBytes] * 8,
|
||||||
|
[DataRateFormats.GibiBytesPerSec]: dataFormatConfig[DataFormats.GibiBytes],
|
||||||
|
[DataRateFormats.GibiBitsPerSec]: dataFormatConfig[DataFormats.GibiBytes] * 8,
|
||||||
|
[DataRateFormats.GigaBytesPerSec]: dataFormatConfig[DataFormats.GigaBytes],
|
||||||
|
[DataRateFormats.GigaBitsPerSec]: dataFormatConfig[DataFormats.GigaBytes] * 8,
|
||||||
|
[DataRateFormats.TebiBytesPerSec]: dataFormatConfig[DataFormats.TebiBytes],
|
||||||
|
[DataRateFormats.TebiBitsPerSec]: dataFormatConfig[DataFormats.TebiBytes] * 8,
|
||||||
|
[DataRateFormats.TeraBytesPerSec]: dataFormatConfig[DataFormats.TeraBytes],
|
||||||
|
[DataRateFormats.TeraBitsPerSec]: dataFormatConfig[DataFormats.TeraBytes] * 8,
|
||||||
|
[DataRateFormats.PebiBytesPerSec]: dataFormatConfig[DataFormats.PebiBytes],
|
||||||
|
[DataRateFormats.PebiBitsPerSec]: dataFormatConfig[DataFormats.PebiBytes] * 8,
|
||||||
|
[DataRateFormats.PetaBytesPerSec]: dataFormatConfig[DataFormats.PetaBytes],
|
||||||
|
[DataRateFormats.PetaBitsPerSec]: dataFormatConfig[DataFormats.PetaBytes] * 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const miscUnitsConfig: Record<MiscellaneousFormats, number> = {
|
||||||
|
[MiscellaneousFormats.None]: 1,
|
||||||
|
[MiscellaneousFormats.String]: 1,
|
||||||
|
[MiscellaneousFormats.Short]: 1,
|
||||||
|
[MiscellaneousFormats.Percent]: 0.01,
|
||||||
|
[MiscellaneousFormats.PercentUnit]: 1,
|
||||||
|
[MiscellaneousFormats.Humidity]: 1,
|
||||||
|
[MiscellaneousFormats.Decibel]: 1,
|
||||||
|
[MiscellaneousFormats.Hexadecimal0x]: 1,
|
||||||
|
[MiscellaneousFormats.Hexadecimal]: 1,
|
||||||
|
[MiscellaneousFormats.ScientificNotation]: 1,
|
||||||
|
[MiscellaneousFormats.LocaleFormat]: 1,
|
||||||
|
[MiscellaneousFormats.Pixels]: 1,
|
||||||
|
};
|
@ -9,10 +9,12 @@ import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
|||||||
import getChartData from 'lib/getChartData';
|
import getChartData from 'lib/getChartData';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { AlertDef } from 'types/api/alerts/def';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
|
||||||
import { ChartContainer, FailedMessageContainer } from './styles';
|
import { ChartContainer, FailedMessageContainer } from './styles';
|
||||||
|
import { covertIntoDataFormats } from './utils';
|
||||||
|
|
||||||
export interface ChartPreviewProps {
|
export interface ChartPreviewProps {
|
||||||
name: string;
|
name: string;
|
||||||
@ -21,7 +23,7 @@ export interface ChartPreviewProps {
|
|||||||
selectedTime?: timePreferenceType;
|
selectedTime?: timePreferenceType;
|
||||||
selectedInterval?: Time;
|
selectedInterval?: Time;
|
||||||
headline?: JSX.Element;
|
headline?: JSX.Element;
|
||||||
threshold?: number | undefined;
|
alertDef?: AlertDef;
|
||||||
userQueryKey?: string;
|
userQueryKey?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,18 +34,28 @@ function ChartPreview({
|
|||||||
selectedTime = 'GLOBAL_TIME',
|
selectedTime = 'GLOBAL_TIME',
|
||||||
selectedInterval = '5min',
|
selectedInterval = '5min',
|
||||||
headline,
|
headline,
|
||||||
threshold,
|
|
||||||
userQueryKey,
|
userQueryKey,
|
||||||
|
alertDef,
|
||||||
}: ChartPreviewProps): JSX.Element | null {
|
}: ChartPreviewProps): JSX.Element | null {
|
||||||
const { t } = useTranslation('alerts');
|
const { t } = useTranslation('alerts');
|
||||||
|
const threshold = alertDef?.condition.target || 0;
|
||||||
|
|
||||||
|
const thresholdValue = covertIntoDataFormats({
|
||||||
|
value: threshold,
|
||||||
|
sourceUnit: alertDef?.condition.targetUnit,
|
||||||
|
targetUnit: query?.unit,
|
||||||
|
});
|
||||||
|
|
||||||
const staticLine: StaticLineProps | undefined =
|
const staticLine: StaticLineProps | undefined =
|
||||||
threshold !== undefined
|
threshold !== undefined
|
||||||
? {
|
? {
|
||||||
yMin: threshold,
|
yMin: thresholdValue,
|
||||||
yMax: threshold,
|
yMax: thresholdValue,
|
||||||
borderColor: '#f14',
|
borderColor: '#f14',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
lineText: `${t('preview_chart_threshold_label')} (y=${threshold})`,
|
lineText: `${t('preview_chart_threshold_label')} (y=${thresholdValue} ${
|
||||||
|
query?.unit || ''
|
||||||
|
})`,
|
||||||
textColor: '#f14',
|
textColor: '#f14',
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
@ -121,6 +133,7 @@ function ChartPreview({
|
|||||||
staticLine={staticLine}
|
staticLine={staticLine}
|
||||||
panelData={queryResponse.data?.payload.data.newResult.data.result || []}
|
panelData={queryResponse.data?.payload.data.newResult.data.result || []}
|
||||||
query={query || initialQueriesMap.metrics}
|
query={query || initialQueriesMap.metrics}
|
||||||
|
yAxisUnit={query?.unit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ChartContainer>
|
</ChartContainer>
|
||||||
@ -132,8 +145,8 @@ ChartPreview.defaultProps = {
|
|||||||
selectedTime: 'GLOBAL_TIME',
|
selectedTime: 'GLOBAL_TIME',
|
||||||
selectedInterval: '5min',
|
selectedInterval: '5min',
|
||||||
headline: undefined,
|
headline: undefined,
|
||||||
threshold: undefined,
|
|
||||||
userQueryKey: '',
|
userQueryKey: '',
|
||||||
|
alertDef: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ChartPreview;
|
export default ChartPreview;
|
||||||
|
105
frontend/src/container/FormAlertRules/ChartPreview/utils.test.ts
Normal file
105
frontend/src/container/FormAlertRules/ChartPreview/utils.test.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { DataFormats } from 'container/NewWidget/RightContainer/types';
|
||||||
|
|
||||||
|
import { covertIntoDataFormats } from './utils';
|
||||||
|
|
||||||
|
describe('Convert One Unit to another unit', () => {
|
||||||
|
it('should convert from BitsIEC to BytesIEC', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: 8,
|
||||||
|
sourceUnit: DataFormats.BitsIEC,
|
||||||
|
targetUnit: DataFormats.BytesIEC,
|
||||||
|
});
|
||||||
|
expect(result).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// for KibiBytes to MebiBytes conversion
|
||||||
|
it('should convert from KibiBytes to MebiBytes', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: 1024,
|
||||||
|
sourceUnit: DataFormats.KibiBytes,
|
||||||
|
targetUnit: DataFormats.MebiBytes,
|
||||||
|
});
|
||||||
|
expect(result).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// for MegaBytes to GigaBytes conversion (SI units)
|
||||||
|
it('should convert from MegaBytes to GigaBytes (SI)', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: 1000,
|
||||||
|
sourceUnit: DataFormats.MegaBytes,
|
||||||
|
targetUnit: DataFormats.GigaBytes,
|
||||||
|
});
|
||||||
|
expect(result).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// for identity conversion
|
||||||
|
it('should handle identity conversion', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: 100,
|
||||||
|
sourceUnit: DataFormats.KibiBytes,
|
||||||
|
targetUnit: DataFormats.KibiBytes,
|
||||||
|
});
|
||||||
|
expect(result).toBe(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// BytesIEC to BitsIEC conversion
|
||||||
|
it('should convert from BytesIEC to BitsIEC', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: 1,
|
||||||
|
sourceUnit: DataFormats.BytesIEC,
|
||||||
|
targetUnit: DataFormats.BitsIEC,
|
||||||
|
});
|
||||||
|
expect(result).toBe(8);
|
||||||
|
});
|
||||||
|
|
||||||
|
// for GibiBytes to TebiBytes conversion
|
||||||
|
it('should convert from GibiBytes to TebiBytes', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: 1024,
|
||||||
|
sourceUnit: DataFormats.GibiBytes,
|
||||||
|
targetUnit: DataFormats.TebiBytes,
|
||||||
|
});
|
||||||
|
expect(result).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// for GigaBytes to TeraBytes conversion (SI units)
|
||||||
|
it('should convert from GigaBytes to TeraBytes (SI)', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: 1000,
|
||||||
|
sourceUnit: DataFormats.GigaBytes,
|
||||||
|
targetUnit: DataFormats.TeraBytes,
|
||||||
|
});
|
||||||
|
expect(result).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// for GigaBytes to GibiBytes conversion (cross conversion)
|
||||||
|
it('should convert from GigaBytes to GibiBytes', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: 1,
|
||||||
|
sourceUnit: DataFormats.GigaBytes,
|
||||||
|
targetUnit: DataFormats.GibiBytes,
|
||||||
|
});
|
||||||
|
// 1 GB = 0.93132257461548 GiB approximately
|
||||||
|
expect(result).toBeCloseTo(0.93132257461548);
|
||||||
|
});
|
||||||
|
|
||||||
|
// for a large number conversion
|
||||||
|
it('should handle large number conversion from PebiBytes to BitsIEC', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: 1,
|
||||||
|
sourceUnit: DataFormats.PebiBytes,
|
||||||
|
targetUnit: DataFormats.BitsIEC,
|
||||||
|
});
|
||||||
|
expect(result).toBe(1 * 1024 ** 5 * 8); // 1 PebiByte = 2^50 Bytes = 2^53 Bits
|
||||||
|
});
|
||||||
|
|
||||||
|
// Negative value conversion
|
||||||
|
it('should handle negative values', () => {
|
||||||
|
const result = covertIntoDataFormats({
|
||||||
|
value: -1,
|
||||||
|
sourceUnit: DataFormats.KibiBytes,
|
||||||
|
targetUnit: DataFormats.BytesIEC,
|
||||||
|
});
|
||||||
|
expect(result).toBe(-1024);
|
||||||
|
});
|
||||||
|
});
|
54
frontend/src/container/FormAlertRules/ChartPreview/utils.ts
Normal file
54
frontend/src/container/FormAlertRules/ChartPreview/utils.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
BooleanFormats,
|
||||||
|
DataFormats,
|
||||||
|
DataRateFormats,
|
||||||
|
MiscellaneousFormats,
|
||||||
|
ThroughputFormats,
|
||||||
|
TimeFormats,
|
||||||
|
} from 'container/NewWidget/RightContainer/types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
dataFormatConfig,
|
||||||
|
dataRateUnitsConfig,
|
||||||
|
miscUnitsConfig,
|
||||||
|
throughputConfig,
|
||||||
|
timeUnitsConfig,
|
||||||
|
} from './config';
|
||||||
|
|
||||||
|
export function covertIntoDataFormats({
|
||||||
|
value,
|
||||||
|
sourceUnit,
|
||||||
|
targetUnit,
|
||||||
|
}: IUnit): number {
|
||||||
|
if (Object.values(BooleanFormats).includes(sourceUnit as BooleanFormats)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceMultiplier =
|
||||||
|
dataFormatConfig[sourceUnit as DataFormats] ||
|
||||||
|
timeUnitsConfig[sourceUnit as TimeFormats] ||
|
||||||
|
dataRateUnitsConfig[sourceUnit as DataRateFormats] ||
|
||||||
|
miscUnitsConfig[sourceUnit as MiscellaneousFormats] ||
|
||||||
|
throughputConfig[sourceUnit as ThroughputFormats];
|
||||||
|
|
||||||
|
const targetDivider =
|
||||||
|
dataFormatConfig[targetUnit as DataFormats] ||
|
||||||
|
timeUnitsConfig[targetUnit as TimeFormats] ||
|
||||||
|
dataRateUnitsConfig[targetUnit as DataRateFormats] ||
|
||||||
|
miscUnitsConfig[targetUnit as MiscellaneousFormats] ||
|
||||||
|
throughputConfig[sourceUnit as ThroughputFormats];
|
||||||
|
|
||||||
|
const intermediateValue = value * sourceMultiplier;
|
||||||
|
|
||||||
|
const roundedValue = Math.round(intermediateValue * 1000000) / 1000000;
|
||||||
|
|
||||||
|
const result = roundedValue / targetDivider;
|
||||||
|
|
||||||
|
return Number.isNaN(result) ? 0 : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUnit {
|
||||||
|
value: number;
|
||||||
|
sourceUnit?: string;
|
||||||
|
targetUnit?: string;
|
||||||
|
}
|
@ -1,4 +1,17 @@
|
|||||||
import { Form, InputNumber, InputNumberProps, Select, Typography } from 'antd';
|
import {
|
||||||
|
Form,
|
||||||
|
InputNumber,
|
||||||
|
InputNumberProps,
|
||||||
|
Select,
|
||||||
|
SelectProps,
|
||||||
|
Space,
|
||||||
|
Typography,
|
||||||
|
} from 'antd';
|
||||||
|
import {
|
||||||
|
getCategoryByOptionId,
|
||||||
|
getCategorySelectOptionByName,
|
||||||
|
} from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||||
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
AlertDef,
|
AlertDef,
|
||||||
@ -10,9 +23,6 @@ import { EQueryType } from 'types/common/dashboard';
|
|||||||
|
|
||||||
import { FormContainer, InlineSelect, StepHeading } from './styles';
|
import { FormContainer, InlineSelect, StepHeading } from './styles';
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
const FormItem = Form.Item;
|
|
||||||
|
|
||||||
function RuleOptions({
|
function RuleOptions({
|
||||||
alertDef,
|
alertDef,
|
||||||
setAlertDef,
|
setAlertDef,
|
||||||
@ -20,6 +30,7 @@ function RuleOptions({
|
|||||||
}: RuleOptionsProps): JSX.Element {
|
}: RuleOptionsProps): JSX.Element {
|
||||||
// init namespace for translations
|
// init namespace for translations
|
||||||
const { t } = useTranslation('alerts');
|
const { t } = useTranslation('alerts');
|
||||||
|
const { currentQuery } = useQueryBuilder();
|
||||||
|
|
||||||
const handleMatchOptChange = (value: string | unknown): void => {
|
const handleMatchOptChange = (value: string | unknown): void => {
|
||||||
const m = (value as string) || alertDef.condition?.matchType;
|
const m = (value as string) || alertDef.condition?.matchType;
|
||||||
@ -49,10 +60,10 @@ function RuleOptions({
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Option value="1">{t('option_above')}</Option>
|
<Select.Option value="1">{t('option_above')}</Select.Option>
|
||||||
<Option value="2">{t('option_below')}</Option>
|
<Select.Option value="2">{t('option_below')}</Select.Option>
|
||||||
<Option value="3">{t('option_equal')}</Option>
|
<Select.Option value="3">{t('option_equal')}</Select.Option>
|
||||||
<Option value="4">{t('option_notequal')}</Option>
|
<Select.Option value="4">{t('option_notequal')}</Select.Option>
|
||||||
</InlineSelect>
|
</InlineSelect>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -63,10 +74,10 @@ function RuleOptions({
|
|||||||
value={alertDef.condition?.matchType}
|
value={alertDef.condition?.matchType}
|
||||||
onChange={(value: string | unknown): void => handleMatchOptChange(value)}
|
onChange={(value: string | unknown): void => handleMatchOptChange(value)}
|
||||||
>
|
>
|
||||||
<Option value="1">{t('option_atleastonce')}</Option>
|
<Select.Option value="1">{t('option_atleastonce')}</Select.Option>
|
||||||
<Option value="2">{t('option_allthetimes')}</Option>
|
<Select.Option value="2">{t('option_allthetimes')}</Select.Option>
|
||||||
<Option value="3">{t('option_onaverage')}</Option>
|
<Select.Option value="3">{t('option_onaverage')}</Select.Option>
|
||||||
<Option value="4">{t('option_intotal')}</Option>
|
<Select.Option value="4">{t('option_intotal')}</Select.Option>
|
||||||
</InlineSelect>
|
</InlineSelect>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -77,7 +88,7 @@ function RuleOptions({
|
|||||||
value={alertDef.condition?.matchType}
|
value={alertDef.condition?.matchType}
|
||||||
onChange={(value: string | unknown): void => handleMatchOptChange(value)}
|
onChange={(value: string | unknown): void => handleMatchOptChange(value)}
|
||||||
>
|
>
|
||||||
<Option value="1">{t('option_atleastonce')}</Option>
|
<Select.Option value="1">{t('option_atleastonce')}</Select.Option>
|
||||||
</InlineSelect>
|
</InlineSelect>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -94,31 +105,30 @@ function RuleOptions({
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{' '}
|
<Select.Option value="5m0s">{t('option_5min')}</Select.Option>
|
||||||
<Option value="5m0s">{t('option_5min')}</Option>
|
<Select.Option value="10m0s">{t('option_10min')}</Select.Option>
|
||||||
<Option value="10m0s">{t('option_10min')}</Option>
|
<Select.Option value="15m0s">{t('option_15min')}</Select.Option>
|
||||||
<Option value="15m0s">{t('option_15min')}</Option>
|
<Select.Option value="1h0m0s">{t('option_60min')}</Select.Option>
|
||||||
<Option value="1h0m0s">{t('option_60min')}</Option>
|
<Select.Option value="4h0m0s">{t('option_4hours')}</Select.Option>
|
||||||
<Option value="4h0m0s">{t('option_4hours')}</Option>
|
<Select.Option value="24h0m0s">{t('option_24hours')}</Select.Option>
|
||||||
<Option value="24h0m0s">{t('option_24hours')}</Option>
|
|
||||||
</InlineSelect>
|
</InlineSelect>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderThresholdRuleOpts = (): JSX.Element => (
|
const renderThresholdRuleOpts = (): JSX.Element => (
|
||||||
<FormItem>
|
<Form.Item>
|
||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
{t('text_condition1')} {renderCompareOps()} {t('text_condition2')}{' '}
|
{t('text_condition1')} {renderCompareOps()} {t('text_condition2')}{' '}
|
||||||
{renderThresholdMatchOpts()} {t('text_condition3')} {renderEvalWindows()}
|
{renderThresholdMatchOpts()} {t('text_condition3')} {renderEvalWindows()}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</FormItem>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
const renderPromRuleOptions = (): JSX.Element => (
|
const renderPromRuleOptions = (): JSX.Element => (
|
||||||
<FormItem>
|
<Form.Item>
|
||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
{t('text_condition1')} {renderCompareOps()} {t('text_condition2')}{' '}
|
{t('text_condition1')} {renderCompareOps()} {t('text_condition2')}{' '}
|
||||||
{renderPromMatchOpts()}
|
{renderPromMatchOpts()}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</FormItem>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChange: InputNumberProps['onChange'] = (value): void => {
|
const onChange: InputNumberProps['onChange'] = (value): void => {
|
||||||
@ -133,6 +143,22 @@ function RuleOptions({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onChangeAlertUnit: SelectProps['onChange'] = (value) => {
|
||||||
|
setAlertDef({
|
||||||
|
...alertDef,
|
||||||
|
condition: {
|
||||||
|
...alertDef.condition,
|
||||||
|
targetUnit: value as string,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedCategory = getCategoryByOptionId(currentQuery?.unit || '');
|
||||||
|
|
||||||
|
const categorySelectOptions = getCategorySelectOptionByName(
|
||||||
|
selectedCategory?.name,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StepHeading>{t('alert_form_step2')}</StepHeading>
|
<StepHeading>{t('alert_form_step2')}</StepHeading>
|
||||||
@ -140,14 +166,29 @@ function RuleOptions({
|
|||||||
{queryCategory === EQueryType.PROM
|
{queryCategory === EQueryType.PROM
|
||||||
? renderPromRuleOptions()
|
? renderPromRuleOptions()
|
||||||
: renderThresholdRuleOpts()}
|
: renderThresholdRuleOpts()}
|
||||||
<Form.Item name={['condition', 'target']}>
|
|
||||||
<InputNumber
|
<Space align="start">
|
||||||
addonBefore={t('field_threshold')}
|
<Form.Item noStyle name={['condition', 'target']}>
|
||||||
value={alertDef?.condition?.target}
|
<InputNumber
|
||||||
onChange={onChange}
|
addonBefore={t('field_threshold')}
|
||||||
type="number"
|
value={alertDef?.condition?.target}
|
||||||
/>
|
onChange={onChange}
|
||||||
</Form.Item>
|
type="number"
|
||||||
|
onWheel={(e): void => e.currentTarget.blur()}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
showSearch
|
||||||
|
options={categorySelectOptions}
|
||||||
|
placeholder={t('field_unit')}
|
||||||
|
value={alertDef.condition.targetUnit}
|
||||||
|
onChange={onChangeAlertUnit}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Space>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,7 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import QueryTypeTag from 'container/NewWidget/LeftContainer/QueryTypeTag';
|
import QueryTypeTag from 'container/NewWidget/LeftContainer/QueryTypeTag';
|
||||||
import PlotTag from 'container/NewWidget/LeftContainer/WidgetGraph/PlotTag';
|
import PlotTag from 'container/NewWidget/LeftContainer/WidgetGraph/PlotTag';
|
||||||
|
import { BuilderUnitsFilter } from 'container/QueryBuilder/filters';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||||
import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||||
@ -39,6 +40,7 @@ import {
|
|||||||
ButtonContainer,
|
ButtonContainer,
|
||||||
MainFormContainer,
|
MainFormContainer,
|
||||||
PanelContainer,
|
PanelContainer,
|
||||||
|
StepContainer,
|
||||||
StyledLeftContainer,
|
StyledLeftContainer,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import UserGuide from './UserGuide';
|
import UserGuide from './UserGuide';
|
||||||
@ -224,6 +226,7 @@ function FormAlertRules({
|
|||||||
chQueries: mapQueryDataToApi(currentQuery.clickhouse_sql, 'name').data,
|
chQueries: mapQueryDataToApi(currentQuery.clickhouse_sql, 'name').data,
|
||||||
queryType: currentQuery.queryType,
|
queryType: currentQuery.queryType,
|
||||||
panelType: initQuery.panelType,
|
panelType: initQuery.panelType,
|
||||||
|
unit: currentQuery.unit,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -360,9 +363,9 @@ function FormAlertRules({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
name=""
|
name=""
|
||||||
threshold={alertDef.condition?.target}
|
|
||||||
query={stagedQuery}
|
query={stagedQuery}
|
||||||
selectedInterval={toChartInterval(alertDef.evalWindow)}
|
selectedInterval={toChartInterval(alertDef.evalWindow)}
|
||||||
|
alertDef={alertDef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -375,8 +378,8 @@ function FormAlertRules({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
name="Chart Preview"
|
name="Chart Preview"
|
||||||
threshold={alertDef.condition?.target}
|
|
||||||
query={stagedQuery}
|
query={stagedQuery}
|
||||||
|
alertDef={alertDef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -389,8 +392,8 @@ function FormAlertRules({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
name="Chart Preview"
|
name="Chart Preview"
|
||||||
threshold={alertDef.condition?.target}
|
|
||||||
query={stagedQuery}
|
query={stagedQuery}
|
||||||
|
alertDef={alertDef}
|
||||||
selectedInterval={toChartInterval(alertDef.evalWindow)}
|
selectedInterval={toChartInterval(alertDef.evalWindow)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -404,9 +407,21 @@ function FormAlertRules({
|
|||||||
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
||||||
alertType !== AlertTypes.METRICS_BASED_ALERT;
|
alertType !== AlertTypes.METRICS_BASED_ALERT;
|
||||||
|
|
||||||
|
const onUnitChangeHandler = (): void => {
|
||||||
|
// reset target unit
|
||||||
|
setAlertDef((def) => ({
|
||||||
|
...def,
|
||||||
|
condition: {
|
||||||
|
...def.condition,
|
||||||
|
targetUnit: undefined,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Element}
|
{Element}
|
||||||
|
|
||||||
<PanelContainer>
|
<PanelContainer>
|
||||||
<StyledLeftContainer flex="5 1 600px" md={18}>
|
<StyledLeftContainer flex="5 1 600px" md={18}>
|
||||||
<MainFormContainer
|
<MainFormContainer
|
||||||
@ -419,6 +434,11 @@ function FormAlertRules({
|
|||||||
{currentQuery.queryType === EQueryType.PROM && renderPromChartPreview()}
|
{currentQuery.queryType === EQueryType.PROM && renderPromChartPreview()}
|
||||||
{currentQuery.queryType === EQueryType.CLICKHOUSE &&
|
{currentQuery.queryType === EQueryType.CLICKHOUSE &&
|
||||||
renderChQueryChartPreview()}
|
renderChQueryChartPreview()}
|
||||||
|
|
||||||
|
<StepContainer>
|
||||||
|
<BuilderUnitsFilter onChange={onUnitChangeHandler} />
|
||||||
|
</StepContainer>
|
||||||
|
|
||||||
<QuerySection
|
<QuerySection
|
||||||
queryCategory={currentQuery.queryType}
|
queryCategory={currentQuery.queryType}
|
||||||
setQueryCategory={onQueryCategoryChange}
|
setQueryCategory={onQueryCategoryChange}
|
||||||
|
@ -89,3 +89,7 @@ export const FormItemMedium = styled(Item)`
|
|||||||
export const ChannelSelectTip = styled(Typography.Text)`
|
export const ChannelSelectTip = styled(Typography.Text)`
|
||||||
color: hsla(0, 0%, 100%, 0.3);
|
color: hsla(0, 0%, 100%, 0.3);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const StepContainer = styled.div`
|
||||||
|
margin-top: 2rem;
|
||||||
|
`;
|
||||||
|
@ -53,6 +53,7 @@ function WidgetGraphComponent({
|
|||||||
setLayout,
|
setLayout,
|
||||||
onDragSelect,
|
onDragSelect,
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
|
threshold,
|
||||||
headerMenuList,
|
headerMenuList,
|
||||||
}: WidgetGraphComponentProps): JSX.Element {
|
}: WidgetGraphComponentProps): JSX.Element {
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
const [deleteModal, setDeleteModal] = useState(false);
|
||||||
@ -279,6 +280,7 @@ function WidgetGraphComponent({
|
|||||||
onClone={onCloneHandler}
|
onClone={onCloneHandler}
|
||||||
queryResponse={queryResponse}
|
queryResponse={queryResponse}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
|
threshold={threshold}
|
||||||
headerMenuList={headerMenuList}
|
headerMenuList={headerMenuList}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,6 +29,7 @@ function GridCardGraph({
|
|||||||
onClickHandler,
|
onClickHandler,
|
||||||
headerMenuList = [MenuItemKeys.View],
|
headerMenuList = [MenuItemKeys.View],
|
||||||
isQueryEnabled,
|
isQueryEnabled,
|
||||||
|
threshold,
|
||||||
}: GridCardGraphProps): JSX.Element {
|
}: GridCardGraphProps): JSX.Element {
|
||||||
const { isAddWidget } = useSelector<AppState, DashboardReducer>(
|
const { isAddWidget } = useSelector<AppState, DashboardReducer>(
|
||||||
(state) => state.dashboards,
|
(state) => state.dashboards,
|
||||||
@ -70,11 +71,12 @@ function GridCardGraph({
|
|||||||
{
|
{
|
||||||
queryKey: [
|
queryKey: [
|
||||||
`GetMetricsQueryRange-${widget?.timePreferance}-${globalSelectedInterval}-${widget?.id}`,
|
`GetMetricsQueryRange-${widget?.timePreferance}-${globalSelectedInterval}-${widget?.id}`,
|
||||||
widget,
|
|
||||||
maxTime,
|
maxTime,
|
||||||
minTime,
|
minTime,
|
||||||
globalSelectedInterval,
|
globalSelectedInterval,
|
||||||
variables,
|
variables,
|
||||||
|
widget?.query,
|
||||||
|
widget?.panelTypes,
|
||||||
],
|
],
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
enabled: isGraphVisible && !isEmptyWidget && isQueryEnabled && !isAddWidget,
|
enabled: isGraphVisible && !isEmptyWidget && isQueryEnabled && !isAddWidget,
|
||||||
@ -101,11 +103,11 @@ function GridCardGraph({
|
|||||||
|
|
||||||
const isEmptyLayout = widget?.id === 'empty' || isEmpty(widget);
|
const isEmptyLayout = widget?.id === 'empty' || isEmpty(widget);
|
||||||
|
|
||||||
if (queryResponse.isRefetching) {
|
if (queryResponse.isRefetching || queryResponse.isLoading) {
|
||||||
return <Spinner height="20vh" tip="Loading..." />;
|
return <Spinner height="20vh" tip="Loading..." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryResponse.isError && !isEmptyLayout) {
|
if ((queryResponse.isError && !isEmptyLayout) || !isQueryEnabled) {
|
||||||
return (
|
return (
|
||||||
<span ref={graphRef}>
|
<span ref={graphRef}>
|
||||||
{!isEmpty(widget) && prevChartDataSetRef && (
|
{!isEmpty(widget) && prevChartDataSetRef && (
|
||||||
@ -120,6 +122,7 @@ function GridCardGraph({
|
|||||||
yAxisUnit={yAxisUnit}
|
yAxisUnit={yAxisUnit}
|
||||||
layout={layout}
|
layout={layout}
|
||||||
setLayout={setLayout}
|
setLayout={setLayout}
|
||||||
|
threshold={threshold}
|
||||||
headerMenuList={headerMenuList}
|
headerMenuList={headerMenuList}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -127,27 +130,24 @@ function GridCardGraph({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queryResponse.status === 'loading' || queryResponse.status === 'idle') {
|
if (!isEmpty(widget) && prevChartDataSetRef?.labels) {
|
||||||
return (
|
return (
|
||||||
<span ref={graphRef}>
|
<span ref={graphRef}>
|
||||||
{!isEmpty(widget) && prevChartDataSetRef?.labels ? (
|
<WidgetGraphComponent
|
||||||
<WidgetGraphComponent
|
enableModel={false}
|
||||||
enableModel={false}
|
enableWidgetHeader
|
||||||
enableWidgetHeader
|
widget={widget}
|
||||||
widget={widget}
|
queryResponse={queryResponse}
|
||||||
queryResponse={queryResponse}
|
errorMessage={errorMessage}
|
||||||
errorMessage={errorMessage}
|
data={prevChartDataSetRef}
|
||||||
data={prevChartDataSetRef}
|
name={name}
|
||||||
name={name}
|
yAxisUnit={yAxisUnit}
|
||||||
yAxisUnit={yAxisUnit}
|
layout={layout}
|
||||||
layout={layout}
|
setLayout={setLayout}
|
||||||
setLayout={setLayout}
|
threshold={threshold}
|
||||||
headerMenuList={headerMenuList}
|
headerMenuList={headerMenuList}
|
||||||
onClickHandler={onClickHandler}
|
onClickHandler={onClickHandler}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<Spinner height="20vh" tip="Loading..." />
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -165,6 +165,7 @@ function GridCardGraph({
|
|||||||
name={name}
|
name={name}
|
||||||
yAxisUnit={yAxisUnit}
|
yAxisUnit={yAxisUnit}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
|
threshold={threshold}
|
||||||
headerMenuList={headerMenuList}
|
headerMenuList={headerMenuList}
|
||||||
onClickHandler={onClickHandler}
|
onClickHandler={onClickHandler}
|
||||||
/>
|
/>
|
||||||
@ -179,6 +180,7 @@ GridCardGraph.defaultProps = {
|
|||||||
onDragSelect: undefined,
|
onDragSelect: undefined,
|
||||||
onClickHandler: undefined,
|
onClickHandler: undefined,
|
||||||
isQueryEnabled: true,
|
isQueryEnabled: true,
|
||||||
|
threshold: undefined,
|
||||||
headerMenuList: [MenuItemKeys.View],
|
headerMenuList: [MenuItemKeys.View],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ChartData } from 'chart.js';
|
import { ChartData } from 'chart.js';
|
||||||
import { GraphOnClickHandler, ToggleGraphProps } from 'components/Graph/types';
|
import { GraphOnClickHandler, ToggleGraphProps } from 'components/Graph/types';
|
||||||
import { Dispatch, MutableRefObject, SetStateAction } from 'react';
|
import { Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react';
|
||||||
import { Layout } from 'react-grid-layout';
|
import { Layout } from 'react-grid-layout';
|
||||||
import { UseQueryResult } from 'react-query';
|
import { UseQueryResult } from 'react-query';
|
||||||
import { DeleteWidgetProps } from 'store/actions/dashboard/deleteWidget';
|
import { DeleteWidgetProps } from 'store/actions/dashboard/deleteWidget';
|
||||||
@ -39,6 +39,7 @@ export interface WidgetGraphComponentProps extends DispatchProps {
|
|||||||
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
|
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
|
||||||
onDragSelect?: (start: number, end: number) => void;
|
onDragSelect?: (start: number, end: number) => void;
|
||||||
onClickHandler?: GraphOnClickHandler;
|
onClickHandler?: GraphOnClickHandler;
|
||||||
|
threshold?: ReactNode;
|
||||||
headerMenuList: MenuItemKeys[];
|
headerMenuList: MenuItemKeys[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ export interface GridCardGraphProps {
|
|||||||
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
|
setLayout?: Dispatch<SetStateAction<LayoutProps[]>>;
|
||||||
onDragSelect?: (start: number, end: number) => void;
|
onDragSelect?: (start: number, end: number) => void;
|
||||||
onClickHandler?: GraphOnClickHandler;
|
onClickHandler?: GraphOnClickHandler;
|
||||||
|
threshold?: ReactNode;
|
||||||
headerMenuList?: WidgetGraphComponentProps['headerMenuList'];
|
headerMenuList?: WidgetGraphComponentProps['headerMenuList'];
|
||||||
isQueryEnabled: boolean;
|
isQueryEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DisplayThresholdContainer,
|
||||||
|
TypographHeading,
|
||||||
|
Typography,
|
||||||
|
} from './styles';
|
||||||
|
import { DisplayThresholdProps } from './types';
|
||||||
|
|
||||||
|
function DisplayThreshold({ threshold }: DisplayThresholdProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<DisplayThresholdContainer>
|
||||||
|
<TypographHeading>Threshold </TypographHeading>
|
||||||
|
<Typography>{threshold || <InfoCircleOutlined />}</Typography>
|
||||||
|
</DisplayThresholdContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DisplayThreshold;
|
@ -12,7 +12,7 @@ import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { ReactNode, useCallback, useMemo, useState } from 'react';
|
||||||
import { UseQueryResult } from 'react-query';
|
import { UseQueryResult } from 'react-query';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
@ -32,12 +32,14 @@ import {
|
|||||||
ArrowContainer,
|
ArrowContainer,
|
||||||
HeaderContainer,
|
HeaderContainer,
|
||||||
HeaderContentContainer,
|
HeaderContentContainer,
|
||||||
|
ThesholdContainer,
|
||||||
|
WidgetHeaderContainer,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import { MenuItem } from './types';
|
import { MenuItem } from './types';
|
||||||
import { generateMenuList, isTWidgetOptions } from './utils';
|
import { generateMenuList, isTWidgetOptions } from './utils';
|
||||||
|
|
||||||
interface IWidgetHeaderProps {
|
interface IWidgetHeaderProps {
|
||||||
title: string;
|
title: ReactNode;
|
||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
onView: VoidFunction;
|
onView: VoidFunction;
|
||||||
onDelete?: VoidFunction;
|
onDelete?: VoidFunction;
|
||||||
@ -47,6 +49,7 @@ interface IWidgetHeaderProps {
|
|||||||
SuccessResponse<MetricRangePayloadProps> | ErrorResponse
|
SuccessResponse<MetricRangePayloadProps> | ErrorResponse
|
||||||
>;
|
>;
|
||||||
errorMessage: string | undefined;
|
errorMessage: string | undefined;
|
||||||
|
threshold?: ReactNode;
|
||||||
headerMenuList?: MenuItemKeys[];
|
headerMenuList?: MenuItemKeys[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +62,7 @@ function WidgetHeader({
|
|||||||
parentHover,
|
parentHover,
|
||||||
queryResponse,
|
queryResponse,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
|
threshold,
|
||||||
headerMenuList,
|
headerMenuList,
|
||||||
}: IWidgetHeaderProps): JSX.Element {
|
}: IWidgetHeaderProps): JSX.Element {
|
||||||
const [localHover, setLocalHover] = useState(false);
|
const [localHover, setLocalHover] = useState(false);
|
||||||
@ -171,7 +175,7 @@ function WidgetHeader({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<WidgetHeaderContainer>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
destroyPopupOnHide
|
destroyPopupOnHide
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
@ -196,6 +200,7 @@ function WidgetHeader({
|
|||||||
</HeaderContentContainer>
|
</HeaderContentContainer>
|
||||||
</HeaderContainer>
|
</HeaderContainer>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
<ThesholdContainer>{threshold}</ThesholdContainer>
|
||||||
{queryResponse.isFetching && !queryResponse.isError && (
|
{queryResponse.isFetching && !queryResponse.isError && (
|
||||||
<Spinner height="5vh" style={spinnerStyles} />
|
<Spinner height="5vh" style={spinnerStyles} />
|
||||||
)}
|
)}
|
||||||
@ -204,13 +209,14 @@ function WidgetHeader({
|
|||||||
<ExclamationCircleOutlined style={tooltipStyles} />
|
<ExclamationCircleOutlined style={tooltipStyles} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</WidgetHeaderContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetHeader.defaultProps = {
|
WidgetHeader.defaultProps = {
|
||||||
onDelete: undefined,
|
onDelete: undefined,
|
||||||
onClone: undefined,
|
onClone: undefined,
|
||||||
|
threshold: undefined,
|
||||||
headerMenuList: [MenuItemKeys.View],
|
headerMenuList: [MenuItemKeys.View],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { grey } from '@ant-design/colors';
|
import { grey } from '@ant-design/colors';
|
||||||
|
import { Typography as TypographyComponent } from 'antd';
|
||||||
|
import { themeColors } from 'constants/theme';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const HeaderContainer = styled.div<{ hover: boolean }>`
|
export const HeaderContainer = styled.div<{ hover: boolean }>`
|
||||||
@ -24,3 +26,34 @@ export const ArrowContainer = styled.span<{ hover: boolean }>`
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: -1rem;
|
right: -1rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ThesholdContainer = styled.span`
|
||||||
|
margin-top: -0.3rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DisplayThresholdContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: auto;
|
||||||
|
justify-content: space-between;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const WidgetHeaderContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Typography = styled(TypographyComponent)`
|
||||||
|
&&& {
|
||||||
|
color: ${themeColors.white};
|
||||||
|
width: auto;
|
||||||
|
margin-left: 0.2rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const TypographHeading = styled(TypographyComponent)`
|
||||||
|
&&& {
|
||||||
|
color: ${grey[2]};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
@ -10,3 +10,7 @@ export interface MenuItem {
|
|||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
danger?: boolean;
|
danger?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DisplayThresholdProps {
|
||||||
|
threshold: ReactNode;
|
||||||
|
}
|
||||||
|
@ -38,6 +38,7 @@ export const UpdateDashboard = async (
|
|||||||
query: widgetData?.query || initialQueriesMap.metrics,
|
query: widgetData?.query || initialQueriesMap.metrics,
|
||||||
timePreferance: widgetData?.timePreferance || 'GLOBAL_TIME',
|
timePreferance: widgetData?.timePreferance || 'GLOBAL_TIME',
|
||||||
title: widgetData ? copyTitle : '',
|
title: widgetData ? copyTitle : '',
|
||||||
|
yAxisUnit: widgetData?.yAxisUnit,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
layout,
|
layout,
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
} from 'components/Graph/types';
|
} from 'components/Graph/types';
|
||||||
import { GridTableComponentProps } from 'container/GridTableComponent/types';
|
import { GridTableComponentProps } from 'container/GridTableComponent/types';
|
||||||
import { GridValueComponentProps } from 'container/GridValueComponent/types';
|
import { GridValueComponentProps } from 'container/GridValueComponent/types';
|
||||||
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ import { PANEL_TYPES } from '../../constants/queryBuilder';
|
|||||||
export type GridPanelSwitchProps = {
|
export type GridPanelSwitchProps = {
|
||||||
panelType: PANEL_TYPES;
|
panelType: PANEL_TYPES;
|
||||||
data: ChartData;
|
data: ChartData;
|
||||||
title?: string;
|
title?: Widgets['title'];
|
||||||
opacity?: string;
|
opacity?: string;
|
||||||
isStacked?: boolean;
|
isStacked?: boolean;
|
||||||
onClickHandler?: GraphOnClickHandler;
|
onClickHandler?: GraphOnClickHandler;
|
||||||
|
12
frontend/src/container/GridPanelSwitch/utils.ts
Normal file
12
frontend/src/container/GridPanelSwitch/utils.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import React, { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export const generateGridTitle = (title: ReactNode): string => {
|
||||||
|
if (React.isValidElement(title)) {
|
||||||
|
return Array.isArray(title.props.children)
|
||||||
|
? title.props.children
|
||||||
|
.map((child: ReactNode) => (typeof child === 'string' ? child : ''))
|
||||||
|
.join(' ')
|
||||||
|
: title.props.children;
|
||||||
|
}
|
||||||
|
return title?.toString() || '';
|
||||||
|
};
|
@ -1,7 +1,8 @@
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||||
import ValueGraph from 'components/ValueGraph';
|
import ValueGraph from 'components/ValueGraph';
|
||||||
import { memo } from 'react';
|
import { generateGridTitle } from 'container/GridPanelSwitch/utils';
|
||||||
|
import { memo, useMemo } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import { TitleContainer, ValueContainer } from './styles';
|
import { TitleContainer, ValueContainer } from './styles';
|
||||||
@ -15,6 +16,7 @@ function GridValueComponent({
|
|||||||
const value = (((data.datasets[0] || []).data || [])[0] || 0) as number;
|
const value = (((data.datasets[0] || []).data || [])[0] || 0) as number;
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const gridTitle = useMemo(() => generateGridTitle(title), [title]);
|
||||||
|
|
||||||
const isDashboardPage = location.pathname.split('/').length === 3;
|
const isDashboardPage = location.pathname.split('/').length === 3;
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ function GridValueComponent({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TitleContainer isDashboardPage={isDashboardPage}>
|
<TitleContainer isDashboardPage={isDashboardPage}>
|
||||||
<Typography>{title}</Typography>
|
<Typography>{gridTitle}</Typography>
|
||||||
</TitleContainer>
|
</TitleContainer>
|
||||||
<ValueContainer isDashboardPage={isDashboardPage}>
|
<ValueContainer isDashboardPage={isDashboardPage}>
|
||||||
<ValueGraph
|
<ValueGraph
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { ChartData } from 'chart.js';
|
import { ChartData } from 'chart.js';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
export type GridValueComponentProps = {
|
export type GridValueComponentProps = {
|
||||||
data: ChartData;
|
data: ChartData;
|
||||||
title?: string;
|
title?: ReactNode;
|
||||||
yAxisUnit?: string;
|
yAxisUnit?: string;
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,13 @@ import {
|
|||||||
QUERYNAME_AND_EXPRESSION,
|
QUERYNAME_AND_EXPRESSION,
|
||||||
WidgetKeys,
|
WidgetKeys,
|
||||||
} from '../constant';
|
} from '../constant';
|
||||||
import { LatencyProps, OperationPerSecProps } from '../Tabs/types';
|
import {
|
||||||
|
ApDexMetricsQueryBuilderQueriesProps,
|
||||||
|
ApDexProps,
|
||||||
|
LatencyProps,
|
||||||
|
OperationPerSecProps,
|
||||||
|
} from '../Tabs/types';
|
||||||
|
import { convertMilSecToNanoSec, getNearestHighestBucketValue } from '../utils';
|
||||||
import {
|
import {
|
||||||
getQueryBuilderQueries,
|
getQueryBuilderQueries,
|
||||||
getQueryBuilderQuerieswithFormula,
|
getQueryBuilderQuerieswithFormula,
|
||||||
@ -85,6 +91,365 @@ export const latency = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const apDexTracesQueryBuilderQueries = ({
|
||||||
|
servicename,
|
||||||
|
tagFilterItems,
|
||||||
|
topLevelOperationsRoute,
|
||||||
|
threashold,
|
||||||
|
}: ApDexProps): QueryBuilderData => {
|
||||||
|
const autoCompleteDataA: BaseAutocompleteData = {
|
||||||
|
dataType: DataType.FLOAT64,
|
||||||
|
isColumn: true,
|
||||||
|
key: '',
|
||||||
|
type: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const autoCompleteDataB: BaseAutocompleteData = {
|
||||||
|
dataType: DataType.FLOAT64,
|
||||||
|
isColumn: true,
|
||||||
|
key: '',
|
||||||
|
type: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const autoCompleteDataC: BaseAutocompleteData = {
|
||||||
|
dataType: DataType.FLOAT64,
|
||||||
|
isColumn: true,
|
||||||
|
key: '',
|
||||||
|
type: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterItemA: TagFilterItem[] = [
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.ServiceName,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: servicename,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Name,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS.IN,
|
||||||
|
value: [...topLevelOperationsRoute],
|
||||||
|
},
|
||||||
|
...tagFilterItems,
|
||||||
|
];
|
||||||
|
|
||||||
|
const filterItemB: TagFilterItem[] = [
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.HasError,
|
||||||
|
dataType: DataType.BOOL,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.DurationNano,
|
||||||
|
dataType: DataType.FLOAT64,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['<='],
|
||||||
|
value: convertMilSecToNanoSec(threashold),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.ServiceName,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: servicename,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Name,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS.IN,
|
||||||
|
value: [...topLevelOperationsRoute],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const filterItemC: TagFilterItem[] = [
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.DurationNano,
|
||||||
|
dataType: DataType.FLOAT64,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['<='],
|
||||||
|
value: convertMilSecToNanoSec(threashold * 4),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.HasError,
|
||||||
|
dataType: DataType.BOOL,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.ServiceName,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: servicename,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Name,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: true,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS.IN,
|
||||||
|
value: [...topLevelOperationsRoute],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const autocompleteData = [
|
||||||
|
autoCompleteDataA,
|
||||||
|
autoCompleteDataB,
|
||||||
|
autoCompleteDataC,
|
||||||
|
];
|
||||||
|
const additionalItems = [filterItemA, filterItemB, filterItemC];
|
||||||
|
const legends = [GraphTitle.APDEX];
|
||||||
|
const disabled = Array(3).fill(true);
|
||||||
|
const expressions = [FORMULA.APDEX_TRACES];
|
||||||
|
const legendFormulas = [GraphTitle.APDEX];
|
||||||
|
const aggregateOperators = [
|
||||||
|
MetricAggregateOperator.COUNT,
|
||||||
|
MetricAggregateOperator.COUNT,
|
||||||
|
MetricAggregateOperator.COUNT,
|
||||||
|
];
|
||||||
|
const dataSource = DataSource.TRACES;
|
||||||
|
|
||||||
|
return getQueryBuilderQuerieswithFormula({
|
||||||
|
autocompleteData,
|
||||||
|
additionalItems,
|
||||||
|
legends,
|
||||||
|
disabled,
|
||||||
|
expressions,
|
||||||
|
legendFormulas,
|
||||||
|
aggregateOperators,
|
||||||
|
dataSource,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const apDexMetricsQueryBuilderQueries = ({
|
||||||
|
servicename,
|
||||||
|
tagFilterItems,
|
||||||
|
topLevelOperationsRoute,
|
||||||
|
threashold,
|
||||||
|
delta,
|
||||||
|
metricsBuckets,
|
||||||
|
}: ApDexMetricsQueryBuilderQueriesProps): QueryBuilderData => {
|
||||||
|
const autoCompleteDataA: BaseAutocompleteData = {
|
||||||
|
key: WidgetKeys.SignozLatencyCount,
|
||||||
|
dataType: DataType.FLOAT64,
|
||||||
|
isColumn: true,
|
||||||
|
type: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const autoCompleteDataB: BaseAutocompleteData = {
|
||||||
|
key: WidgetKeys.Signoz_latency_bucket,
|
||||||
|
dataType: DataType.FLOAT64,
|
||||||
|
isColumn: true,
|
||||||
|
type: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const autoCompleteDataC: BaseAutocompleteData = {
|
||||||
|
key: WidgetKeys.Signoz_latency_bucket,
|
||||||
|
dataType: DataType.FLOAT64,
|
||||||
|
isColumn: true,
|
||||||
|
type: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterItemA: TagFilterItem[] = [
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Service_name,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: servicename,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Operation,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS.IN,
|
||||||
|
value: [...topLevelOperationsRoute],
|
||||||
|
},
|
||||||
|
...tagFilterItems,
|
||||||
|
];
|
||||||
|
|
||||||
|
const filterItemB: TagFilterItem[] = [
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.StatusCode,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: 'STATUS_CODE_UNSET',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Le,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: getNearestHighestBucketValue(threashold * 1000, metricsBuckets),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Service_name,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: servicename,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Operation,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS.IN,
|
||||||
|
value: [...topLevelOperationsRoute],
|
||||||
|
},
|
||||||
|
...tagFilterItems,
|
||||||
|
];
|
||||||
|
|
||||||
|
const filterItemC: TagFilterItem[] = [
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Le,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: getNearestHighestBucketValue(threashold * 1000 * 4, metricsBuckets),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.StatusCode,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: 'STATUS_CODE_UNSET',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Service_name,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS['='],
|
||||||
|
value: servicename,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
key: {
|
||||||
|
key: WidgetKeys.Operation,
|
||||||
|
dataType: DataType.STRING,
|
||||||
|
isColumn: false,
|
||||||
|
type: MetricsType.Tag,
|
||||||
|
},
|
||||||
|
op: OPERATORS.IN,
|
||||||
|
value: [...topLevelOperationsRoute],
|
||||||
|
},
|
||||||
|
...tagFilterItems,
|
||||||
|
];
|
||||||
|
|
||||||
|
const autocompleteData = [
|
||||||
|
autoCompleteDataA,
|
||||||
|
autoCompleteDataB,
|
||||||
|
autoCompleteDataC,
|
||||||
|
];
|
||||||
|
|
||||||
|
const additionalItems = [filterItemA, filterItemB, filterItemC];
|
||||||
|
const legends = [GraphTitle.APDEX];
|
||||||
|
const disabled = Array(3).fill(true);
|
||||||
|
const expressions = delta
|
||||||
|
? [FORMULA.APDEX_DELTA_SPAN_METRICS]
|
||||||
|
: [FORMULA.APDEX_CUMULATIVE_SPAN_METRICS];
|
||||||
|
const legendFormulas = [GraphTitle.APDEX];
|
||||||
|
const aggregateOperators = [
|
||||||
|
MetricAggregateOperator.SUM_RATE,
|
||||||
|
MetricAggregateOperator.SUM_RATE,
|
||||||
|
MetricAggregateOperator.SUM_RATE,
|
||||||
|
];
|
||||||
|
const dataSource = DataSource.METRICS;
|
||||||
|
|
||||||
|
return getQueryBuilderQuerieswithFormula({
|
||||||
|
autocompleteData,
|
||||||
|
additionalItems,
|
||||||
|
legends,
|
||||||
|
disabled,
|
||||||
|
expressions,
|
||||||
|
legendFormulas,
|
||||||
|
aggregateOperators,
|
||||||
|
dataSource,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const operationPerSec = ({
|
export const operationPerSec = ({
|
||||||
servicename,
|
servicename,
|
||||||
tagFilterItems,
|
tagFilterItems,
|
||||||
|
@ -32,7 +32,14 @@ import {
|
|||||||
errorPercentage,
|
errorPercentage,
|
||||||
operationPerSec,
|
operationPerSec,
|
||||||
} from '../MetricsPageQueries/OverviewQueries';
|
} from '../MetricsPageQueries/OverviewQueries';
|
||||||
import { Card, Col, Row } from '../styles';
|
import {
|
||||||
|
Card,
|
||||||
|
Col,
|
||||||
|
ColApDexContainer,
|
||||||
|
ColErrorContainer,
|
||||||
|
Row,
|
||||||
|
} from '../styles';
|
||||||
|
import ApDex from './Overview/ApDex';
|
||||||
import ServiceOverview from './Overview/ServiceOverview';
|
import ServiceOverview from './Overview/ServiceOverview';
|
||||||
import TopLevelOperation from './Overview/TopLevelOperations';
|
import TopLevelOperation from './Overview/TopLevelOperations';
|
||||||
import TopOperation from './Overview/TopOperation';
|
import TopOperation from './Overview/TopOperation';
|
||||||
@ -160,7 +167,7 @@ function Application(): JSX.Element {
|
|||||||
[dispatch],
|
[dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onErrorTrackHandler = (timestamp: number): void => {
|
const onErrorTrackHandler = (timestamp: number): (() => void) => (): void => {
|
||||||
const currentTime = timestamp;
|
const currentTime = timestamp;
|
||||||
const tPlusOne = timestamp + 60 * 1000;
|
const tPlusOne = timestamp + 60 * 1000;
|
||||||
|
|
||||||
@ -190,6 +197,7 @@ function Application(): JSX.Element {
|
|||||||
selectedTimeStamp={selectedTimeStamp}
|
selectedTimeStamp={selectedTimeStamp}
|
||||||
selectedTraceTags={selectedTraceTags}
|
selectedTraceTags={selectedTraceTags}
|
||||||
topLevelOperationsRoute={topLevelOperationsRoute}
|
topLevelOperationsRoute={topLevelOperationsRoute}
|
||||||
|
topLevelOperationsLoading={topLevelOperationsLoading}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
@ -221,28 +229,48 @@ function Application(): JSX.Element {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Button
|
<ColApDexContainer>
|
||||||
type="default"
|
<Button
|
||||||
size="small"
|
type="default"
|
||||||
id="Error_button"
|
size="small"
|
||||||
onClick={(): void => {
|
id="ApDex_button"
|
||||||
onErrorTrackHandler(selectedTimeStamp);
|
onClick={onViewTracePopupClick({
|
||||||
}}
|
servicename,
|
||||||
>
|
selectedTraceTags,
|
||||||
View Traces
|
timestamp: selectedTimeStamp,
|
||||||
</Button>
|
})}
|
||||||
|
>
|
||||||
|
View Traces
|
||||||
|
</Button>
|
||||||
|
<ApDex
|
||||||
|
handleGraphClick={handleGraphClick}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
topLevelOperationsRoute={topLevelOperationsRoute}
|
||||||
|
tagFilterItems={tagFilterItems}
|
||||||
|
/>
|
||||||
|
</ColApDexContainer>
|
||||||
|
<ColErrorContainer>
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
id="Error_button"
|
||||||
|
onClick={onErrorTrackHandler(selectedTimeStamp)}
|
||||||
|
>
|
||||||
|
View Traces
|
||||||
|
</Button>
|
||||||
|
|
||||||
<TopLevelOperation
|
<TopLevelOperation
|
||||||
handleGraphClick={handleGraphClick}
|
handleGraphClick={handleGraphClick}
|
||||||
onDragSelect={onDragSelect}
|
onDragSelect={onDragSelect}
|
||||||
topLevelOperationsError={topLevelOperationsError}
|
topLevelOperationsError={topLevelOperationsError}
|
||||||
topLevelOperationsLoading={topLevelOperationsLoading}
|
topLevelOperationsLoading={topLevelOperationsLoading}
|
||||||
topLevelOperationsIsError={topLevelOperationsIsError}
|
topLevelOperationsIsError={topLevelOperationsIsError}
|
||||||
name="error_percentage_%"
|
name="error_percentage_%"
|
||||||
widget={errorPercentageWidget}
|
widget={errorPercentageWidget}
|
||||||
yAxisUnit="%"
|
yAxisUnit="%"
|
||||||
opName="Error"
|
opName="Error"
|
||||||
/>
|
/>
|
||||||
|
</ColErrorContainer>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
import { Space, Typography } from 'antd';
|
||||||
|
import TextToolTip from 'components/TextToolTip';
|
||||||
|
import {
|
||||||
|
apDexToolTipText,
|
||||||
|
apDexToolTipUrl,
|
||||||
|
apDexToolTipUrlText,
|
||||||
|
} from 'constants/apDex';
|
||||||
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import Graph from 'container/GridGraphLayout/Graph';
|
||||||
|
import DisplayThreshold from 'container/GridGraphLayout/WidgetHeader/DisplayThreshold';
|
||||||
|
import { GraphTitle } from 'container/MetricsApplication/constant';
|
||||||
|
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||||
|
import { apDexMetricsQueryBuilderQueries } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
|
||||||
|
import { ReactNode, useMemo } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { IServiceName } from '../../types';
|
||||||
|
import { ApDexMetricsProps } from './types';
|
||||||
|
|
||||||
|
function ApDexMetrics({
|
||||||
|
delta,
|
||||||
|
metricsBuckets,
|
||||||
|
thresholdValue,
|
||||||
|
onDragSelect,
|
||||||
|
tagFilterItems,
|
||||||
|
topLevelOperationsRoute,
|
||||||
|
handleGraphClick,
|
||||||
|
}: ApDexMetricsProps): JSX.Element {
|
||||||
|
const { servicename } = useParams<IServiceName>();
|
||||||
|
|
||||||
|
const apDexMetricsWidget = useMemo(
|
||||||
|
() =>
|
||||||
|
getWidgetQueryBuilder({
|
||||||
|
query: {
|
||||||
|
queryType: EQueryType.QUERY_BUILDER,
|
||||||
|
promql: [],
|
||||||
|
builder: apDexMetricsQueryBuilderQueries({
|
||||||
|
servicename,
|
||||||
|
tagFilterItems,
|
||||||
|
topLevelOperationsRoute,
|
||||||
|
threashold: thresholdValue || 0,
|
||||||
|
delta: delta || false,
|
||||||
|
metricsBuckets: metricsBuckets || [],
|
||||||
|
}),
|
||||||
|
clickhouse_sql: [],
|
||||||
|
id: uuid(),
|
||||||
|
},
|
||||||
|
title: (
|
||||||
|
<Space>
|
||||||
|
<Typography>{GraphTitle.APDEX}</Typography>
|
||||||
|
<TextToolTip
|
||||||
|
text={apDexToolTipText}
|
||||||
|
url={apDexToolTipUrl}
|
||||||
|
useFilledIcon={false}
|
||||||
|
urlText={apDexToolTipUrlText}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
delta,
|
||||||
|
metricsBuckets,
|
||||||
|
servicename,
|
||||||
|
tagFilterItems,
|
||||||
|
thresholdValue,
|
||||||
|
topLevelOperationsRoute,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const threshold: ReactNode = useMemo(() => {
|
||||||
|
if (thresholdValue) return <DisplayThreshold threshold={thresholdValue} />;
|
||||||
|
return null;
|
||||||
|
}, [thresholdValue]);
|
||||||
|
|
||||||
|
const isQueryEnabled =
|
||||||
|
topLevelOperationsRoute.length > 0 &&
|
||||||
|
metricsBuckets &&
|
||||||
|
metricsBuckets?.length > 0 &&
|
||||||
|
delta !== undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Graph
|
||||||
|
name="apdex"
|
||||||
|
widget={apDexMetricsWidget}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
onClickHandler={handleGraphClick('ApDex')}
|
||||||
|
yAxisUnit=""
|
||||||
|
threshold={threshold}
|
||||||
|
isQueryEnabled={isQueryEnabled}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApDexMetrics.defaultProps = {
|
||||||
|
delta: undefined,
|
||||||
|
le: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ApDexMetrics;
|
@ -0,0 +1,36 @@
|
|||||||
|
import Spinner from 'components/Spinner';
|
||||||
|
import { useGetMetricMeta } from 'hooks/apDex/useGetMetricMeta';
|
||||||
|
import useErrorNotification from 'hooks/useErrorNotification';
|
||||||
|
|
||||||
|
import ApDexMetrics from './ApDexMetrics';
|
||||||
|
import { metricMeta } from './constants';
|
||||||
|
import { ApDexDataSwitcherProps } from './types';
|
||||||
|
|
||||||
|
function ApDexMetricsApplication({
|
||||||
|
handleGraphClick,
|
||||||
|
onDragSelect,
|
||||||
|
tagFilterItems,
|
||||||
|
topLevelOperationsRoute,
|
||||||
|
thresholdValue,
|
||||||
|
}: ApDexDataSwitcherProps): JSX.Element {
|
||||||
|
const { data, isLoading, error } = useGetMetricMeta(metricMeta);
|
||||||
|
useErrorNotification(error);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Spinner height="40vh" tip="Loading..." />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ApDexMetrics
|
||||||
|
handleGraphClick={handleGraphClick}
|
||||||
|
delta={data?.data.delta}
|
||||||
|
metricsBuckets={data?.data.le}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
topLevelOperationsRoute={topLevelOperationsRoute}
|
||||||
|
tagFilterItems={tagFilterItems}
|
||||||
|
thresholdValue={thresholdValue}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApDexMetricsApplication;
|
@ -0,0 +1,62 @@
|
|||||||
|
// This component is not been used in the application as we support only metrics for ApDex as of now.
|
||||||
|
// This component is been kept for future reference.
|
||||||
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import Graph from 'container/GridGraphLayout/Graph';
|
||||||
|
import { GraphTitle } from 'container/MetricsApplication/constant';
|
||||||
|
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||||
|
import { apDexTracesQueryBuilderQueries } from 'container/MetricsApplication/MetricsPageQueries/OverviewQueries';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { IServiceName } from '../../types';
|
||||||
|
import { ApDexDataSwitcherProps } from './types';
|
||||||
|
|
||||||
|
function ApDexTraces({
|
||||||
|
handleGraphClick,
|
||||||
|
onDragSelect,
|
||||||
|
topLevelOperationsRoute,
|
||||||
|
tagFilterItems,
|
||||||
|
thresholdValue,
|
||||||
|
}: ApDexDataSwitcherProps): JSX.Element {
|
||||||
|
const { servicename } = useParams<IServiceName>();
|
||||||
|
|
||||||
|
const apDexTracesWidget = useMemo(
|
||||||
|
() =>
|
||||||
|
getWidgetQueryBuilder({
|
||||||
|
query: {
|
||||||
|
queryType: EQueryType.QUERY_BUILDER,
|
||||||
|
promql: [],
|
||||||
|
builder: apDexTracesQueryBuilderQueries({
|
||||||
|
servicename,
|
||||||
|
tagFilterItems,
|
||||||
|
topLevelOperationsRoute,
|
||||||
|
threashold: thresholdValue || 0,
|
||||||
|
}),
|
||||||
|
clickhouse_sql: [],
|
||||||
|
id: uuid(),
|
||||||
|
},
|
||||||
|
title: GraphTitle.APDEX,
|
||||||
|
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||||
|
}),
|
||||||
|
[servicename, tagFilterItems, thresholdValue, topLevelOperationsRoute],
|
||||||
|
);
|
||||||
|
|
||||||
|
const isQueryEnabled =
|
||||||
|
topLevelOperationsRoute.length > 0 && thresholdValue !== undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Graph
|
||||||
|
name="apdex"
|
||||||
|
widget={apDexTracesWidget}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
onClickHandler={handleGraphClick('ApDex')}
|
||||||
|
yAxisUnit=""
|
||||||
|
threshold={thresholdValue}
|
||||||
|
isQueryEnabled={isQueryEnabled}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApDexTraces;
|
@ -0,0 +1 @@
|
|||||||
|
export const metricMeta = 'signoz_latency_bucket';
|
@ -0,0 +1,47 @@
|
|||||||
|
import Spinner from 'components/Spinner';
|
||||||
|
import { Card, GraphContainer } from 'container/MetricsApplication/styles';
|
||||||
|
import { useGetApDexSettings } from 'hooks/apDex/useGetApDexSettings';
|
||||||
|
import useErrorNotification from 'hooks/useErrorNotification';
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { IServiceName } from '../../types';
|
||||||
|
import ApDexMetricsApplication from './ApDexMetricsApplication';
|
||||||
|
import { ApDexApplicationProps } from './types';
|
||||||
|
|
||||||
|
function ApDexApplication({
|
||||||
|
handleGraphClick,
|
||||||
|
onDragSelect,
|
||||||
|
topLevelOperationsRoute,
|
||||||
|
tagFilterItems,
|
||||||
|
}: ApDexApplicationProps): JSX.Element {
|
||||||
|
const { servicename } = useParams<IServiceName>();
|
||||||
|
const { data, isLoading, error, isRefetching } = useGetApDexSettings(
|
||||||
|
servicename,
|
||||||
|
);
|
||||||
|
useErrorNotification(error);
|
||||||
|
|
||||||
|
if (isLoading || isRefetching) {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<Spinner height="40vh" tip="Loading..." />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<GraphContainer>
|
||||||
|
<ApDexMetricsApplication
|
||||||
|
handleGraphClick={handleGraphClick}
|
||||||
|
onDragSelect={onDragSelect}
|
||||||
|
topLevelOperationsRoute={topLevelOperationsRoute}
|
||||||
|
tagFilterItems={tagFilterItems}
|
||||||
|
thresholdValue={data?.data[0].threshold}
|
||||||
|
/>
|
||||||
|
</GraphContainer>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ApDexApplication);
|
@ -0,0 +1,19 @@
|
|||||||
|
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
|
import { ClickHandlerType } from '../../Overview';
|
||||||
|
|
||||||
|
export interface ApDexApplicationProps {
|
||||||
|
handleGraphClick: (type: string) => ClickHandlerType;
|
||||||
|
onDragSelect: (start: number, end: number) => void;
|
||||||
|
topLevelOperationsRoute: string[];
|
||||||
|
tagFilterItems: TagFilterItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApDexDataSwitcherProps extends ApDexApplicationProps {
|
||||||
|
thresholdValue?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApDexMetricsProps extends ApDexDataSwitcherProps {
|
||||||
|
delta?: boolean;
|
||||||
|
metricsBuckets?: number[];
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
import Spinner from 'components/Spinner';
|
||||||
import { FeatureKeys } from 'constants/features';
|
import { FeatureKeys } from 'constants/features';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import Graph from 'container/GridGraphLayout/Graph/';
|
import Graph from 'container/GridGraphLayout/Graph/';
|
||||||
@ -24,6 +25,7 @@ function ServiceOverview({
|
|||||||
selectedTraceTags,
|
selectedTraceTags,
|
||||||
selectedTimeStamp,
|
selectedTimeStamp,
|
||||||
topLevelOperationsRoute,
|
topLevelOperationsRoute,
|
||||||
|
topLevelOperationsLoading,
|
||||||
}: ServiceOverviewProps): JSX.Element {
|
}: ServiceOverviewProps): JSX.Element {
|
||||||
const { servicename } = useParams<IServiceName>();
|
const { servicename } = useParams<IServiceName>();
|
||||||
|
|
||||||
@ -63,6 +65,14 @@ function ServiceOverview({
|
|||||||
|
|
||||||
const isQueryEnabled = topLevelOperationsRoute.length > 0;
|
const isQueryEnabled = topLevelOperationsRoute.length > 0;
|
||||||
|
|
||||||
|
if (topLevelOperationsLoading) {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<Spinner height="40vh" tip="Loading..." />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
@ -99,6 +109,7 @@ interface ServiceOverviewProps {
|
|||||||
onDragSelect: (start: number, end: number) => void;
|
onDragSelect: (start: number, end: number) => void;
|
||||||
handleGraphClick: (type: string) => ClickHandlerType;
|
handleGraphClick: (type: string) => ClickHandlerType;
|
||||||
topLevelOperationsRoute: string[];
|
topLevelOperationsRoute: string[];
|
||||||
|
topLevelOperationsLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ServiceOverview;
|
export default ServiceOverview;
|
||||||
|
@ -56,7 +56,19 @@ export interface LatencyProps {
|
|||||||
topLevelOperationsRoute: string[];
|
topLevelOperationsRoute: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApDexProps {
|
||||||
|
servicename: IServiceName['servicename'];
|
||||||
|
tagFilterItems: TagFilterItem[];
|
||||||
|
topLevelOperationsRoute: string[];
|
||||||
|
threashold: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TableRendererProps {
|
export interface TableRendererProps {
|
||||||
columnName: string;
|
columnName: string;
|
||||||
renderFunction: (record: RowData) => ReactNode;
|
renderFunction: (record: RowData) => ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApDexMetricsQueryBuilderQueriesProps extends ApDexProps {
|
||||||
|
delta: boolean;
|
||||||
|
metricsBuckets: number[];
|
||||||
|
}
|
||||||
|
@ -14,9 +14,13 @@ export const OPERATION_LEGENDS = ['Operations'];
|
|||||||
export enum FORMULA {
|
export enum FORMULA {
|
||||||
ERROR_PERCENTAGE = 'A*100/B',
|
ERROR_PERCENTAGE = 'A*100/B',
|
||||||
DATABASE_CALLS_AVG_DURATION = 'A/B',
|
DATABASE_CALLS_AVG_DURATION = 'A/B',
|
||||||
|
APDEX_TRACES = '((B + C)/2)/A',
|
||||||
|
APDEX_DELTA_SPAN_METRICS = '(B + C/2)/A',
|
||||||
|
APDEX_CUMULATIVE_SPAN_METRICS = '((B + C)/2)/A',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum GraphTitle {
|
export enum GraphTitle {
|
||||||
|
APDEX = 'Apdex',
|
||||||
LATENCY = 'Latency',
|
LATENCY = 'Latency',
|
||||||
RATE_PER_OPS = 'Rate (ops/s)',
|
RATE_PER_OPS = 'Rate (ops/s)',
|
||||||
ERROR_PERCENTAGE = 'Error Percentage',
|
ERROR_PERCENTAGE = 'Error Percentage',
|
||||||
@ -41,6 +45,7 @@ export enum DataType {
|
|||||||
STRING = 'string',
|
STRING = 'string',
|
||||||
FLOAT64 = 'float64',
|
FLOAT64 = 'float64',
|
||||||
INT64 = 'int64',
|
INT64 = 'int64',
|
||||||
|
BOOL = 'bool',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MetricsType {
|
export enum MetricsType {
|
||||||
@ -49,7 +54,9 @@ export enum MetricsType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum WidgetKeys {
|
export enum WidgetKeys {
|
||||||
|
Le = 'le',
|
||||||
Name = 'name',
|
Name = 'name',
|
||||||
|
HasError = 'hasError',
|
||||||
Address = 'address',
|
Address = 'address',
|
||||||
DurationNano = 'durationNano',
|
DurationNano = 'durationNano',
|
||||||
StatusCode = 'status_code',
|
StatusCode = 'status_code',
|
||||||
|
@ -29,6 +29,14 @@ export const Col = styled(ColComponent)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ColApDexContainer = styled(ColComponent)`
|
||||||
|
padding: 0 !important;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ColErrorContainer = styled(ColComponent)`
|
||||||
|
padding: 2rem 0 !important;
|
||||||
|
`;
|
||||||
|
|
||||||
export const GraphContainer = styled.div`
|
export const GraphContainer = styled.div`
|
||||||
height: 40vh;
|
height: 40vh;
|
||||||
`;
|
`;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
@ -5,7 +6,7 @@ import { IServiceName } from './Tabs/types';
|
|||||||
|
|
||||||
export interface GetWidgetQueryBuilderProps {
|
export interface GetWidgetQueryBuilderProps {
|
||||||
query: Widgets['query'];
|
query: Widgets['query'];
|
||||||
title?: string;
|
title?: ReactNode;
|
||||||
panelTypes: Widgets['panelTypes'];
|
panelTypes: Widgets['panelTypes'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,3 +24,14 @@ export const navigateToTrace = ({
|
|||||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"operation":["${operation}"]}&filterToFetchData=["duration","status","serviceName","operation"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false,"operation":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&spanAggregateCurrentPage=1`,
|
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"operation":["${operation}"]}&filterToFetchData=["duration","status","serviceName","operation"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false,"operation":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&spanAggregateCurrentPage=1`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getNearestHighestBucketValue = (
|
||||||
|
value: number,
|
||||||
|
buckets: number[],
|
||||||
|
): string => {
|
||||||
|
const nearestBucket = buckets.find((bucket) => bucket >= value);
|
||||||
|
return nearestBucket?.toString() || '+Inf';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertMilSecToNanoSec = (value: number): number =>
|
||||||
|
value * 1000000000;
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
import { DefaultOptionType } from 'antd/es/select';
|
||||||
|
|
||||||
|
import {
|
||||||
|
BooleanFormats,
|
||||||
|
Category,
|
||||||
|
CategoryNames,
|
||||||
|
DataFormats,
|
||||||
|
DataRateFormats,
|
||||||
|
MiscellaneousFormats,
|
||||||
|
ThroughputFormats,
|
||||||
|
TimeFormats,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export const alertsCategory = [
|
||||||
|
{
|
||||||
|
name: CategoryNames.Time,
|
||||||
|
formats: [
|
||||||
|
{ name: 'nanoseconds (ns)', id: TimeFormats.Nanoseconds },
|
||||||
|
{ name: 'microseconds (µs)', id: TimeFormats.Microseconds },
|
||||||
|
{ name: 'milliseconds (ms)', id: TimeFormats.Milliseconds },
|
||||||
|
{ name: 'seconds (s)', id: TimeFormats.Seconds },
|
||||||
|
{ name: 'minutes (m)', id: TimeFormats.Minutes },
|
||||||
|
{ name: 'hours (h)', id: TimeFormats.Hours },
|
||||||
|
{ name: 'days (d)', id: TimeFormats.Days },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Data,
|
||||||
|
formats: [
|
||||||
|
{ name: 'bytes(IEC)', id: DataFormats.BytesIEC },
|
||||||
|
{ name: 'bytes(SI)', id: DataFormats.BytesSI },
|
||||||
|
{ name: 'bits(IEC)', id: DataFormats.BitsIEC },
|
||||||
|
{ name: 'bits(SI)', id: DataFormats.BitsSI },
|
||||||
|
{ name: 'kibibytes', id: DataFormats.KibiBytes },
|
||||||
|
{ name: 'kilobytes', id: DataFormats.KiloBytes },
|
||||||
|
{ name: 'mebibytes', id: DataFormats.MebiBytes },
|
||||||
|
{ name: 'megabytes', id: DataFormats.MegaBytes },
|
||||||
|
{ name: 'gibibytes', id: DataFormats.GibiBytes },
|
||||||
|
{ name: 'gigabytes', id: DataFormats.GigaBytes },
|
||||||
|
{ name: 'tebibytes', id: DataFormats.TebiBytes },
|
||||||
|
{ name: 'terabytes', id: DataFormats.TeraBytes },
|
||||||
|
{ name: 'pebibytes', id: DataFormats.PebiBytes },
|
||||||
|
{ name: 'petabytes', id: DataFormats.PetaBytes },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.DataRate,
|
||||||
|
formats: [
|
||||||
|
{ name: 'bytes/sec(IEC)', id: DataRateFormats.BytesPerSecIEC },
|
||||||
|
{ name: 'bytes/sec(SI)', id: DataRateFormats.BytesPerSecSI },
|
||||||
|
{ name: 'bits/sec(IEC)', id: DataRateFormats.BitsPerSecIEC },
|
||||||
|
{ name: 'bits/sec(SI)', id: DataRateFormats.BitsPerSecSI },
|
||||||
|
{ name: 'kibibytes/sec', id: DataRateFormats.KibiBytesPerSec },
|
||||||
|
{ name: 'kibibits/sec', id: DataRateFormats.KibiBitsPerSec },
|
||||||
|
{ name: 'kilobytes/sec', id: DataRateFormats.KiloBytesPerSec },
|
||||||
|
{ name: 'kilobits/sec', id: DataRateFormats.KiloBitsPerSec },
|
||||||
|
{ name: 'mebibytes/sec', id: DataRateFormats.MebiBytesPerSec },
|
||||||
|
{ name: 'mebibits/sec', id: DataRateFormats.MebiBitsPerSec },
|
||||||
|
{ name: 'megabytes/sec', id: DataRateFormats.MegaBytesPerSec },
|
||||||
|
{ name: 'megabits/sec', id: DataRateFormats.MegaBitsPerSec },
|
||||||
|
{ name: 'gibibytes/sec', id: DataRateFormats.GibiBytesPerSec },
|
||||||
|
{ name: 'gibibits/sec', id: DataRateFormats.GibiBitsPerSec },
|
||||||
|
{ name: 'gigabytes/sec', id: DataRateFormats.GigaBytesPerSec },
|
||||||
|
{ name: 'gigabits/sec', id: DataRateFormats.GigaBitsPerSec },
|
||||||
|
{ name: 'tebibytes/sec', id: DataRateFormats.TebiBytesPerSec },
|
||||||
|
{ name: 'tebibits/sec', id: DataRateFormats.TebiBitsPerSec },
|
||||||
|
{ name: 'terabytes/sec', id: DataRateFormats.TeraBytesPerSec },
|
||||||
|
{ name: 'terabits/sec', id: DataRateFormats.TeraBitsPerSec },
|
||||||
|
{ name: 'pebibytes/sec', id: DataRateFormats.PebiBytesPerSec },
|
||||||
|
{ name: 'pebibits/sec', id: DataRateFormats.PebiBitsPerSec },
|
||||||
|
{ name: 'petabytes/sec', id: DataRateFormats.PetaBytesPerSec },
|
||||||
|
{ name: 'petabits/sec', id: DataRateFormats.PetaBitsPerSec },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Miscellaneous,
|
||||||
|
formats: [
|
||||||
|
{ name: 'Percent (0.0-1.0)', id: MiscellaneousFormats.PercentUnit },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Boolean,
|
||||||
|
formats: [
|
||||||
|
{ name: 'True / False', id: BooleanFormats.TRUE_FALSE },
|
||||||
|
{ name: 'Yes / No', id: BooleanFormats.YES_NO },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Throughput,
|
||||||
|
formats: [
|
||||||
|
{ name: 'counts/sec (cps)', id: ThroughputFormats.CountsPerSec },
|
||||||
|
{ name: 'ops/sec (ops)', id: ThroughputFormats.OpsPerSec },
|
||||||
|
{ name: 'requests/sec (reqps)', id: ThroughputFormats.RequestsPerSec },
|
||||||
|
{ name: 'reads/sec (rps)', id: ThroughputFormats.ReadsPerSec },
|
||||||
|
{ name: 'writes/sec (wps)', id: ThroughputFormats.WritesPerSec },
|
||||||
|
{ name: 'I/O operations/sec (iops)', id: ThroughputFormats.IOOpsPerSec },
|
||||||
|
{ name: 'counts/min (cpm)', id: ThroughputFormats.CountsPerMin },
|
||||||
|
{ name: 'ops/min (opm)', id: ThroughputFormats.OpsPerMin },
|
||||||
|
{ name: 'reads/min (rpm)', id: ThroughputFormats.ReadsPerMin },
|
||||||
|
{ name: 'writes/min (wpm)', id: ThroughputFormats.WritesPerMin },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getCategorySelectOptionByName = (
|
||||||
|
name?: CategoryNames | string,
|
||||||
|
): DefaultOptionType[] =>
|
||||||
|
alertsCategory
|
||||||
|
.find((category) => category.name === name)
|
||||||
|
?.formats.map((format) => ({
|
||||||
|
label: format.name,
|
||||||
|
value: format.id,
|
||||||
|
})) || [];
|
||||||
|
|
||||||
|
export const getCategoryByOptionId = (id: string): Category | undefined =>
|
||||||
|
alertsCategory.find((category) =>
|
||||||
|
category.formats.some((format) => format.id === id),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const isCategoryName = (name: string): name is CategoryNames =>
|
||||||
|
alertsCategory.some((category) => category.name === name);
|
@ -1,383 +1,436 @@
|
|||||||
import { flattenDeep } from 'lodash-es';
|
import { flattenDeep } from 'lodash-es';
|
||||||
|
|
||||||
export const dataTypeCategories = [
|
import {
|
||||||
{
|
AccelerationFormats,
|
||||||
name: 'Time',
|
AngularFormats,
|
||||||
formats: [
|
AreaFormats,
|
||||||
{ name: 'Hertz (1/s)', id: 'hertz' },
|
BooleanFormats,
|
||||||
{ name: 'nanoseconds (ns)', id: 'ns' },
|
CategoryNames,
|
||||||
{ name: 'microseconds (µs)', id: 'µs' },
|
ConcentrationFormats,
|
||||||
{ name: 'milliseconds (ms)', id: 'ms' },
|
CurrencyFormats,
|
||||||
{ name: 'seconds (s)', id: 's' },
|
DataFormats,
|
||||||
{ name: 'minutes (m)', id: 'm' },
|
DataRateFormats,
|
||||||
{ name: 'hours (h)', id: 'h' },
|
DataTypeCategories,
|
||||||
{ name: 'days (d)', id: 'd' },
|
DatetimeFormats,
|
||||||
{ name: 'duration (ms)', id: 'dtdurationms' },
|
FlopsFormats,
|
||||||
{ name: 'duration (s)', id: 'dtdurations' },
|
FlowFormats,
|
||||||
{ name: 'duration (hh:mm:ss)', id: 'dthms' },
|
ForceFormats,
|
||||||
{ name: 'duration (d hh:mm:ss)', id: 'dtdhms' },
|
HashRateFormats,
|
||||||
{ name: 'Timeticks (s/100)', id: 'timeticks' },
|
LengthFormats,
|
||||||
{ name: 'clock (ms)', id: 'clockms' },
|
MassFormats,
|
||||||
{ name: 'clock (s)', id: 'clocks' },
|
MiscellaneousFormats,
|
||||||
],
|
PowerElectricalFormats,
|
||||||
},
|
PressureFormats,
|
||||||
{
|
RadiationFormats,
|
||||||
name: 'Throughput',
|
RotationSpeedFormats,
|
||||||
formats: [
|
TemperatureFormats,
|
||||||
{ name: 'counts/sec (cps)', id: 'cps' },
|
ThroughputFormats,
|
||||||
{ name: 'ops/sec (ops)', id: 'ops' },
|
TimeFormats,
|
||||||
{ name: 'requests/sec (rps)', id: 'reqps' },
|
VelocityFormats,
|
||||||
{ name: 'reads/sec (rps)', id: 'rps' },
|
VolumeFormats,
|
||||||
{ name: 'writes/sec (wps)', id: 'wps' },
|
} from './types';
|
||||||
{ name: 'I/O ops/sec (iops)', id: 'iops' },
|
|
||||||
{ name: 'counts/min (cpm)', id: 'cpm' },
|
|
||||||
{ name: 'ops/min (opm)', id: 'opm' },
|
|
||||||
{ name: 'reads/min (rpm)', id: 'rpm' },
|
|
||||||
{ name: 'writes/min (wpm)', id: 'wpm' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Data',
|
|
||||||
formats: [
|
|
||||||
{ name: 'bytes(IEC)', id: 'bytes' },
|
|
||||||
{ name: 'bytes(SI)', id: 'decbytes' },
|
|
||||||
{ name: 'bits(IEC)', id: 'bits' },
|
|
||||||
{ name: 'bits(SI)', id: 'decbits' },
|
|
||||||
{ name: 'kibibytes', id: 'kbytes' },
|
|
||||||
{ name: 'kilobytes', id: 'deckbytes' },
|
|
||||||
{ name: 'mebibytes', id: 'mbytes' },
|
|
||||||
{ name: 'megabytes', id: 'decmbytes' },
|
|
||||||
{ name: 'gibibytes', id: 'gbytes' },
|
|
||||||
{ name: 'gigabytes', id: 'decgbytes' },
|
|
||||||
{ name: 'tebibytes', id: 'tbytes' },
|
|
||||||
{ name: 'terabytes', id: 'dectbytes' },
|
|
||||||
{ name: 'pebibytes', id: 'pbytes' },
|
|
||||||
{ name: 'petabytes', id: 'decpbytes' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Data rate',
|
|
||||||
formats: [
|
|
||||||
{ name: 'packets/sec', id: 'pps' },
|
|
||||||
{ name: 'bytes/sec(IEC)', id: 'binBps' },
|
|
||||||
{ name: 'bytes/sec(SI)', id: 'Bps' },
|
|
||||||
{ name: 'bits/sec(IEC)', id: 'binbps' },
|
|
||||||
{ name: 'bits/sec(SI)', id: 'bps' },
|
|
||||||
{ name: 'kibibytes/sec', id: 'KiBs' },
|
|
||||||
{ name: 'kibibits/sec', id: 'Kibits' },
|
|
||||||
{ name: 'kilobytes/sec', id: 'KBs' },
|
|
||||||
{ name: 'kilobits/sec', id: 'Kbits' },
|
|
||||||
{ name: 'mebibytes/sec', id: 'MiBs' },
|
|
||||||
{ name: 'mebibits/sec', id: 'Mibits' },
|
|
||||||
{ name: 'megabytes/sec', id: 'MBs' },
|
|
||||||
{ name: 'megabits/sec', id: 'Mbits' },
|
|
||||||
{ name: 'gibibytes/sec', id: 'GiBs' },
|
|
||||||
{ name: 'gibibits/sec', id: 'Gibits' },
|
|
||||||
{ name: 'gigabytes/sec', id: 'GBs' },
|
|
||||||
{ name: 'gigabits/sec', id: 'Gbits' },
|
|
||||||
{ name: 'tebibytes/sec', id: 'TiBs' },
|
|
||||||
{ name: 'tebibits/sec', id: 'Tibits' },
|
|
||||||
{ name: 'terabytes/sec', id: 'TBs' },
|
|
||||||
{ name: 'terabits/sec', id: 'Tbits' },
|
|
||||||
{ name: 'pebibytes/sec', id: 'PiBs' },
|
|
||||||
{ name: 'pebibits/sec', id: 'Pibits' },
|
|
||||||
{ name: 'petabytes/sec', id: 'PBs' },
|
|
||||||
{ name: 'petabits/sec', id: 'Pbits' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Hash rate',
|
|
||||||
formats: [
|
|
||||||
{ name: 'hashes/sec', id: 'Hs' },
|
|
||||||
{ name: 'kilohashes/sec', id: 'KHs' },
|
|
||||||
{ name: 'megahashes/sec', id: 'MHs' },
|
|
||||||
{ name: 'gigahashes/sec', id: 'GHs' },
|
|
||||||
{ name: 'terahashes/sec', id: 'THs' },
|
|
||||||
{ name: 'petahashes/sec', id: 'PHs' },
|
|
||||||
{ name: 'exahashes/sec', id: 'EHs' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Misc',
|
|
||||||
formats: [
|
|
||||||
{ name: 'none', id: 'none' },
|
|
||||||
{ name: 'String', id: 'string' },
|
|
||||||
{ name: 'short', id: 'short' },
|
|
||||||
{ name: 'Percent (0-100)', id: 'percent' },
|
|
||||||
{ name: 'Percent (0.0-1.0)', id: 'percentunit' },
|
|
||||||
{ name: 'Humidity (%H)', id: 'humidity' },
|
|
||||||
{ name: 'Decibel', id: 'dB' },
|
|
||||||
{ name: 'Hexadecimal (0x)', id: 'hex0x' },
|
|
||||||
{ name: 'Hexadecimal', id: 'hex' },
|
|
||||||
{ name: 'Scientific notation', id: 'sci' },
|
|
||||||
{ name: 'Locale format', id: 'locale' },
|
|
||||||
{ name: 'Pixels', id: 'pixel' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Acceleration',
|
|
||||||
formats: [
|
|
||||||
{ name: 'Meters/sec²', id: 'accMS2' },
|
|
||||||
{ name: 'Feet/sec²', id: 'accFS2' },
|
|
||||||
{ name: 'G unit', id: 'accG' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Angle',
|
|
||||||
formats: [
|
|
||||||
{ name: 'Degrees (°)', id: 'degree' },
|
|
||||||
{ name: 'Radians', id: 'radian' },
|
|
||||||
{ name: 'Gradian', id: 'grad' },
|
|
||||||
{ name: 'Arc Minutes', id: 'arcmin' },
|
|
||||||
{ name: 'Arc Seconds', id: 'arcsec' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Area',
|
|
||||||
formats: [
|
|
||||||
{ name: 'Square Meters (m²)', id: 'areaM2' },
|
|
||||||
{ name: 'Square Feet (ft²)', id: 'areaF2' },
|
|
||||||
{ name: 'Square Miles (mi²)', id: 'areaMI2' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Computation',
|
|
||||||
formats: [
|
|
||||||
{ name: 'FLOP/s', id: 'flops' },
|
|
||||||
{ name: 'MFLOP/s', id: 'mflops' },
|
|
||||||
{ name: 'GFLOP/s', id: 'gflops' },
|
|
||||||
{ name: 'TFLOP/s', id: 'tflops' },
|
|
||||||
{ name: 'PFLOP/s', id: 'pflops' },
|
|
||||||
{ name: 'EFLOP/s', id: 'eflops' },
|
|
||||||
{ name: 'ZFLOP/s', id: 'zflops' },
|
|
||||||
{ name: 'YFLOP/s', id: 'yflops' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Concentration',
|
|
||||||
formats: [
|
|
||||||
{ name: 'parts-per-million (ppm)', id: 'ppm' },
|
|
||||||
{ name: 'parts-per-billion (ppb)', id: 'conppb' },
|
|
||||||
{ name: 'nanogram per cubic meter (ng/m³)', id: 'conngm3' },
|
|
||||||
{ name: 'nanogram per normal cubic meter (ng/Nm³)', id: 'conngNm3' },
|
|
||||||
{ name: 'microgram per cubic meter (μg/m³)', id: 'conμgm3' },
|
|
||||||
{ name: 'microgram per normal cubic meter (μg/Nm³)', id: 'conμgNm3' },
|
|
||||||
{ name: 'milligram per cubic meter (mg/m³)', id: 'conmgm3' },
|
|
||||||
{ name: 'milligram per normal cubic meter (mg/Nm³)', id: 'conmgNm3' },
|
|
||||||
{ name: 'gram per cubic meter (g/m³)', id: 'congm3' },
|
|
||||||
{ name: 'gram per normal cubic meter (g/Nm³)', id: 'congNm3' },
|
|
||||||
{ name: 'milligrams per decilitre (mg/dL)', id: 'conmgdL' },
|
|
||||||
{ name: 'millimoles per litre (mmol/L)', id: 'conmmolL' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Currency',
|
|
||||||
formats: [
|
|
||||||
{ name: 'Dollars ($)', id: 'currencyUSD' },
|
|
||||||
{ name: 'Pounds (£)', id: 'currencyGBP' },
|
|
||||||
{ name: 'Euro (€)', id: 'currencyEUR' },
|
|
||||||
{ name: 'Yen (¥)', id: 'currencyJPY' },
|
|
||||||
{ name: 'Rubles (₽)', id: 'currencyRUB' },
|
|
||||||
{ name: 'Hryvnias (₴)', id: 'currencyUAH' },
|
|
||||||
{ name: 'Real (R$)', id: 'currencyBRL' },
|
|
||||||
{ name: 'Danish Krone (kr)', id: 'currencyDKK' },
|
|
||||||
{ name: 'Icelandic Króna (kr)', id: 'currencyISK' },
|
|
||||||
{ name: 'Norwegian Krone (kr)', id: 'currencyNOK' },
|
|
||||||
{ name: 'Swedish Krona (kr)', id: 'currencySEK' },
|
|
||||||
{ name: 'Czech koruna (czk)', id: 'currencyCZK' },
|
|
||||||
{ name: 'Swiss franc (CHF)', id: 'currencyCHF' },
|
|
||||||
{ name: 'Polish Złoty (PLN)', id: 'currencyPLN' },
|
|
||||||
{ name: 'Bitcoin (฿)', id: 'currencyBTC' },
|
|
||||||
{ name: 'Milli Bitcoin (฿)', id: 'currencymBTC' },
|
|
||||||
{ name: 'Micro Bitcoin (฿)', id: 'currencyμBTC' },
|
|
||||||
{ name: 'South African Rand (R)', id: 'currencyZAR' },
|
|
||||||
{ name: 'Indian Rupee (₹)', id: 'currencyINR' },
|
|
||||||
{ name: 'South Korean Won (₩)', id: 'currencyKRW' },
|
|
||||||
{ name: 'Indonesian Rupiah (Rp)', id: 'currencyIDR' },
|
|
||||||
{ name: 'Philippine Peso (PHP)', id: 'currencyPHP' },
|
|
||||||
{ name: 'Vietnamese Dong (VND)', id: 'currencyVND' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
|
export const dataTypeCategories: DataTypeCategories = [
|
||||||
{
|
{
|
||||||
name: 'Date & time',
|
name: CategoryNames.Time,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'Datetime ISO', id: 'dateTimeAsIso' },
|
{ name: 'Hertz (1/s)', id: TimeFormats.Hertz },
|
||||||
|
{ name: 'nanoseconds (ns)', id: TimeFormats.Nanoseconds },
|
||||||
|
{ name: 'microseconds (µs)', id: TimeFormats.Microseconds },
|
||||||
|
{ name: 'milliseconds (ms)', id: TimeFormats.Milliseconds },
|
||||||
|
{ name: 'seconds (s)', id: TimeFormats.Seconds },
|
||||||
|
{ name: 'minutes (m)', id: TimeFormats.Minutes },
|
||||||
|
{ name: 'hours (h)', id: TimeFormats.Hours },
|
||||||
|
{ name: 'days (d)', id: TimeFormats.Days },
|
||||||
|
{ name: 'duration in ms (dtdurationms)', id: TimeFormats.DurationMs },
|
||||||
|
{ name: 'duration in s (dtdurations)', id: TimeFormats.DurationS },
|
||||||
|
{ name: 'duration in h:m:s (dthms)', id: TimeFormats.DurationHms },
|
||||||
|
{ name: 'duration in d:h:m:s (dtdhms)', id: TimeFormats.DurationDhms },
|
||||||
|
{ name: 'timeticks (timeticks)', id: TimeFormats.Timeticks },
|
||||||
|
{ name: 'clock in ms (clockms)', id: TimeFormats.ClockMs },
|
||||||
|
{ name: 'clock in s (clocks)', id: TimeFormats.ClockS },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Throughput,
|
||||||
|
formats: [
|
||||||
|
{ name: 'counts/sec (cps)', id: ThroughputFormats.CountsPerSec },
|
||||||
|
{ name: 'ops/sec (ops)', id: ThroughputFormats.OpsPerSec },
|
||||||
|
{ name: 'requests/sec (reqps)', id: ThroughputFormats.RequestsPerSec },
|
||||||
|
{ name: 'reads/sec (rps)', id: ThroughputFormats.ReadsPerSec },
|
||||||
|
{ name: 'writes/sec (wps)', id: ThroughputFormats.WritesPerSec },
|
||||||
|
{ name: 'I/O operations/sec (iops)', id: ThroughputFormats.IOOpsPerSec },
|
||||||
|
{ name: 'counts/min (cpm)', id: ThroughputFormats.CountsPerMin },
|
||||||
|
{ name: 'ops/min (opm)', id: ThroughputFormats.OpsPerMin },
|
||||||
|
{ name: 'reads/min (rpm)', id: ThroughputFormats.ReadsPerMin },
|
||||||
|
{ name: 'writes/min (wpm)', id: ThroughputFormats.WritesPerMin },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Data,
|
||||||
|
formats: [
|
||||||
|
{ name: 'bytes(IEC)', id: DataFormats.BytesIEC },
|
||||||
|
{ name: 'bytes(SI)', id: DataFormats.BytesSI },
|
||||||
|
{ name: 'bits(IEC)', id: DataFormats.BitsIEC },
|
||||||
|
{ name: 'bits(SI)', id: DataFormats.BitsSI },
|
||||||
|
{ name: 'kibibytes', id: DataFormats.KibiBytes },
|
||||||
|
{ name: 'kilobytes', id: DataFormats.KiloBytes },
|
||||||
|
{ name: 'mebibytes', id: DataFormats.MebiBytes },
|
||||||
|
{ name: 'megabytes', id: DataFormats.MegaBytes },
|
||||||
|
{ name: 'gibibytes', id: DataFormats.GibiBytes },
|
||||||
|
{ name: 'gigabytes', id: DataFormats.GigaBytes },
|
||||||
|
{ name: 'tebibytes', id: DataFormats.TebiBytes },
|
||||||
|
{ name: 'terabytes', id: DataFormats.TeraBytes },
|
||||||
|
{ name: 'pebibytes', id: DataFormats.PebiBytes },
|
||||||
|
{ name: 'petabytes', id: DataFormats.PetaBytes },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.DataRate,
|
||||||
|
formats: [
|
||||||
|
{ name: 'packets/sec', id: DataRateFormats.PacketsPerSec },
|
||||||
|
{ name: 'bytes/sec(IEC)', id: DataRateFormats.BytesPerSecIEC },
|
||||||
|
{ name: 'bytes/sec(SI)', id: DataRateFormats.BytesPerSecSI },
|
||||||
|
{ name: 'bits/sec(IEC)', id: DataRateFormats.BitsPerSecIEC },
|
||||||
|
{ name: 'bits/sec(SI)', id: DataRateFormats.BitsPerSecSI },
|
||||||
|
{ name: 'kibibytes/sec', id: DataRateFormats.KibiBytesPerSec },
|
||||||
|
{ name: 'kibibits/sec', id: DataRateFormats.KibiBitsPerSec },
|
||||||
|
{ name: 'kilobytes/sec', id: DataRateFormats.KiloBytesPerSec },
|
||||||
|
{ name: 'kilobits/sec', id: DataRateFormats.KiloBitsPerSec },
|
||||||
|
{ name: 'mebibytes/sec', id: DataRateFormats.MebiBytesPerSec },
|
||||||
|
{ name: 'mebibits/sec', id: DataRateFormats.MebiBitsPerSec },
|
||||||
|
{ name: 'megabytes/sec', id: DataRateFormats.MegaBytesPerSec },
|
||||||
|
{ name: 'megabits/sec', id: DataRateFormats.MegaBitsPerSec },
|
||||||
|
{ name: 'gibibytes/sec', id: DataRateFormats.GibiBytesPerSec },
|
||||||
|
{ name: 'gibibits/sec', id: DataRateFormats.GibiBitsPerSec },
|
||||||
|
{ name: 'gigabytes/sec', id: DataRateFormats.GigaBytesPerSec },
|
||||||
|
{ name: 'gigabits/sec', id: DataRateFormats.GigaBitsPerSec },
|
||||||
|
{ name: 'tebibytes/sec', id: DataRateFormats.TebiBytesPerSec },
|
||||||
|
{ name: 'tebibits/sec', id: DataRateFormats.TebiBitsPerSec },
|
||||||
|
{ name: 'terabytes/sec', id: DataRateFormats.TeraBytesPerSec },
|
||||||
|
{ name: 'terabits/sec', id: DataRateFormats.TeraBitsPerSec },
|
||||||
|
{ name: 'pebibytes/sec', id: DataRateFormats.PebiBytesPerSec },
|
||||||
|
{ name: 'pebibits/sec', id: DataRateFormats.PebiBitsPerSec },
|
||||||
|
{ name: 'petabytes/sec', id: DataRateFormats.PetaBytesPerSec },
|
||||||
|
{ name: 'petabits/sec', id: DataRateFormats.PetaBitsPerSec },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.HashRate,
|
||||||
|
formats: [
|
||||||
|
{ name: 'hashes/sec', id: HashRateFormats.HashesPerSec },
|
||||||
|
{ name: 'kilohashes/sec', id: HashRateFormats.KiloHashesPerSec },
|
||||||
|
{ name: 'megahashes/sec', id: HashRateFormats.MegaHashesPerSec },
|
||||||
|
{ name: 'gigahashes/sec', id: HashRateFormats.GigaHashesPerSec },
|
||||||
|
{ name: 'terahashes/sec', id: HashRateFormats.TeraHashesPerSec },
|
||||||
|
{ name: 'petahashes/sec', id: HashRateFormats.PetaHashesPerSec },
|
||||||
|
{ name: 'exahashes/sec', id: HashRateFormats.ExaHashesPerSec },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Miscellaneous,
|
||||||
|
formats: [
|
||||||
|
{ name: 'none', id: MiscellaneousFormats.None },
|
||||||
|
{ name: 'String', id: MiscellaneousFormats.String },
|
||||||
|
{ name: 'short', id: MiscellaneousFormats.Short },
|
||||||
|
{ name: 'Percent (0-100)', id: MiscellaneousFormats.Percent },
|
||||||
|
{ name: 'Percent (0.0-1.0)', id: MiscellaneousFormats.PercentUnit },
|
||||||
|
{ name: 'Humidity (%H)', id: MiscellaneousFormats.Humidity },
|
||||||
|
{ name: 'Decibel', id: MiscellaneousFormats.Decibel },
|
||||||
|
{ name: 'Hexadecimal (0x)', id: MiscellaneousFormats.Hexadecimal0x },
|
||||||
|
{ name: 'Hexadecimal', id: MiscellaneousFormats.Hexadecimal },
|
||||||
|
{ name: 'Scientific notation', id: MiscellaneousFormats.ScientificNotation },
|
||||||
|
{ name: 'Locale format', id: MiscellaneousFormats.LocaleFormat },
|
||||||
|
{ name: 'Pixels', id: MiscellaneousFormats.Pixels },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Acceleration,
|
||||||
|
formats: [
|
||||||
|
{ name: 'Meters/sec²', id: AccelerationFormats.MetersPerSecondSquared },
|
||||||
|
{ name: 'Feet/sec²', id: AccelerationFormats.FeetPerSecondSquared },
|
||||||
|
{ name: 'G unit', id: AccelerationFormats.GUnit },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Angle,
|
||||||
|
formats: [
|
||||||
|
{ name: 'Degrees (°)', id: AngularFormats.Degree },
|
||||||
|
{ name: 'Radians', id: AngularFormats.Radian },
|
||||||
|
{ name: 'Gradian', id: AngularFormats.Gradian },
|
||||||
|
{ name: 'Arc Minutes', id: AngularFormats.ArcMinute },
|
||||||
|
{ name: 'Arc Seconds', id: AngularFormats.ArcSecond },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Area,
|
||||||
|
formats: [
|
||||||
|
{ name: 'Square Meters (m²)', id: AreaFormats.SquareMeters },
|
||||||
|
{ name: 'Square Feet (ft²)', id: AreaFormats.SquareFeet },
|
||||||
|
{ name: 'Square Miles (mi²)', id: AreaFormats.SquareMiles },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Computation,
|
||||||
|
formats: [
|
||||||
|
{ name: 'FLOP/s', id: FlopsFormats.FLOPs },
|
||||||
|
{ name: 'MFLOP/s', id: FlopsFormats.MFLOPs },
|
||||||
|
{ name: 'GFLOP/s', id: FlopsFormats.GFLOPs },
|
||||||
|
{ name: 'TFLOP/s', id: FlopsFormats.TFLOPs },
|
||||||
|
{ name: 'PFLOP/s', id: FlopsFormats.PFLOPs },
|
||||||
|
{ name: 'EFLOP/s', id: FlopsFormats.EFLOPs },
|
||||||
|
{ name: 'ZFLOP/s', id: FlopsFormats.ZFLOPs },
|
||||||
|
{ name: 'YFLOP/s', id: FlopsFormats.YFLOPs },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Concentration,
|
||||||
|
formats: [
|
||||||
|
{ name: 'parts-per-million (ppm)', id: ConcentrationFormats.PPM },
|
||||||
|
{ name: 'parts-per-billion (ppb)', id: ConcentrationFormats.PPB },
|
||||||
|
{ name: 'nanogram per cubic meter (ng/m³)', id: ConcentrationFormats.NgM3 },
|
||||||
|
{
|
||||||
|
name: 'nanogram per normal cubic meter (ng/Nm³)',
|
||||||
|
id: ConcentrationFormats.NgNM3,
|
||||||
|
},
|
||||||
|
{ name: 'microgram per cubic meter (μg/m³)', id: ConcentrationFormats.UgM3 },
|
||||||
|
{
|
||||||
|
name: 'microgram per normal cubic meter (μg/Nm³)',
|
||||||
|
id: ConcentrationFormats.UgNM3,
|
||||||
|
},
|
||||||
|
{ name: 'milligram per cubic meter (mg/m³)', id: ConcentrationFormats.MgM3 },
|
||||||
|
{
|
||||||
|
name: 'milligram per normal cubic meter (mg/Nm³)',
|
||||||
|
id: ConcentrationFormats.MgNM3,
|
||||||
|
},
|
||||||
|
{ name: 'gram per cubic meter (g/m³)', id: ConcentrationFormats.GM3 },
|
||||||
|
{
|
||||||
|
name: 'gram per normal cubic meter (g/Nm³)',
|
||||||
|
id: ConcentrationFormats.GNM3,
|
||||||
|
},
|
||||||
|
{ name: 'milligrams per decilitre (mg/dL)', id: ConcentrationFormats.MgDL },
|
||||||
|
{ name: 'millimoles per litre (mmol/L)', id: ConcentrationFormats.MmolL },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Currency,
|
||||||
|
formats: [
|
||||||
|
{ name: 'Dollars ($)', id: CurrencyFormats.USD },
|
||||||
|
{ name: 'Pounds (£)', id: CurrencyFormats.GBP },
|
||||||
|
{ name: 'Euro (€)', id: CurrencyFormats.EUR },
|
||||||
|
{ name: 'Yen (¥)', id: CurrencyFormats.JPY },
|
||||||
|
{ name: 'Rubles (₽)', id: CurrencyFormats.RUB },
|
||||||
|
{ name: 'Hryvnias (₴)', id: CurrencyFormats.UAH },
|
||||||
|
{ name: 'Real (R$)', id: CurrencyFormats.BRL },
|
||||||
|
{ name: 'Danish Krone (kr)', id: CurrencyFormats.DKK },
|
||||||
|
{ name: 'Icelandic Króna (kr)', id: CurrencyFormats.ISK },
|
||||||
|
{ name: 'Norwegian Krone (kr)', id: CurrencyFormats.NOK },
|
||||||
|
{ name: 'Swedish Krona (kr)', id: CurrencyFormats.SEK },
|
||||||
|
{ name: 'Czech koruna (czk)', id: CurrencyFormats.CZK },
|
||||||
|
{ name: 'Swiss franc (CHF)', id: CurrencyFormats.CHF },
|
||||||
|
{ name: 'Polish Złoty (PLN)', id: CurrencyFormats.PLN },
|
||||||
|
{ name: 'Bitcoin (฿)', id: CurrencyFormats.BTC },
|
||||||
|
{ name: 'Milli Bitcoin (฿)', id: CurrencyFormats.MBTC },
|
||||||
|
{ name: 'Micro Bitcoin (฿)', id: CurrencyFormats.UBTC },
|
||||||
|
{ name: 'South African Rand (R)', id: CurrencyFormats.ZAR },
|
||||||
|
{ name: 'Indian Rupee (₹)', id: CurrencyFormats.INR },
|
||||||
|
{ name: 'South Korean Won (₩)', id: CurrencyFormats.KRW },
|
||||||
|
{ name: 'Indonesian Rupiah (Rp)', id: CurrencyFormats.IDR },
|
||||||
|
{ name: 'Philippine Peso (PHP)', id: CurrencyFormats.PHP },
|
||||||
|
{ name: 'Vietnamese Dong (VND)', id: CurrencyFormats.VND },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CategoryNames.Datetime,
|
||||||
|
formats: [
|
||||||
|
{ name: 'Datetime ISO', id: DatetimeFormats.ISO },
|
||||||
{
|
{
|
||||||
name: 'Datetime ISO (No date if today)',
|
name: 'Datetime ISO (No date if today)',
|
||||||
id: 'dateTimeAsIsoNoDateIfToday',
|
id: DatetimeFormats.ISONoDateIfToday,
|
||||||
},
|
},
|
||||||
{ name: 'Datetime US', id: 'dateTimeAsUS' },
|
{ name: 'Datetime US', id: DatetimeFormats.US },
|
||||||
{ name: 'Datetime US (No date if today)', id: 'dateTimeAsUSNoDateIfToday' },
|
{
|
||||||
{ name: 'Datetime local', id: 'dateTimeAsLocal' },
|
name: 'Datetime US (No date if today)',
|
||||||
|
id: DatetimeFormats.USNoDateIfToday,
|
||||||
|
},
|
||||||
|
{ name: 'Datetime local', id: DatetimeFormats.Local },
|
||||||
{
|
{
|
||||||
name: 'Datetime local (No date if today)',
|
name: 'Datetime local (No date if today)',
|
||||||
id: 'dateTimeAsLocalNoDateIfToday',
|
id: DatetimeFormats.LocalNoDateIfToday,
|
||||||
},
|
},
|
||||||
{ name: 'Datetime default', id: 'dateTimeAsSystem' },
|
{ name: 'Datetime default', id: DatetimeFormats.System },
|
||||||
{ name: 'From Now', id: 'dateTimeFromNow' },
|
{ name: 'From Now', id: DatetimeFormats.FromNow },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Energy',
|
name: CategoryNames.Energy,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'Watt (W)', id: 'watt' },
|
{ name: 'Watt (W)', id: PowerElectricalFormats.WATT },
|
||||||
{ name: 'Kilowatt (kW)', id: 'kwatt' },
|
{ name: 'Kilowatt (kW)', id: PowerElectricalFormats.KWATT },
|
||||||
{ name: 'Megawatt (MW)', id: 'megwatt' },
|
{ name: 'Megawatt (MW)', id: PowerElectricalFormats.MEGWATT },
|
||||||
{ name: 'Gigawatt (GW)', id: 'gwatt' },
|
{ name: 'Gigawatt (GW)', id: PowerElectricalFormats.GWATT },
|
||||||
{ name: 'Milliwatt (mW)', id: 'mwatt' },
|
{ name: 'Milliwatt (mW)', id: PowerElectricalFormats.MWATT },
|
||||||
{ name: 'Watt per square meter (W/m²)', id: 'Wm2' },
|
{ name: 'Watt per square meter (W/m²)', id: PowerElectricalFormats.WM2 },
|
||||||
{ name: 'Volt-Ampere (VA)', id: 'voltamp' },
|
{ name: 'Volt-Ampere (VA)', id: PowerElectricalFormats.VOLTAMP },
|
||||||
{ name: 'Kilovolt-Ampere (kVA)', id: 'kvoltamp' },
|
{ name: 'Kilovolt-Ampere (kVA)', id: PowerElectricalFormats.KVOLTAMP },
|
||||||
{ name: 'Volt-Ampere reactive (VAr)', id: 'voltampreact' },
|
{
|
||||||
{ name: 'Kilovolt-Ampere reactive (kVAr)', id: 'kvoltampreact' },
|
name: 'Volt-Ampere reactive (VAr)',
|
||||||
{ name: 'Watt-hour (Wh)', id: 'watth' },
|
id: PowerElectricalFormats.VOLTAMPREACT,
|
||||||
{ name: 'Watt-hour per Kilogram (Wh/kg)', id: 'watthperkg' },
|
},
|
||||||
{ name: 'Kilowatt-hour (kWh)', id: 'kwatth' },
|
{
|
||||||
{ name: 'Kilowatt-min (kWm)', id: 'kwattm' },
|
name: 'Kilovolt-Ampere reactive (kVAr)',
|
||||||
{ name: 'Ampere-hour (Ah)', id: 'amph' },
|
id: PowerElectricalFormats.KVOLTAMPREACT,
|
||||||
{ name: 'Kiloampere-hour (kAh)', id: 'kamph' },
|
},
|
||||||
{ name: 'Milliampere-hour (mAh)', id: 'mamph' },
|
{ name: 'Watt-hour (Wh)', id: PowerElectricalFormats.WATTH },
|
||||||
{ name: 'Joule (J)', id: 'joule' },
|
{
|
||||||
{ name: 'Electron volt (eV)', id: 'ev' },
|
name: 'Watt-hour per Kilogram (Wh/kg)',
|
||||||
{ name: 'Ampere (A)', id: 'amp' },
|
id: PowerElectricalFormats.WATTHPERKG,
|
||||||
{ name: 'Kiloampere (kA)', id: 'kamp' },
|
},
|
||||||
{ name: 'Milliampere (mA)', id: 'mamp' },
|
{ name: 'Kilowatt-hour (kWh)', id: PowerElectricalFormats.KWATTH },
|
||||||
{ name: 'Volt (V)', id: 'volt' },
|
{ name: 'Kilowatt-min (kWm)', id: PowerElectricalFormats.KWATTM },
|
||||||
{ name: 'Kilovolt (kV)', id: 'kvolt' },
|
{ name: 'Ampere-hour (Ah)', id: PowerElectricalFormats.AMPH },
|
||||||
{ name: 'Millivolt (mV)', id: 'mvolt' },
|
{ name: 'Kiloampere-hour (kAh)', id: PowerElectricalFormats.KAMPH },
|
||||||
{ name: 'Decibel-milliwatt (dBm)', id: 'dBm' },
|
{ name: 'Milliampere-hour (mAh)', id: PowerElectricalFormats.MAMPH },
|
||||||
{ name: 'Ohm (Ω)', id: 'ohm' },
|
{ name: 'Joule (J)', id: PowerElectricalFormats.JOULE },
|
||||||
{ name: 'Kiloohm (kΩ)', id: 'kohm' },
|
{ name: 'Electron volt (eV)', id: PowerElectricalFormats.EV },
|
||||||
{ name: 'Megaohm (MΩ)', id: 'Mohm' },
|
{ name: 'Ampere (A)', id: PowerElectricalFormats.AMP },
|
||||||
{ name: 'Farad (F)', id: 'farad' },
|
{ name: 'Kiloampere (kA)', id: PowerElectricalFormats.KAMP },
|
||||||
{ name: 'Microfarad (µF)', id: 'µfarad' },
|
{ name: 'Milliampere (mA)', id: PowerElectricalFormats.MAMP },
|
||||||
{ name: 'Nanofarad (nF)', id: 'nfarad' },
|
{ name: 'Volt (V)', id: PowerElectricalFormats.VOLT },
|
||||||
{ name: 'Picofarad (pF)', id: 'pfarad' },
|
{ name: 'Kilovolt (kV)', id: PowerElectricalFormats.KVOLT },
|
||||||
{ name: 'Femtofarad (fF)', id: 'ffarad' },
|
{ name: 'Millivolt (mV)', id: PowerElectricalFormats.MVOLT },
|
||||||
{ name: 'Henry (H)', id: 'henry' },
|
{ name: 'Decibel-milliwatt (dBm)', id: PowerElectricalFormats.DBM },
|
||||||
{ name: 'Millihenry (mH)', id: 'mhenry' },
|
{ name: 'Ohm (Ω)', id: PowerElectricalFormats.OHM },
|
||||||
{ name: 'Microhenry (µH)', id: 'µhenry' },
|
{ name: 'Kiloohm (kΩ)', id: PowerElectricalFormats.KOHM },
|
||||||
{ name: 'Lumens (Lm)', id: 'lumens' },
|
{ name: 'Megaohm (MΩ)', id: PowerElectricalFormats.MOHM },
|
||||||
|
{ name: 'Farad (F)', id: PowerElectricalFormats.FARAD },
|
||||||
|
{ name: 'Microfarad (µF)', id: PowerElectricalFormats.µFARAD },
|
||||||
|
{ name: 'Nanofarad (nF)', id: PowerElectricalFormats.NFARAD },
|
||||||
|
{ name: 'Picofarad (pF)', id: PowerElectricalFormats.PFARAD },
|
||||||
|
{ name: 'Femtofarad (fF)', id: PowerElectricalFormats.FFARAD },
|
||||||
|
{ name: 'Henry (H)', id: PowerElectricalFormats.HENRY },
|
||||||
|
{ name: 'Millihenry (mH)', id: PowerElectricalFormats.MHENRY },
|
||||||
|
{ name: 'Microhenry (µH)', id: PowerElectricalFormats.µHENRY },
|
||||||
|
{ name: 'Lumens (Lm)', id: PowerElectricalFormats.LUMENS },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Flow',
|
name: CategoryNames.Flow,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'Gallons/min (gpm)', id: 'flowgpm' },
|
{ name: 'Gallons/min (gpm)', id: FlowFormats.FLOWGPM },
|
||||||
{ name: 'Cubic meters/sec (cms)', id: 'flowcms' },
|
{ name: 'Cubic meters/sec (cms)', id: FlowFormats.FLOWCMS },
|
||||||
{ name: 'Cubic feet/sec (cfs)', id: 'flowcfs' },
|
{ name: 'Cubic feet/sec (cfs)', id: FlowFormats.FLOWCFS },
|
||||||
{ name: 'Cubic feet/min (cfm)', id: 'flowcfm' },
|
{ name: 'Cubic feet/min (cfm)', id: FlowFormats.FLOWCFM },
|
||||||
{ name: 'Litre/hour', id: 'litreh' },
|
{ name: 'Litre/hour', id: FlowFormats.LITREH },
|
||||||
{ name: 'Litre/min (L/min)', id: 'flowlpm' },
|
{ name: 'Litre/min (L/min)', id: FlowFormats.FLOWLPM },
|
||||||
{ name: 'milliLitre/min (mL/min)', id: 'flowmlpm' },
|
{ name: 'milliLitre/min (mL/min)', id: FlowFormats.FLOWMLPM },
|
||||||
{ name: 'Lux (lx)', id: 'lux' },
|
{ name: 'Lux (lx)', id: FlowFormats.LUX },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Force',
|
name: CategoryNames.Force,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'Newton-meters (Nm)', id: 'forceNm' },
|
{ name: 'Newton-meters (Nm)', id: ForceFormats.FORCENM },
|
||||||
{ name: 'Kilonewton-meters (kNm)', id: 'forcekNm' },
|
{ name: 'Kilonewton-meters (kNm)', id: ForceFormats.FORCEKNM },
|
||||||
{ name: 'Newtons (N)', id: 'forceN' },
|
{ name: 'Newtons (N)', id: ForceFormats.FORCEN },
|
||||||
{ name: 'Kilonewtons (kN)', id: 'forcekN' },
|
{ name: 'Kilonewtons (kN)', id: ForceFormats.FORCEKN },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Mass',
|
name: CategoryNames.Mass,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'milligram (mg)', id: 'massmg' },
|
{ name: 'milligram (mg)', id: MassFormats.MASSMG },
|
||||||
{ name: 'gram (g)', id: 'massg' },
|
{ name: 'gram (g)', id: MassFormats.MASSG },
|
||||||
{ name: 'pound (lb)', id: 'masslb' },
|
{ name: 'pound (lb)', id: MassFormats.MASSLB },
|
||||||
{ name: 'kilogram (kg)', id: 'masskg' },
|
{ name: 'kilogram (kg)', id: MassFormats.MASSKG },
|
||||||
{ name: 'metric ton (t)', id: 'masst' },
|
{ name: 'metric ton (t)', id: MassFormats.MASST },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Length',
|
name: CategoryNames.Length,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'millimeter (mm)', id: 'lengthmm' },
|
{ name: 'millimeter (mm)', id: LengthFormats.LENGTHMM },
|
||||||
{ name: 'inch (in)', id: 'lengthin' },
|
{ name: 'inch (in)', id: LengthFormats.LENGTHIN },
|
||||||
{ name: 'feet (ft)', id: 'lengthft' },
|
{ name: 'feet (ft)', id: LengthFormats.LENGTHFT },
|
||||||
{ name: 'meter (m)', id: 'lengthm' },
|
{ name: 'meter (m)', id: LengthFormats.LENGTHM },
|
||||||
{ name: 'kilometer (km)', id: 'lengthkm' },
|
{ name: 'kilometer (km)', id: LengthFormats.LENGTHKM },
|
||||||
{ name: 'mile (mi)', id: 'lengthmi' },
|
{ name: 'mile (mi)', id: LengthFormats.LENGTHMI },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Pressure',
|
name: CategoryNames.Pressure,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'Millibars', id: 'pressurembar' },
|
{ name: 'Millibars', id: PressureFormats.PRESSUREMBAR },
|
||||||
{ name: 'Bars', id: 'pressurebar' },
|
{ name: 'Bars', id: PressureFormats.PRESSUREBAR },
|
||||||
{ name: 'Kilobars', id: 'pressurekbar' },
|
{ name: 'Kilobars', id: PressureFormats.PRESSUREKBAR },
|
||||||
{ name: 'Pascals', id: 'pressurepa' },
|
{ name: 'Pascals', id: PressureFormats.PRESSUREPA },
|
||||||
{ name: 'Hectopascals', id: 'pressurehpa' },
|
{ name: 'Hectopascals', id: PressureFormats.PRESSUREHPA },
|
||||||
{ name: 'Kilopascals', id: 'pressurekpa' },
|
{ name: 'Kilopascals', id: PressureFormats.PRESSUREKPA },
|
||||||
{ name: 'Inches of mercury', id: 'pressurehg' },
|
{ name: 'Inches of mercury', id: PressureFormats.PRESSUREHG },
|
||||||
{ name: 'PSI', id: 'pressurepsi' },
|
{ name: 'PSI', id: PressureFormats.PRESSUREPSI },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Radiation',
|
name: CategoryNames.Radiation,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'Becquerel (Bq)', id: 'radbq' },
|
{ name: 'Becquerel (Bq)', id: RadiationFormats.RADBQ },
|
||||||
{ name: 'curie (Ci)', id: 'radci' },
|
{ name: 'curie (Ci)', id: RadiationFormats.RADCI },
|
||||||
{ name: 'Gray (Gy)', id: 'radgy' },
|
{ name: 'Gray (Gy)', id: RadiationFormats.RADGY },
|
||||||
{ name: 'rad', id: 'radrad' },
|
{ name: 'rad', id: RadiationFormats.RADRAD },
|
||||||
{ name: 'Sievert (Sv)', id: 'radsv' },
|
{ name: 'Sievert (Sv)', id: RadiationFormats.RADSV },
|
||||||
{ name: 'milliSievert (mSv)', id: 'radmsv' },
|
{ name: 'milliSievert (mSv)', id: RadiationFormats.RADMSV },
|
||||||
{ name: 'microSievert (µSv)', id: 'radusv' },
|
{ name: 'microSievert (µSv)', id: RadiationFormats.RADUSV },
|
||||||
{ name: 'rem', id: 'radrem' },
|
{ name: 'rem', id: RadiationFormats.RADREM },
|
||||||
{ name: 'Exposure (C/kg)', id: 'radexpckg' },
|
{ name: 'Exposure (C/kg)', id: RadiationFormats.RADEXPCKG },
|
||||||
{ name: 'roentgen (R)', id: 'radr' },
|
{ name: 'roentgen (R)', id: RadiationFormats.RADR },
|
||||||
{ name: 'Sievert/hour (Sv/h)', id: 'radsvh' },
|
{ name: 'Sievert/hour (Sv/h)', id: RadiationFormats.RADSVH },
|
||||||
{ name: 'milliSievert/hour (mSv/h)', id: 'radmsvh' },
|
{ name: 'milliSievert/hour (mSv/h)', id: RadiationFormats.RADMSVH },
|
||||||
{ name: 'microSievert/hour (µSv/h)', id: 'radusvh' },
|
{ name: 'microSievert/hour (µSv/h)', id: RadiationFormats.RADUSVH },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Rotational Speed',
|
name: CategoryNames.RotationSpeed,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'Revolutions per minute (rpm)', id: 'rotrpm' },
|
{ name: 'Revolutions per minute (rpm)', id: RotationSpeedFormats.ROTRPM },
|
||||||
{ name: 'Hertz (Hz)', id: 'rothz' },
|
{ name: 'Hertz (Hz)', id: RotationSpeedFormats.ROTHZ },
|
||||||
{ name: 'Radians per second (rad/s)', id: 'rotrads' },
|
{ name: 'Radians per second (rad/s)', id: RotationSpeedFormats.ROTRADS },
|
||||||
{ name: 'Degrees per second (°/s)', id: 'rotdegs' },
|
{ name: 'Degrees per second (°/s)', id: RotationSpeedFormats.ROTDEGS },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Temperature',
|
name: CategoryNames.Temperature,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'Celsius (°C)', id: 'celsius' },
|
{ name: 'Celsius (°C)', id: TemperatureFormats.CELSIUS },
|
||||||
{ name: 'Fahrenheit (°F)', id: 'fahrenheit' },
|
{ name: 'Fahrenheit (°F)', id: TemperatureFormats.FAHRENHEIT },
|
||||||
{ name: 'Kelvin (K)', id: 'kelvin' },
|
{ name: 'Kelvin (K)', id: TemperatureFormats.KELVIN },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Velocity',
|
name: CategoryNames.Velocity,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'meters/second (m/s)', id: 'velocityms' },
|
{ name: 'meters/second (m/s)', id: VelocityFormats.METERS_PER_SECOND },
|
||||||
{ name: 'kilometers/hour (km/h)', id: 'velocitykmh' },
|
{ name: 'kilometers/hour (km/h)', id: VelocityFormats.KILOMETERS_PER_HOUR },
|
||||||
{ name: 'miles/hour (mph)', id: 'velocitymph' },
|
{ name: 'miles/hour (mph)', id: VelocityFormats.MILES_PER_HOUR },
|
||||||
{ name: 'knot (kn)', id: 'velocityknot' },
|
{ name: 'knot (kn)', id: VelocityFormats.KNOT },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Volume',
|
name: CategoryNames.Volume,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'millilitre (mL)', id: 'mlitre' },
|
{ name: 'millilitre (mL)', id: VolumeFormats.MILLILITRE },
|
||||||
{ name: 'litre (L)', id: 'litre' },
|
{ name: 'litre (L)', id: VolumeFormats.LITRE },
|
||||||
{ name: 'cubic meter', id: 'm3' },
|
{ name: 'cubic meter', id: VolumeFormats.CUBIC_METER },
|
||||||
{ name: 'Normal cubic meter', id: 'Nm3' },
|
{ name: 'Normal cubic meter', id: VolumeFormats.NORMAL_CUBIC_METER },
|
||||||
{ name: 'cubic decimeter', id: 'dm3' },
|
{ name: 'cubic decimeter', id: VolumeFormats.CUBIC_DECIMETER },
|
||||||
{ name: 'gallons', id: 'gallons' },
|
{ name: 'gallons', id: VolumeFormats.GALLONS },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Boolean',
|
name: CategoryNames.Boolean,
|
||||||
formats: [
|
formats: [
|
||||||
{ name: 'True / False', id: 'bool' },
|
{ name: 'True / False', id: BooleanFormats.TRUE_FALSE },
|
||||||
{ name: 'Yes / No', id: 'bool_yes_no' },
|
{ name: 'Yes / No', id: BooleanFormats.YES_NO },
|
||||||
{ name: 'On / Off', id: 'bool_on_off' },
|
{ name: 'On / Off', id: BooleanFormats.ON_OFF },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
364
frontend/src/container/NewWidget/RightContainer/types.ts
Normal file
364
frontend/src/container/NewWidget/RightContainer/types.ts
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
export enum CategoryNames {
|
||||||
|
Time = 'Time',
|
||||||
|
Throughput = 'Throughput',
|
||||||
|
Data = 'Data',
|
||||||
|
DataRate = 'DataRate',
|
||||||
|
HashRate = 'HashRate',
|
||||||
|
Miscellaneous = 'Miscellaneous',
|
||||||
|
Acceleration = 'Acceleration',
|
||||||
|
Angular = 'Angular',
|
||||||
|
Area = 'Area',
|
||||||
|
Flops = 'Flops',
|
||||||
|
Concentration = 'Concentration',
|
||||||
|
Currency = 'Currency',
|
||||||
|
Datetime = 'Datetime',
|
||||||
|
PowerElectrical = 'PowerElectrical',
|
||||||
|
Flow = 'Flow',
|
||||||
|
Force = 'Force',
|
||||||
|
Mass = 'Mass',
|
||||||
|
Length = 'Length',
|
||||||
|
Pressure = 'Pressure',
|
||||||
|
Radiation = 'Radiation',
|
||||||
|
RotationSpeed = 'RotationSpeed',
|
||||||
|
Temperature = 'Temperature',
|
||||||
|
Velocity = 'Velocity',
|
||||||
|
Volume = 'Volume',
|
||||||
|
Boolean = 'Boolean',
|
||||||
|
Angle = 'Angle',
|
||||||
|
Computation = 'Computation',
|
||||||
|
Energy = 'Energy',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TimeFormats {
|
||||||
|
Hertz = 'hertz',
|
||||||
|
Nanoseconds = 'ns',
|
||||||
|
Microseconds = 'µs',
|
||||||
|
Milliseconds = 'ms',
|
||||||
|
Seconds = 's',
|
||||||
|
Minutes = 'm',
|
||||||
|
Hours = 'h',
|
||||||
|
Days = 'd',
|
||||||
|
DurationMs = 'dtdurationms',
|
||||||
|
DurationS = 'dtdurations',
|
||||||
|
DurationHms = 'dthms',
|
||||||
|
DurationDhms = 'dtdhms',
|
||||||
|
Timeticks = 'timeticks',
|
||||||
|
ClockMs = 'clockms',
|
||||||
|
ClockS = 'clocks',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ThroughputFormats {
|
||||||
|
CountsPerSec = 'cps',
|
||||||
|
OpsPerSec = 'ops',
|
||||||
|
RequestsPerSec = 'reqps',
|
||||||
|
ReadsPerSec = 'rps',
|
||||||
|
WritesPerSec = 'wps',
|
||||||
|
IOOpsPerSec = 'iops',
|
||||||
|
CountsPerMin = 'cpm',
|
||||||
|
OpsPerMin = 'opm',
|
||||||
|
ReadsPerMin = 'rpm',
|
||||||
|
WritesPerMin = 'wpm',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DataFormats {
|
||||||
|
BytesIEC = 'bytes',
|
||||||
|
BytesSI = 'decbytes',
|
||||||
|
BitsIEC = 'bits',
|
||||||
|
BitsSI = 'decbits',
|
||||||
|
KibiBytes = 'kbytes',
|
||||||
|
KiloBytes = 'deckbytes',
|
||||||
|
MebiBytes = 'mbytes',
|
||||||
|
MegaBytes = 'decmbytes',
|
||||||
|
GibiBytes = 'gbytes',
|
||||||
|
GigaBytes = 'decgbytes',
|
||||||
|
TebiBytes = 'tbytes',
|
||||||
|
TeraBytes = 'dectbytes',
|
||||||
|
PebiBytes = 'pbytes',
|
||||||
|
PetaBytes = 'decpbytes',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DataRateFormats {
|
||||||
|
PacketsPerSec = 'pps',
|
||||||
|
BytesPerSecIEC = 'binBps',
|
||||||
|
BytesPerSecSI = 'Bps',
|
||||||
|
BitsPerSecIEC = 'binbps',
|
||||||
|
BitsPerSecSI = 'bps',
|
||||||
|
KibiBytesPerSec = 'KiBs',
|
||||||
|
KibiBitsPerSec = 'Kibits',
|
||||||
|
KiloBytesPerSec = 'KBs',
|
||||||
|
KiloBitsPerSec = 'Kbits',
|
||||||
|
MebiBytesPerSec = 'MiBs',
|
||||||
|
MebiBitsPerSec = 'Mibits',
|
||||||
|
MegaBytesPerSec = 'MBs',
|
||||||
|
MegaBitsPerSec = 'Mbits',
|
||||||
|
GibiBytesPerSec = 'GiBs',
|
||||||
|
GibiBitsPerSec = 'Gibits',
|
||||||
|
GigaBytesPerSec = 'GBs',
|
||||||
|
GigaBitsPerSec = 'Gbits',
|
||||||
|
TebiBytesPerSec = 'TiBs',
|
||||||
|
TebiBitsPerSec = 'Tibits',
|
||||||
|
TeraBytesPerSec = 'TBs',
|
||||||
|
TeraBitsPerSec = 'Tbits',
|
||||||
|
PebiBytesPerSec = 'PiBs',
|
||||||
|
PebiBitsPerSec = 'Pibits',
|
||||||
|
PetaBytesPerSec = 'PBs',
|
||||||
|
PetaBitsPerSec = 'Pbits',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum HashRateFormats {
|
||||||
|
HashesPerSec = 'Hs',
|
||||||
|
KiloHashesPerSec = 'KHs',
|
||||||
|
MegaHashesPerSec = 'MHs',
|
||||||
|
GigaHashesPerSec = 'GHs',
|
||||||
|
TeraHashesPerSec = 'THs',
|
||||||
|
PetaHashesPerSec = 'PHs',
|
||||||
|
ExaHashesPerSec = 'EHs',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MiscellaneousFormats {
|
||||||
|
None = 'none',
|
||||||
|
String = 'string',
|
||||||
|
Short = 'short',
|
||||||
|
Percent = 'percent',
|
||||||
|
PercentUnit = 'percentunit',
|
||||||
|
Humidity = 'humidity',
|
||||||
|
Decibel = 'dB',
|
||||||
|
Hexadecimal0x = 'hex0x',
|
||||||
|
Hexadecimal = 'hex',
|
||||||
|
ScientificNotation = 'sci',
|
||||||
|
LocaleFormat = 'locale',
|
||||||
|
Pixels = 'pixel',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AccelerationFormats {
|
||||||
|
MetersPerSecondSquared = 'accMS2',
|
||||||
|
FeetPerSecondSquared = 'accFS2',
|
||||||
|
GUnit = 'accG',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AngularFormats {
|
||||||
|
Degree = 'degree',
|
||||||
|
Radian = 'radian',
|
||||||
|
Gradian = 'grad',
|
||||||
|
ArcMinute = 'arcmin',
|
||||||
|
ArcSecond = 'arcsec',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AreaFormats {
|
||||||
|
SquareMeters = 'areaM2',
|
||||||
|
SquareFeet = 'areaF2',
|
||||||
|
SquareMiles = 'areaMI2',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FlopsFormats {
|
||||||
|
FLOPs = 'flops',
|
||||||
|
MFLOPs = 'mflops',
|
||||||
|
GFLOPs = 'gflops',
|
||||||
|
TFLOPs = 'tflops',
|
||||||
|
PFLOPs = 'pflops',
|
||||||
|
EFLOPs = 'eflops',
|
||||||
|
ZFLOPs = 'zflops',
|
||||||
|
YFLOPs = 'yflops',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ConcentrationFormats {
|
||||||
|
PPM = 'ppm',
|
||||||
|
PPB = 'conppb',
|
||||||
|
NgM3 = 'conngm3',
|
||||||
|
NgNM3 = 'conngNm3',
|
||||||
|
UgM3 = 'conμgm3',
|
||||||
|
UgNM3 = 'conμgNm3',
|
||||||
|
MgM3 = 'conmgm3',
|
||||||
|
MgNM3 = 'conmgNm3',
|
||||||
|
GM3 = 'congm3',
|
||||||
|
GNM3 = 'congNm3',
|
||||||
|
MgDL = 'conmgdL',
|
||||||
|
MmolL = 'conmmolL',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CurrencyFormats {
|
||||||
|
USD = 'currencyUSD',
|
||||||
|
GBP = 'currencyGBP',
|
||||||
|
EUR = 'currencyEUR',
|
||||||
|
JPY = 'currencyJPY',
|
||||||
|
RUB = 'currencyRUB',
|
||||||
|
UAH = 'currencyUAH',
|
||||||
|
BRL = 'currencyBRL',
|
||||||
|
DKK = 'currencyDKK',
|
||||||
|
ISK = 'currencyISK',
|
||||||
|
NOK = 'currencyNOK',
|
||||||
|
SEK = 'currencySEK',
|
||||||
|
CZK = 'currencyCZK',
|
||||||
|
CHF = 'currencyCHF',
|
||||||
|
PLN = 'currencyPLN',
|
||||||
|
BTC = 'currencyBTC',
|
||||||
|
MBTC = 'currencymBTC',
|
||||||
|
UBTC = 'currencyμBTC',
|
||||||
|
ZAR = 'currencyZAR',
|
||||||
|
INR = 'currencyINR',
|
||||||
|
KRW = 'currencyKRW',
|
||||||
|
IDR = 'currencyIDR',
|
||||||
|
PHP = 'currencyPHP',
|
||||||
|
VND = 'currencyVND',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DatetimeFormats {
|
||||||
|
ISO = 'dateTimeAsIso',
|
||||||
|
ISONoDateIfToday = 'dateTimeAsIsoNoDateIfToday',
|
||||||
|
US = 'dateTimeAsUS',
|
||||||
|
USNoDateIfToday = 'dateTimeAsUSNoDateIfToday',
|
||||||
|
Local = 'dateTimeAsLocal',
|
||||||
|
LocalNoDateIfToday = 'dateTimeAsLocalNoDateIfToday',
|
||||||
|
System = 'dateTimeAsSystem',
|
||||||
|
FromNow = 'dateTimeFromNow',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PowerElectricalFormats {
|
||||||
|
WATT = 'watt',
|
||||||
|
KWATT = 'kwatt',
|
||||||
|
MEGWATT = 'megwatt',
|
||||||
|
GWATT = 'gwatt',
|
||||||
|
MWATT = 'mwatt',
|
||||||
|
WM2 = 'Wm2',
|
||||||
|
VOLTAMP = 'voltamp',
|
||||||
|
KVOLTAMP = 'kvoltamp',
|
||||||
|
VOLTAMPREACT = 'voltampreact',
|
||||||
|
KVOLTAMPREACT = 'kvoltampreact',
|
||||||
|
WATTH = 'watth',
|
||||||
|
WATTHPERKG = 'watthperkg',
|
||||||
|
KWATTH = 'kwatth',
|
||||||
|
KWATTM = 'kwattm',
|
||||||
|
AMPH = 'amph',
|
||||||
|
KAMPH = 'kamph',
|
||||||
|
MAMPH = 'mamph',
|
||||||
|
JOULE = 'joule',
|
||||||
|
EV = 'ev',
|
||||||
|
AMP = 'amp',
|
||||||
|
KAMP = 'kamp',
|
||||||
|
MAMP = 'mamp',
|
||||||
|
VOLT = 'volt',
|
||||||
|
KVOLT = 'kvolt',
|
||||||
|
MVOLT = 'mvolt',
|
||||||
|
DBM = 'dBm',
|
||||||
|
OHM = 'ohm',
|
||||||
|
KOHM = 'kohm',
|
||||||
|
MOHM = 'Mohm',
|
||||||
|
FARAD = 'farad',
|
||||||
|
µFARAD = 'µfarad',
|
||||||
|
NFARAD = 'nfarad',
|
||||||
|
PFARAD = 'pfarad',
|
||||||
|
FFARAD = 'ffarad',
|
||||||
|
HENRY = 'henry',
|
||||||
|
MHENRY = 'mhenry',
|
||||||
|
µHENRY = 'µhenry',
|
||||||
|
LUMENS = 'lumens',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FlowFormats {
|
||||||
|
FLOWGPM = 'flowgpm',
|
||||||
|
FLOWCMS = 'flowcms',
|
||||||
|
FLOWCFS = 'flowcfs',
|
||||||
|
FLOWCFM = 'flowcfm',
|
||||||
|
LITREH = 'litreh',
|
||||||
|
FLOWLPM = 'flowlpm',
|
||||||
|
FLOWMLPM = 'flowmlpm',
|
||||||
|
LUX = 'lux',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ForceFormats {
|
||||||
|
FORCENM = 'forceNm',
|
||||||
|
FORCEKNM = 'forcekNm',
|
||||||
|
FORCEN = 'forceN',
|
||||||
|
FORCEKN = 'forcekN',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MassFormats {
|
||||||
|
MASSMG = 'massmg',
|
||||||
|
MASSG = 'massg',
|
||||||
|
MASSLB = 'masslb',
|
||||||
|
MASSKG = 'masskg',
|
||||||
|
MASST = 'masst',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum LengthFormats {
|
||||||
|
LENGTHMM = 'lengthmm',
|
||||||
|
LENGTHIN = 'lengthin',
|
||||||
|
LENGTHFT = 'lengthft',
|
||||||
|
LENGTHM = 'lengthm',
|
||||||
|
LENGTHKM = 'lengthkm',
|
||||||
|
LENGTHMI = 'lengthmi',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PressureFormats {
|
||||||
|
PRESSUREMBAR = 'pressurembar',
|
||||||
|
PRESSUREBAR = 'pressurebar',
|
||||||
|
PRESSUREKBAR = 'pressurekbar',
|
||||||
|
PRESSUREPA = 'pressurepa',
|
||||||
|
PRESSUREHPA = 'pressurehpa',
|
||||||
|
PRESSUREKPA = 'pressurekpa',
|
||||||
|
PRESSUREHG = 'pressurehg',
|
||||||
|
PRESSUREPSI = 'pressurepsi',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum RadiationFormats {
|
||||||
|
RADBQ = 'radbq',
|
||||||
|
RADCI = 'radci',
|
||||||
|
RADGY = 'radgy',
|
||||||
|
RADRAD = 'radrad',
|
||||||
|
RADSV = 'radsv',
|
||||||
|
RADMSV = 'radmsv',
|
||||||
|
RADUSV = 'radusv',
|
||||||
|
RADREM = 'radrem',
|
||||||
|
RADEXPCKG = 'radexpckg',
|
||||||
|
RADR = 'radr',
|
||||||
|
RADSVH = 'radsvh',
|
||||||
|
RADMSVH = 'radmsvh',
|
||||||
|
RADUSVH = 'radusvh',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum RotationSpeedFormats {
|
||||||
|
ROTRPM = 'rotrpm',
|
||||||
|
ROTHZ = 'rothz',
|
||||||
|
ROTRADS = 'rotrads',
|
||||||
|
ROTDEGS = 'rotdegs',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TemperatureFormats {
|
||||||
|
CELSIUS = 'celsius',
|
||||||
|
FAHRENHEIT = 'fahrenheit',
|
||||||
|
KELVIN = 'kelvin',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VelocityFormats {
|
||||||
|
METERS_PER_SECOND = 'velocityms',
|
||||||
|
KILOMETERS_PER_HOUR = 'velocitykmh',
|
||||||
|
MILES_PER_HOUR = 'velocitymph',
|
||||||
|
KNOT = 'velocityknot',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VolumeFormats {
|
||||||
|
MILLILITRE = 'mlitre',
|
||||||
|
LITRE = 'litre',
|
||||||
|
CUBIC_METER = 'm3',
|
||||||
|
NORMAL_CUBIC_METER = 'Nm3',
|
||||||
|
CUBIC_DECIMETER = 'dm3',
|
||||||
|
GALLONS = 'gallons',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BooleanFormats {
|
||||||
|
TRUE_FALSE = 'bool',
|
||||||
|
YES_NO = 'bool_yes_no',
|
||||||
|
ON_OFF = 'bool_on_off',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Format = {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Category = {
|
||||||
|
name: string;
|
||||||
|
formats: Format[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DataTypeCategories = Category[];
|
@ -67,7 +67,9 @@ function NewWidget({ selectedGraph, saveSettingOfPanel }: Props): JSX.Element {
|
|||||||
|
|
||||||
const selectedWidget = getWidget();
|
const selectedWidget = getWidget();
|
||||||
|
|
||||||
const [title, setTitle] = useState<string>(selectedWidget?.title || '');
|
const [title, setTitle] = useState<string>(
|
||||||
|
selectedWidget?.title?.toString() || '',
|
||||||
|
);
|
||||||
const [description, setDescription] = useState<string>(
|
const [description, setDescription] = useState<string>(
|
||||||
selectedWidget?.description || '',
|
selectedWidget?.description || '',
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
import { Select, SelectProps, Space } from 'antd';
|
||||||
|
import { getCategorySelectOptionByName } from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||||
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
|
||||||
|
import { categoryToSupport } from './config';
|
||||||
|
import { DefaultLabel, selectStyles } from './styles';
|
||||||
|
import { IBuilderUnitsFilterProps } from './types';
|
||||||
|
import { filterOption } from './utils';
|
||||||
|
|
||||||
|
function BuilderUnitsFilter({
|
||||||
|
onChange,
|
||||||
|
}: IBuilderUnitsFilterProps): JSX.Element {
|
||||||
|
const { currentQuery, handleOnUnitsChange } = useQueryBuilder();
|
||||||
|
|
||||||
|
const selectedValue = currentQuery?.unit;
|
||||||
|
|
||||||
|
const allOptions = categoryToSupport.map((category) => ({
|
||||||
|
label: category,
|
||||||
|
options: getCategorySelectOptionByName(category),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const onChangeHandler: SelectProps['onChange'] = (value): void => {
|
||||||
|
if (onChange) {
|
||||||
|
onChange(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnUnitsChange(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<DefaultLabel>Y-axis unit</DefaultLabel>
|
||||||
|
<Select
|
||||||
|
style={selectStyles}
|
||||||
|
onChange={onChangeHandler}
|
||||||
|
value={selectedValue}
|
||||||
|
options={allOptions}
|
||||||
|
allowClear
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
placeholder="Select unit"
|
||||||
|
filterOption={filterOption}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { BuilderUnitsFilter };
|
@ -0,0 +1,10 @@
|
|||||||
|
import { CategoryNames } from 'container/NewWidget/RightContainer/types';
|
||||||
|
|
||||||
|
export const categoryToSupport = [
|
||||||
|
CategoryNames.Data,
|
||||||
|
CategoryNames.DataRate,
|
||||||
|
CategoryNames.Time,
|
||||||
|
CategoryNames.Throughput,
|
||||||
|
CategoryNames.Miscellaneous,
|
||||||
|
CategoryNames.Boolean,
|
||||||
|
];
|
@ -0,0 +1 @@
|
|||||||
|
export { BuilderUnitsFilter } from './BuilderUnits';
|
@ -0,0 +1,11 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const selectStyles: React.CSSProperties = {
|
||||||
|
minWidth: '10rem',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultLabel = styled.label`
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
`;
|
@ -0,0 +1,3 @@
|
|||||||
|
export interface IBuilderUnitsFilterProps {
|
||||||
|
onChange?: (value: string) => void;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
import { DefaultOptionType } from 'antd/es/select';
|
||||||
|
|
||||||
|
export const filterOption = (
|
||||||
|
inputValue: string,
|
||||||
|
option: DefaultOptionType['options'][number],
|
||||||
|
): boolean => option.label.toLowerCase().includes(inputValue.toLowerCase());
|
@ -125,7 +125,7 @@ function QueryBuilderSearch({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialTagFilters: TagFilter = { items: [], op: 'AND' };
|
const initialTagFilters: TagFilter = { items: [], op: 'AND' };
|
||||||
const initialSourceKeys = query.filters.items.map(
|
const initialSourceKeys = query.filters.items?.map(
|
||||||
(item) => item.key as BaseAutocompleteData,
|
(item) => item.key as BaseAutocompleteData,
|
||||||
);
|
);
|
||||||
initialTagFilters.items = tags.map((tag) => {
|
initialTagFilters.items = tags.map((tag) => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export { AggregatorFilter } from './AggregatorFilter';
|
export { AggregatorFilter } from './AggregatorFilter';
|
||||||
|
export { BuilderUnitsFilter } from './BuilderUnitsFilter';
|
||||||
export { GroupByFilter } from './GroupByFilter';
|
export { GroupByFilter } from './GroupByFilter';
|
||||||
export { HavingFilter } from './HavingFilter';
|
export { HavingFilter } from './HavingFilter';
|
||||||
export { OperatorsSelect } from './OperatorsSelect';
|
export { OperatorsSelect } from './OperatorsSelect';
|
||||||
|
12
frontend/src/hooks/apDex/useGetApDexSettings.ts
Normal file
12
frontend/src/hooks/apDex/useGetApDexSettings.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { getApDexSettings } from 'api/metrics/ApDex/getApDexSettings';
|
||||||
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
|
import { useQuery, UseQueryResult } from 'react-query';
|
||||||
|
import { ApDexPayloadAndSettingsProps } from 'types/api/metrics/getApDex';
|
||||||
|
|
||||||
|
export const useGetApDexSettings = (
|
||||||
|
servicename: string,
|
||||||
|
): UseQueryResult<AxiosResponse<ApDexPayloadAndSettingsProps[]>, AxiosError> =>
|
||||||
|
useQuery<AxiosResponse<ApDexPayloadAndSettingsProps[]>, AxiosError>({
|
||||||
|
queryKey: [{ servicename }],
|
||||||
|
queryFn: async () => getApDexSettings(servicename),
|
||||||
|
});
|
12
frontend/src/hooks/apDex/useGetMetricMeta.ts
Normal file
12
frontend/src/hooks/apDex/useGetMetricMeta.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { getMetricMeta } from 'api/metrics/ApDex/getMetricMeta';
|
||||||
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
|
import { useQuery, UseQueryResult } from 'react-query';
|
||||||
|
import { MetricMetaProps } from 'types/api/metrics/getApDex';
|
||||||
|
|
||||||
|
export const useGetMetricMeta = (
|
||||||
|
metricName: string,
|
||||||
|
): UseQueryResult<AxiosResponse<MetricMetaProps>, AxiosError> =>
|
||||||
|
useQuery<AxiosResponse<MetricMetaProps>, AxiosError>({
|
||||||
|
queryKey: [{ metricName }],
|
||||||
|
queryFn: async () => getMetricMeta(metricName),
|
||||||
|
});
|
21
frontend/src/hooks/apDex/useSetApDexSettings.ts
Normal file
21
frontend/src/hooks/apDex/useSetApDexSettings.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { setApDexSettings } from 'api/metrics/ApDex/apDexSettings';
|
||||||
|
import { useMutation, UseMutationResult } from 'react-query';
|
||||||
|
import {
|
||||||
|
ApDexPayloadAndSettingsProps,
|
||||||
|
SetApDexPayloadProps,
|
||||||
|
} from 'types/api/metrics/getApDex';
|
||||||
|
|
||||||
|
export const useSetApDexSettings = ({
|
||||||
|
servicename,
|
||||||
|
threshold,
|
||||||
|
excludeStatusCode,
|
||||||
|
}: ApDexPayloadAndSettingsProps): UseMutationResult<
|
||||||
|
SetApDexPayloadProps,
|
||||||
|
Error,
|
||||||
|
ApDexPayloadAndSettingsProps
|
||||||
|
> =>
|
||||||
|
useMutation<SetApDexPayloadProps, Error, ApDexPayloadAndSettingsProps>({
|
||||||
|
mutationKey: [servicename, threshold.toString(), excludeStatusCode],
|
||||||
|
mutationFn: async () =>
|
||||||
|
setApDexSettings({ servicename, threshold, excludeStatusCode }),
|
||||||
|
});
|
@ -1,20 +1,20 @@
|
|||||||
import { OPERATORS, QueryBuilderKeys } from 'constants/queryBuilder';
|
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
||||||
|
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
|
import { getOperatorValue } from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { getGeneratedFilterQueryString } from 'lib/getGeneratedFilterQueryString';
|
import { getGeneratedFilterQueryString } from 'lib/getGeneratedFilterQueryString';
|
||||||
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useQueryClient } from 'react-query';
|
import { useQueryClient } from 'react-query';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useHistory, useLocation } from 'react-router-dom';
|
import { useHistory, useLocation } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
||||||
import { SuccessResponse } from 'types/api';
|
|
||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
import {
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
BaseAutocompleteData,
|
|
||||||
IQueryAutocompleteResponse,
|
|
||||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { ILogsReducer } from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
@ -31,6 +31,9 @@ export const useActiveLog = (): UseActiveLog => {
|
|||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
|
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
|
const { t } = useTranslation('common');
|
||||||
|
|
||||||
const isLogsPage = useMemo(() => pathname === ROUTES.LOGS, [pathname]);
|
const isLogsPage = useMemo(() => pathname === ROUTES.LOGS, [pathname]);
|
||||||
|
|
||||||
@ -60,48 +63,64 @@ export const useActiveLog = (): UseActiveLog => {
|
|||||||
const onClearActiveLog = useCallback((): void => setActiveLog(null), []);
|
const onClearActiveLog = useCallback((): void => setActiveLog(null), []);
|
||||||
|
|
||||||
const onAddToQueryExplorer = useCallback(
|
const onAddToQueryExplorer = useCallback(
|
||||||
(fieldKey: string, fieldValue: string, operator: string): void => {
|
async (
|
||||||
const keysAutocomplete: BaseAutocompleteData[] =
|
fieldKey: string,
|
||||||
queryClient.getQueryData<SuccessResponse<IQueryAutocompleteResponse>>(
|
fieldValue: string,
|
||||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS],
|
operator: string,
|
||||||
{ exact: false },
|
): Promise<void> => {
|
||||||
)?.payload.attributeKeys || [];
|
try {
|
||||||
|
const keysAutocompleteResponse = await queryClient.fetchQuery(
|
||||||
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS, fieldKey],
|
||||||
|
async () =>
|
||||||
|
getAggregateKeys({
|
||||||
|
searchText: fieldKey,
|
||||||
|
aggregateOperator: currentQuery.builder.queryData[0].aggregateOperator,
|
||||||
|
dataSource: currentQuery.builder.queryData[0].dataSource,
|
||||||
|
aggregateAttribute:
|
||||||
|
currentQuery.builder.queryData[0].aggregateAttribute.key,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const existAutocompleteKey = chooseAutocompleteFromCustomValue(
|
const keysAutocomplete: BaseAutocompleteData[] =
|
||||||
keysAutocomplete,
|
keysAutocompleteResponse.payload?.attributeKeys || [];
|
||||||
fieldKey,
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentOperator =
|
const existAutocompleteKey = chooseAutocompleteFromCustomValue(
|
||||||
Object.keys(OPERATORS).find((op) => op === operator) || '';
|
keysAutocomplete,
|
||||||
|
fieldKey,
|
||||||
|
);
|
||||||
|
|
||||||
const nextQuery: Query = {
|
const currentOperator = getOperatorValue(operator);
|
||||||
...currentQuery,
|
|
||||||
builder: {
|
|
||||||
...currentQuery.builder,
|
|
||||||
queryData: currentQuery.builder.queryData.map((item) => ({
|
|
||||||
...item,
|
|
||||||
filters: {
|
|
||||||
...item.filters,
|
|
||||||
items: [
|
|
||||||
...item.filters.items.filter(
|
|
||||||
(item) => item.key?.id !== existAutocompleteKey.id,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
id: uuid(),
|
|
||||||
key: existAutocompleteKey,
|
|
||||||
op: currentOperator,
|
|
||||||
value: fieldValue,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
redirectWithQueryBuilderData(nextQuery);
|
const nextQuery: Query = {
|
||||||
|
...currentQuery,
|
||||||
|
builder: {
|
||||||
|
...currentQuery.builder,
|
||||||
|
queryData: currentQuery.builder.queryData.map((item) => ({
|
||||||
|
...item,
|
||||||
|
filters: {
|
||||||
|
...item.filters,
|
||||||
|
items: [
|
||||||
|
...item.filters.items.filter(
|
||||||
|
(item) => item.key?.id !== existAutocompleteKey.id,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
id: uuid(),
|
||||||
|
key: existAutocompleteKey,
|
||||||
|
op: currentOperator,
|
||||||
|
value: fieldValue,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
redirectWithQueryBuilderData(nextQuery);
|
||||||
|
} catch {
|
||||||
|
notifications.error({ message: t('something_went_wrong') });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[currentQuery, queryClient, redirectWithQueryBuilderData],
|
[currentQuery, notifications, queryClient, redirectWithQueryBuilderData, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onAddToQueryLogs = useCallback(
|
const onAddToQueryLogs = useCallback(
|
||||||
|
13
frontend/src/hooks/useFeatureFlag/utils.test.ts
Normal file
13
frontend/src/hooks/useFeatureFlag/utils.test.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { FeatureKeys } from 'constants/features';
|
||||||
|
|
||||||
|
import { isFeatureKeys } from './utils';
|
||||||
|
|
||||||
|
describe('Feature Keys', () => {
|
||||||
|
it('should return true for a valid feature key', () => {
|
||||||
|
expect(isFeatureKeys(FeatureKeys.ALERT_CHANNEL_MSTEAMS)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for an invalid feature key', () => {
|
||||||
|
expect(isFeatureKeys('invalid')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
4
frontend/src/hooks/useFeatureFlag/utils.ts
Normal file
4
frontend/src/hooks/useFeatureFlag/utils.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { FeatureKeys } from 'constants/features';
|
||||||
|
|
||||||
|
export const isFeatureKeys = (key: string): key is keyof typeof FeatureKeys =>
|
||||||
|
Object.keys(FeatureKeys).includes(key);
|
@ -9,8 +9,9 @@ export const chooseAutocompleteFromCustomValue = (
|
|||||||
(sourceAutoComplete) => value === sourceAutoComplete.key,
|
(sourceAutoComplete) => value === sourceAutoComplete.key,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!firstBaseAutoCompleteValue)
|
if (!firstBaseAutoCompleteValue) {
|
||||||
return { ...initialAutocompleteData, key: value };
|
return { ...initialAutocompleteData, key: value, dataType: 'string' };
|
||||||
|
}
|
||||||
|
|
||||||
return firstBaseAutoCompleteValue;
|
return firstBaseAutoCompleteValue;
|
||||||
};
|
};
|
||||||
|
@ -33,5 +33,6 @@ export const mapQueryDataFromApi = (
|
|||||||
clickhouse_sql: clickhouseSql,
|
clickhouse_sql: clickhouseSql,
|
||||||
queryType: compositeQuery.queryType,
|
queryType: compositeQuery.queryType,
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
|
unit: compositeQuery.unit,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,8 @@ import { Typography } from 'antd';
|
|||||||
import get from 'api/channels/get';
|
import get from 'api/channels/get';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import {
|
import {
|
||||||
|
MsTeamsChannel,
|
||||||
|
MsTeamsType,
|
||||||
PagerChannel,
|
PagerChannel,
|
||||||
PagerType,
|
PagerType,
|
||||||
SlackChannel,
|
SlackChannel,
|
||||||
@ -39,9 +41,11 @@ function ChannelsEdit(): JSX.Element {
|
|||||||
|
|
||||||
const prepChannelConfig = (): {
|
const prepChannelConfig = (): {
|
||||||
type: string;
|
type: string;
|
||||||
channel: SlackChannel & WebhookChannel & PagerChannel;
|
channel: SlackChannel & WebhookChannel & PagerChannel & MsTeamsChannel;
|
||||||
} => {
|
} => {
|
||||||
let channel: SlackChannel & WebhookChannel & PagerChannel = { name: '' };
|
let channel: SlackChannel & WebhookChannel & PagerChannel & MsTeamsChannel = {
|
||||||
|
name: '',
|
||||||
|
};
|
||||||
if (value && 'slack_configs' in value) {
|
if (value && 'slack_configs' in value) {
|
||||||
const slackConfig = value.slack_configs[0];
|
const slackConfig = value.slack_configs[0];
|
||||||
channel = slackConfig;
|
channel = slackConfig;
|
||||||
@ -50,6 +54,15 @@ function ChannelsEdit(): JSX.Element {
|
|||||||
channel,
|
channel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value && 'msteams_configs' in value) {
|
||||||
|
const msteamsConfig = value.msteams_configs[0];
|
||||||
|
channel = msteamsConfig;
|
||||||
|
return {
|
||||||
|
type: MsTeamsType,
|
||||||
|
channel,
|
||||||
|
};
|
||||||
|
}
|
||||||
if (value && 'pagerduty_configs' in value) {
|
if (value && 'pagerduty_configs' in value) {
|
||||||
const pagerConfig = value.pagerduty_configs[0];
|
const pagerConfig = value.pagerduty_configs[0];
|
||||||
channel = pagerConfig;
|
channel = pagerConfig;
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { APPLICATION_SETTINGS } from '../constants';
|
||||||
|
import { thresholdMockData } from './__mock__/thresholdMockData';
|
||||||
|
import ApDexApplication from './ApDexApplication';
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useParams: (): {
|
||||||
|
servicename: string;
|
||||||
|
} => ({ servicename: 'mockServiceName' }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('hooks/apDex/useGetApDexSettings', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
useGetApDexSettings: jest.fn().mockReturnValue({
|
||||||
|
data: thresholdMockData,
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
refetch: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('hooks/apDex/useSetApDexSettings', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
useSetApDexSettings: jest.fn().mockReturnValue({
|
||||||
|
mutateAsync: jest.fn(),
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('ApDexApplication', () => {
|
||||||
|
it('should render the component', () => {
|
||||||
|
render(<ApDexApplication />);
|
||||||
|
|
||||||
|
expect(screen.getByText('Settings')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open the popover when the settings button is clicked', async () => {
|
||||||
|
render(<ApDexApplication />);
|
||||||
|
|
||||||
|
const button = screen.getByText('Settings');
|
||||||
|
fireEvent.click(button);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(APPLICATION_SETTINGS)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the popover when the close button is clicked', async () => {
|
||||||
|
render(<ApDexApplication />);
|
||||||
|
|
||||||
|
const button = screen.getByText('Settings');
|
||||||
|
fireEvent.click(button);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(APPLICATION_SETTINGS)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeButton = screen.getByText('Cancel');
|
||||||
|
fireEvent.click(closeButton);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByText(APPLICATION_SETTINGS)).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
import { SettingOutlined } from '@ant-design/icons';
|
||||||
|
import { Popover } from 'antd';
|
||||||
|
import { IServiceName } from 'container/MetricsApplication/Tabs/types';
|
||||||
|
import { useGetApDexSettings } from 'hooks/apDex/useGetApDexSettings';
|
||||||
|
import useErrorNotification from 'hooks/useErrorNotification';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { Button } from '../styles';
|
||||||
|
import ApDexSettings from './ApDexSettings';
|
||||||
|
|
||||||
|
function ApDexApplication(): JSX.Element {
|
||||||
|
const { servicename } = useParams<IServiceName>();
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
refetch: refetchGetApDexSetting,
|
||||||
|
} = useGetApDexSettings(servicename);
|
||||||
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
|
useErrorNotification(error);
|
||||||
|
|
||||||
|
const handlePopOverClose = (): void => {
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenChange = (newOpen: boolean): void => {
|
||||||
|
setIsOpen(newOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
placement="bottomRight"
|
||||||
|
destroyTooltipOnHide
|
||||||
|
trigger={['click']}
|
||||||
|
showArrow={false}
|
||||||
|
open={isOpen}
|
||||||
|
onOpenChange={handleOpenChange}
|
||||||
|
content={
|
||||||
|
<ApDexSettings
|
||||||
|
servicename={servicename}
|
||||||
|
handlePopOverClose={handlePopOverClose}
|
||||||
|
isLoading={isLoading}
|
||||||
|
data={data}
|
||||||
|
refetchGetApDexSetting={refetchGetApDexSetting}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button size="middle" icon={<SettingOutlined />}>
|
||||||
|
Settings
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApDexApplication;
|
@ -0,0 +1,62 @@
|
|||||||
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { axiosResponseThresholdData } from './__mock__/axiosResponseMockThresholdData';
|
||||||
|
import ApDexSettings from './ApDexSettings';
|
||||||
|
|
||||||
|
jest.mock('hooks/apDex/useSetApDexSettings', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
useSetApDexSettings: jest.fn().mockReturnValue({
|
||||||
|
mutateAsync: jest.fn(),
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('ApDexSettings', () => {
|
||||||
|
it('should render the component', () => {
|
||||||
|
render(
|
||||||
|
<ApDexSettings
|
||||||
|
servicename="mockServiceName"
|
||||||
|
handlePopOverClose={jest.fn()}
|
||||||
|
isLoading={false}
|
||||||
|
data={axiosResponseThresholdData}
|
||||||
|
refetchGetApDexSetting={jest.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Application Settings')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the spinner when the data is loading', () => {
|
||||||
|
render(
|
||||||
|
<ApDexSettings
|
||||||
|
servicename="mockServiceName"
|
||||||
|
handlePopOverClose={jest.fn()}
|
||||||
|
isLoading
|
||||||
|
data={axiosResponseThresholdData}
|
||||||
|
refetchGetApDexSetting={jest.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the popover when the cancel button is clicked', async () => {
|
||||||
|
const mockHandlePopOverClose = jest.fn();
|
||||||
|
render(
|
||||||
|
<ApDexSettings
|
||||||
|
servicename="mockServiceName"
|
||||||
|
handlePopOverClose={mockHandlePopOverClose}
|
||||||
|
isLoading={false}
|
||||||
|
data={axiosResponseThresholdData}
|
||||||
|
refetchGetApDexSetting={jest.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByText('Cancel');
|
||||||
|
fireEvent.click(button);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockHandlePopOverClose).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
119
frontend/src/pages/MetricsApplication/ApDex/ApDexSettings.tsx
Normal file
119
frontend/src/pages/MetricsApplication/ApDex/ApDexSettings.tsx
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
|
import { Card, InputNumber } from 'antd';
|
||||||
|
import Spinner from 'components/Spinner';
|
||||||
|
import TextToolTip from 'components/TextToolTip';
|
||||||
|
import {
|
||||||
|
apDexToolTipText,
|
||||||
|
apDexToolTipUrl,
|
||||||
|
apDexToolTipUrlText,
|
||||||
|
} from 'constants/apDex';
|
||||||
|
import { themeColors } from 'constants/theme';
|
||||||
|
import { useSetApDexSettings } from 'hooks/apDex/useSetApDexSettings';
|
||||||
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { APPLICATION_SETTINGS } from '../constants';
|
||||||
|
import {
|
||||||
|
AppDexThresholdContainer,
|
||||||
|
Button,
|
||||||
|
SaveAndCancelContainer,
|
||||||
|
SaveButton,
|
||||||
|
Typography,
|
||||||
|
} from '../styles';
|
||||||
|
import { onSaveApDexSettings } from '../utils';
|
||||||
|
import { ApDexSettingsProps } from './types';
|
||||||
|
|
||||||
|
function ApDexSettings({
|
||||||
|
servicename,
|
||||||
|
handlePopOverClose,
|
||||||
|
isLoading,
|
||||||
|
data,
|
||||||
|
refetchGetApDexSetting,
|
||||||
|
}: ApDexSettingsProps): JSX.Element {
|
||||||
|
const [thresholdValue, setThresholdValue] = useState(() => {
|
||||||
|
if (data) {
|
||||||
|
return data.data[0].threshold;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
|
const { isLoading: isApDexLoading, mutateAsync } = useSetApDexSettings({
|
||||||
|
servicename,
|
||||||
|
threshold: thresholdValue,
|
||||||
|
excludeStatusCode: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleThreadholdChange = (value: number | null): void => {
|
||||||
|
if (value !== null) {
|
||||||
|
setThresholdValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<Typography.Text style={{ color: themeColors.white }}>
|
||||||
|
<Spinner height="5vh" tip="Loading..." />
|
||||||
|
</Typography.Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={APPLICATION_SETTINGS}
|
||||||
|
extra={<CloseOutlined width={10} height={10} onClick={handlePopOverClose} />}
|
||||||
|
actions={[
|
||||||
|
<SaveAndCancelContainer key="SaveAndCancelContainer">
|
||||||
|
<Button onClick={handlePopOverClose}>Cancel</Button>
|
||||||
|
<SaveButton
|
||||||
|
onClick={onSaveApDexSettings({
|
||||||
|
handlePopOverClose,
|
||||||
|
mutateAsync,
|
||||||
|
notifications,
|
||||||
|
refetchGetApDexSetting,
|
||||||
|
servicename,
|
||||||
|
thresholdValue,
|
||||||
|
})}
|
||||||
|
type="primary"
|
||||||
|
loading={isApDexLoading}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</SaveButton>
|
||||||
|
</SaveAndCancelContainer>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<AppDexThresholdContainer>
|
||||||
|
<Typography>
|
||||||
|
Apdex threshold (in seconds){' '}
|
||||||
|
<TextToolTip
|
||||||
|
text={apDexToolTipText}
|
||||||
|
url={apDexToolTipUrl}
|
||||||
|
useFilledIcon={false}
|
||||||
|
urlText={apDexToolTipUrlText}
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
|
<InputNumber
|
||||||
|
value={thresholdValue}
|
||||||
|
onChange={handleThreadholdChange}
|
||||||
|
min={0}
|
||||||
|
step={0.1}
|
||||||
|
/>
|
||||||
|
</AppDexThresholdContainer>
|
||||||
|
{/* TODO: Add this feature later when backend is ready to support it. */}
|
||||||
|
{/* <ExcludeErrorCodeContainer>
|
||||||
|
<Typography.Text>
|
||||||
|
Exclude following error codes from error rate calculation
|
||||||
|
</Typography.Text>
|
||||||
|
<Input placeholder="e.g. 406, 418" />
|
||||||
|
</ExcludeErrorCodeContainer> */}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApDexSettings.defaultProps = {
|
||||||
|
isLoading: undefined,
|
||||||
|
data: undefined,
|
||||||
|
refetchGetApDexSetting: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ApDexSettings;
|
@ -0,0 +1,9 @@
|
|||||||
|
import { AxiosResponse } from 'axios';
|
||||||
|
|
||||||
|
export const axiosResponseThresholdData = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
threshold: 0.5,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as AxiosResponse;
|
@ -0,0 +1,7 @@
|
|||||||
|
export const thresholdMockData = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
threshold: 0.5,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
10
frontend/src/pages/MetricsApplication/ApDex/types.ts
Normal file
10
frontend/src/pages/MetricsApplication/ApDex/types.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { AxiosResponse } from 'axios';
|
||||||
|
import { ApDexPayloadAndSettingsProps } from 'types/api/metrics/getApDex';
|
||||||
|
|
||||||
|
export interface ApDexSettingsProps {
|
||||||
|
servicename: string;
|
||||||
|
handlePopOverClose: () => void;
|
||||||
|
isLoading?: boolean;
|
||||||
|
data?: AxiosResponse<ApDexPayloadAndSettingsProps[]>;
|
||||||
|
refetchGetApDexSetting?: () => void;
|
||||||
|
}
|
1
frontend/src/pages/MetricsApplication/constants.ts
Normal file
1
frontend/src/pages/MetricsApplication/constants.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const APPLICATION_SETTINGS = 'Application Settings';
|
@ -8,6 +8,7 @@ import history from 'lib/history';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { generatePath, useParams } from 'react-router-dom';
|
import { generatePath, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import ApDexApplication from './ApDex/ApDexApplication';
|
||||||
import { MetricsApplicationTab, TAB_KEY_VS_LABEL } from './types';
|
import { MetricsApplicationTab, TAB_KEY_VS_LABEL } from './types';
|
||||||
import useMetricsApplicationTabKey from './useMetricsApplicationTabKey';
|
import useMetricsApplicationTabKey from './useMetricsApplicationTabKey';
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ function MetricsApplication(): JSX.Element {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ResourceAttributesFilter />
|
<ResourceAttributesFilter />
|
||||||
|
<ApDexApplication />
|
||||||
<RouteTab routes={routes} history={history} activeKey={activeKey} />
|
<RouteTab routes={routes} history={history} activeKey={activeKey} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
44
frontend/src/pages/MetricsApplication/styles.ts
Normal file
44
frontend/src/pages/MetricsApplication/styles.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
Button as ButtonComponent,
|
||||||
|
Typography as TypographyComponent,
|
||||||
|
} from 'antd';
|
||||||
|
import { themeColors } from 'constants/theme';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Button = styled(ButtonComponent)`
|
||||||
|
&&& {
|
||||||
|
width: min-content;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const AppDexThresholdContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Typography = styled(TypographyComponent)`
|
||||||
|
&&& {
|
||||||
|
width: 24rem;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
color: ${themeColors.white};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SaveAndCancelContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-right: 1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SaveButton = styled(ButtonComponent)`
|
||||||
|
&&& {
|
||||||
|
margin: 0 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ExcludeErrorCodeContainer = styled.div`
|
||||||
|
margin: 1rem 0;
|
||||||
|
`;
|
@ -1,3 +1,10 @@
|
|||||||
|
import { NotificationInstance } from 'antd/es/notification/interface';
|
||||||
|
import { UseMutateAsyncFunction } from 'react-query';
|
||||||
|
import {
|
||||||
|
ApDexPayloadAndSettingsProps,
|
||||||
|
SetApDexPayloadProps,
|
||||||
|
} from 'types/api/metrics/getApDex';
|
||||||
|
|
||||||
export enum MetricsApplicationTab {
|
export enum MetricsApplicationTab {
|
||||||
OVER_METRICS = 'OVER_METRICS',
|
OVER_METRICS = 'OVER_METRICS',
|
||||||
DB_CALL_METRICS = 'DB_CALL_METRICS',
|
DB_CALL_METRICS = 'DB_CALL_METRICS',
|
||||||
@ -9,3 +16,16 @@ export const TAB_KEY_VS_LABEL = {
|
|||||||
[MetricsApplicationTab.DB_CALL_METRICS]: 'DB Call Metrics',
|
[MetricsApplicationTab.DB_CALL_METRICS]: 'DB Call Metrics',
|
||||||
[MetricsApplicationTab.EXTERNAL_METRICS]: 'External Metrics',
|
[MetricsApplicationTab.EXTERNAL_METRICS]: 'External Metrics',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface OnSaveApDexSettingsProps {
|
||||||
|
thresholdValue: number;
|
||||||
|
servicename: string;
|
||||||
|
notifications: NotificationInstance;
|
||||||
|
refetchGetApDexSetting?: VoidFunction;
|
||||||
|
mutateAsync: UseMutateAsyncFunction<
|
||||||
|
SetApDexPayloadProps,
|
||||||
|
Error,
|
||||||
|
ApDexPayloadAndSettingsProps
|
||||||
|
>;
|
||||||
|
handlePopOverClose: VoidFunction;
|
||||||
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
|
|
||||||
import { TAB_KEYS_VS_METRICS_APPLICATION_KEY } from './config';
|
import { TAB_KEYS_VS_METRICS_APPLICATION_KEY } from './config';
|
||||||
import { MetricsApplicationTab } from './types';
|
import { MetricsApplicationTab, OnSaveApDexSettingsProps } from './types';
|
||||||
|
|
||||||
export const isMetricsApplicationTab = (
|
export const isMetricsApplicationTab = (
|
||||||
tab: string,
|
tab: string,
|
||||||
@ -15,3 +18,29 @@ export const getMetricsApplicationKey = (
|
|||||||
|
|
||||||
return MetricsApplicationTab.OVER_METRICS;
|
return MetricsApplicationTab.OVER_METRICS;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const onSaveApDexSettings = ({
|
||||||
|
thresholdValue,
|
||||||
|
refetchGetApDexSetting,
|
||||||
|
mutateAsync,
|
||||||
|
notifications,
|
||||||
|
handlePopOverClose,
|
||||||
|
servicename,
|
||||||
|
}: OnSaveApDexSettingsProps) => async (): Promise<void> => {
|
||||||
|
if (!refetchGetApDexSetting) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mutateAsync({
|
||||||
|
servicename,
|
||||||
|
threshold: thresholdValue,
|
||||||
|
excludeStatusCode: '',
|
||||||
|
});
|
||||||
|
await refetchGetApDexSetting();
|
||||||
|
} catch (err) {
|
||||||
|
notifications.error({
|
||||||
|
message: axios.isAxiosError(err) ? err.message : SOMETHING_WENT_WRONG,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
handlePopOverClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -71,6 +71,7 @@ export const QueryBuilderContext = createContext<QueryBuilderContextType>({
|
|||||||
updateAllQueriesOperators: () => initialQueriesMap.metrics,
|
updateAllQueriesOperators: () => initialQueriesMap.metrics,
|
||||||
updateQueriesData: () => initialQueriesMap.metrics,
|
updateQueriesData: () => initialQueriesMap.metrics,
|
||||||
initQueryBuilderData: () => {},
|
initQueryBuilderData: () => {},
|
||||||
|
handleOnUnitsChange: () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function QueryBuilderProvider({
|
export function QueryBuilderProvider({
|
||||||
@ -176,6 +177,7 @@ export function QueryBuilderProvider({
|
|||||||
queryData: setupedQueryData,
|
queryData: setupedQueryData,
|
||||||
},
|
},
|
||||||
id: query.id,
|
id: query.id,
|
||||||
|
unit: query.unit,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nextQuery: Query = {
|
const nextQuery: Query = {
|
||||||
@ -474,6 +476,7 @@ export function QueryBuilderProvider({
|
|||||||
promql,
|
promql,
|
||||||
clickhouse_sql: clickhouseSql,
|
clickhouse_sql: clickhouseSql,
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
|
unit: query.unit || initialQueryState.unit,
|
||||||
};
|
};
|
||||||
|
|
||||||
urlQuery.set(
|
urlQuery.set(
|
||||||
@ -513,6 +516,7 @@ export function QueryBuilderProvider({
|
|||||||
promql: currentQuery.promql,
|
promql: currentQuery.promql,
|
||||||
id: currentQuery.id,
|
id: currentQuery.id,
|
||||||
queryType,
|
queryType,
|
||||||
|
unit: currentQuery.unit,
|
||||||
},
|
},
|
||||||
maxTime,
|
maxTime,
|
||||||
minTime,
|
minTime,
|
||||||
@ -550,6 +554,16 @@ export function QueryBuilderProvider({
|
|||||||
stagedQuery,
|
stagedQuery,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const handleOnUnitsChange = useCallback(
|
||||||
|
(unit: string) => {
|
||||||
|
setCurrentQuery((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
unit,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
[setCurrentQuery],
|
||||||
|
);
|
||||||
|
|
||||||
const query: Query = useMemo(
|
const query: Query = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...currentQuery,
|
...currentQuery,
|
||||||
@ -585,6 +599,7 @@ export function QueryBuilderProvider({
|
|||||||
updateAllQueriesOperators,
|
updateAllQueriesOperators,
|
||||||
updateQueriesData,
|
updateQueriesData,
|
||||||
initQueryBuilderData,
|
initQueryBuilderData,
|
||||||
|
handleOnUnitsChange,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
query,
|
query,
|
||||||
@ -607,6 +622,7 @@ export function QueryBuilderProvider({
|
|||||||
updateAllQueriesOperators,
|
updateAllQueriesOperators,
|
||||||
updateQueriesData,
|
updateQueriesData,
|
||||||
initQueryBuilderData,
|
initQueryBuilderData,
|
||||||
|
handleOnUnitsChange,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ export async function GetMetricQueryRange({
|
|||||||
compositeQuery: {
|
compositeQuery: {
|
||||||
queryType: query.queryType,
|
queryType: query.queryType,
|
||||||
panelType: graphType,
|
panelType: graphType,
|
||||||
|
unit: query?.unit,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
BuilderClickHouseResource,
|
BuilderClickHouseResource,
|
||||||
BuilderPromQLResource,
|
BuilderPromQLResource,
|
||||||
BuilderQueryDataResourse,
|
BuilderQueryDataResourse,
|
||||||
|
Query,
|
||||||
} from 'types/api/queryBuilder/queryBuilderData';
|
} from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
|
||||||
@ -12,4 +13,5 @@ export interface ICompositeMetricQuery {
|
|||||||
chQueries: BuilderClickHouseResource;
|
chQueries: BuilderClickHouseResource;
|
||||||
queryType: EQueryType;
|
queryType: EQueryType;
|
||||||
panelType: PANEL_TYPES;
|
panelType: PANEL_TYPES;
|
||||||
|
unit: Query['unit'];
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ export interface RuleCondition {
|
|||||||
op?: string | undefined;
|
op?: string | undefined;
|
||||||
target?: number | undefined;
|
target?: number | undefined;
|
||||||
matchType?: string | undefined;
|
matchType?: string | undefined;
|
||||||
|
targetUnit?: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Labels {
|
export interface Labels {
|
||||||
|
8
frontend/src/types/api/channels/createMsTeams.ts
Normal file
8
frontend/src/types/api/channels/createMsTeams.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { MsTeamsChannel } from 'container/CreateAlertChannels/config';
|
||||||
|
|
||||||
|
export type Props = MsTeamsChannel;
|
||||||
|
|
||||||
|
export interface PayloadProps {
|
||||||
|
data: string;
|
||||||
|
status: string;
|
||||||
|
}
|
10
frontend/src/types/api/channels/editMsTeams.ts
Normal file
10
frontend/src/types/api/channels/editMsTeams.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { MsTeamsChannel } from 'container/CreateAlertChannels/config';
|
||||||
|
|
||||||
|
export interface Props extends MsTeamsChannel {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PayloadProps {
|
||||||
|
data: string;
|
||||||
|
status: string;
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
import { Layout } from 'react-grid-layout';
|
import { Layout } from 'react-grid-layout';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ export interface IBaseWidget {
|
|||||||
isStacked: boolean;
|
isStacked: boolean;
|
||||||
id: string;
|
id: string;
|
||||||
panelTypes: PANEL_TYPES;
|
panelTypes: PANEL_TYPES;
|
||||||
title: string;
|
title: ReactNode;
|
||||||
description: string;
|
description: string;
|
||||||
opacity: string;
|
opacity: string;
|
||||||
nullZeroValues: string;
|
nullZeroValues: string;
|
||||||
|
14
frontend/src/types/api/metrics/getApDex.ts
Normal file
14
frontend/src/types/api/metrics/getApDex.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export interface ApDexPayloadAndSettingsProps {
|
||||||
|
servicename: string;
|
||||||
|
threshold: number;
|
||||||
|
excludeStatusCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SetApDexPayloadProps {
|
||||||
|
data: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MetricMetaProps {
|
||||||
|
delta: boolean;
|
||||||
|
le: number[];
|
||||||
|
}
|
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