mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-06 00:09:22 +00:00
feat: auto complete for promql legend, variables, filters, drilldown variables (#3153)
**1. **Add Variables:****  **2. **PromQL Legend:****  **3. **Filter Options:****  **4. **DrillDown PopUp:****   --------- Co-authored-by: ktx-abhay <abhay.padamani@kiara.tech> Co-authored-by: Kirtan Thakkar <kirtan@kiara.tech>
This commit is contained in:
co-authored by
ktx-abhay
Kirtan Thakkar
parent
f1b92ec40b
commit
f01035f915
@@ -590,7 +590,7 @@ export default defineComponent({
|
||||
|
||||
variablesData.value.values.forEach((variable: any) => {
|
||||
if (variable.type != "dynamic_filters") {
|
||||
drilldownVariables["var-" + variable.name] = variable.value;
|
||||
drilldownVariables[variable.name] = variable.value;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -653,11 +653,11 @@ export default defineComponent({
|
||||
pos > -1
|
||||
? window.location.origin +
|
||||
window.location.pathname.slice(0, pos) +
|
||||
"/web/"
|
||||
"/web"
|
||||
: window.location.origin;
|
||||
|
||||
// always, go to view dashboard page
|
||||
currentUrl += "dashboards/view?";
|
||||
currentUrl += "/dashboards/view?";
|
||||
|
||||
// if pass all variables in url
|
||||
currentUrl += drilldownData.data.passAllVariables
|
||||
@@ -670,7 +670,8 @@ export default defineComponent({
|
||||
drilldownData.data.variables.forEach((variable: any) => {
|
||||
if (variable?.name?.trim() && variable?.value?.trim()) {
|
||||
url.searchParams.set(
|
||||
replacePlaceholders(variable.name, drilldownVariables),
|
||||
"var-" +
|
||||
replacePlaceholders(variable.name, drilldownVariables),
|
||||
replacePlaceholders(variable.value, drilldownVariables)
|
||||
);
|
||||
}
|
||||
@@ -693,7 +694,8 @@ export default defineComponent({
|
||||
drilldownData.data.variables.forEach((variable: any) => {
|
||||
if (variable?.name?.trim() && variable?.value?.trim()) {
|
||||
oldParams[
|
||||
replacePlaceholders(variable.name, drilldownVariables)
|
||||
"var-" +
|
||||
replacePlaceholders(variable.name, drilldownVariables)
|
||||
] = replacePlaceholders(variable.value, drilldownVariables);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -298,6 +298,9 @@ export default defineComponent({
|
||||
const changeInitialVariableValues = async (
|
||||
newInitialVariableValues: any
|
||||
) => {
|
||||
// reject all promises
|
||||
rejectAllPromises();
|
||||
|
||||
// NOTE: need to re-initialize variables data
|
||||
resetVariablesData();
|
||||
|
||||
@@ -307,9 +310,6 @@ export default defineComponent({
|
||||
// make list of variables using variables config list
|
||||
initializeVariablesData();
|
||||
|
||||
// reject all promises
|
||||
rejectAllPromises();
|
||||
|
||||
// load all variables
|
||||
loadAllVariablesData();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div class="relative" style="margin-top: 5px;">
|
||||
<q-input
|
||||
v-model="inputValue"
|
||||
@update:model-value="onModelValueChanged"
|
||||
dense
|
||||
filled
|
||||
:label="label"
|
||||
style="width: 100%;"
|
||||
@focus="showOptions = true"
|
||||
@blur="hideOptions"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<template v-if="hasSlot('label')" v-slot:label>
|
||||
<slot name="label"></slot>
|
||||
</template>
|
||||
</q-input>
|
||||
<div
|
||||
class="options-container"
|
||||
v-if="showOptions && fieldsFilteredOptions.length > 0"
|
||||
:style="{
|
||||
'background-color': store.state.theme === 'dark' ? '#2d2d2d' : 'white',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-for="(option, index) in fieldsFilteredOptions"
|
||||
:key="index"
|
||||
class="option"
|
||||
@mousedown="selectOption(option)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, toRef, defineComponent, watch } from "vue";
|
||||
import { useSearchInputUsingRegex } from "@/composables/useSearchInputUsingRegex";
|
||||
import { useStore } from "vuex";
|
||||
import { useSlots } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "CommonAutoComplete",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
},
|
||||
items: {
|
||||
// always expected to be in [ { label: "", value: ""}] format, search in label, and replace the value
|
||||
type: Array,
|
||||
},
|
||||
searchRegex: {
|
||||
type: String,
|
||||
},
|
||||
valueReplaceFn: {
|
||||
// special treatment needed to any value replacing function, then this can be used
|
||||
type: Function,
|
||||
default: (selectedValue) => selectedValue,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, { emit }) {
|
||||
const store = useStore();
|
||||
const showOptions = ref(false);
|
||||
|
||||
// this is used to sync with modelvalue and q-input's v-model
|
||||
const inputValue = ref(props.modelValue);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
inputValue.value = props.modelValue;
|
||||
}
|
||||
);
|
||||
|
||||
// when q-input's model value updated, update the model value which will be synced back to input value
|
||||
const onModelValueChanged = (value) => {
|
||||
emit("update:modelValue", value);
|
||||
fieldsFilterFn(value);
|
||||
};
|
||||
|
||||
// apply filter on label
|
||||
const { filterFn: fieldsFilterFn, filteredOptions: fieldsFilteredOptions } =
|
||||
useSearchInputUsingRegex(toRef(props.items), "label", props.searchRegex);
|
||||
|
||||
const hideOptions = () => {
|
||||
showOptions.value = false;
|
||||
};
|
||||
|
||||
// when any item is selected, replace it with the value and hide the options
|
||||
const selectOption = (option) => {
|
||||
emit("update:modelValue", props.valueReplaceFn(option?.value));
|
||||
showOptions.value = false;
|
||||
};
|
||||
|
||||
const slots = useSlots();
|
||||
const hasSlot = (name) => {
|
||||
return !!slots[name];
|
||||
};
|
||||
|
||||
return {
|
||||
showOptions,
|
||||
fieldsFilterFn,
|
||||
fieldsFilteredOptions,
|
||||
hideOptions,
|
||||
selectOption,
|
||||
store,
|
||||
onModelValueChanged,
|
||||
inputValue,
|
||||
hasSlot
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.options-container {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border: 1px solid #ccc;
|
||||
max-height: 100px;
|
||||
overflow-y: auto;
|
||||
top: 42px;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.option {
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
background-color: #f0f0f0b1;
|
||||
}
|
||||
</style>
|
||||
@@ -497,23 +497,29 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<q-input
|
||||
<CommonAutoComplete
|
||||
v-if="promqlMode"
|
||||
:label="t('common.legend')"
|
||||
v-model="
|
||||
dashboardPanelData.data.queries[
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].config.promql_legend
|
||||
"
|
||||
:label="t('common.legend')"
|
||||
:items="dashboardSelectfieldPromQlList"
|
||||
searchRegex="(?:{([^}]*)(?:{.*})*$|([a-zA-Z-_]+)$)"
|
||||
color="input-border"
|
||||
bg-color="input-bg"
|
||||
class="q-py-md showLabelOnTop"
|
||||
class="showLabelOnTop"
|
||||
stack-label
|
||||
outlined
|
||||
filled
|
||||
dense
|
||||
label-slot
|
||||
data-test="dashboard-config-promql-legend"
|
||||
style="
|
||||
top: none !important;
|
||||
margin-top: none !important;
|
||||
padding-top: 3px !important;
|
||||
width: auto !important;
|
||||
"
|
||||
:value-replace-fn="selectPromQlNameOption"
|
||||
>
|
||||
<template v-slot:label>
|
||||
<div class="row items-center all-pointer-events">
|
||||
@@ -535,7 +541,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</q-input>
|
||||
</CommonAutoComplete>
|
||||
|
||||
<div class="space"></div>
|
||||
|
||||
@@ -735,10 +741,12 @@ import useDashboardPanelData from "@/composables/useDashboardPanel";
|
||||
import { computed, defineComponent, onBeforeMount } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import Drilldown from "./Drilldown.vue";
|
||||
import CommonAutoComplete from "@/components/dashboards/addPanel/CommonAutoComplete.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: { Drilldown },
|
||||
setup() {
|
||||
components: { Drilldown, CommonAutoComplete },
|
||||
props: ["dashboardPanelData"],
|
||||
setup(props) {
|
||||
const { dashboardPanelData, promqlMode } = useDashboardPanelData();
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -913,7 +921,30 @@ export default defineComponent({
|
||||
field[value] = key;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const selectPromQlNameOption = (option: any) => {
|
||||
const inputValue =
|
||||
dashboardPanelData.data.queries[
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].config.promql_legend;
|
||||
|
||||
const openingBraceIndex = inputValue.lastIndexOf("{");
|
||||
const newValue =
|
||||
inputValue.slice(0, openingBraceIndex + 1) + option + "}";
|
||||
return newValue;
|
||||
};
|
||||
|
||||
const dashboardSelectfieldPromQlList = computed(() =>
|
||||
props.dashboardPanelData.meta.stream.selectedStreamFields.map(
|
||||
(it: any) => {
|
||||
return {
|
||||
label: it.name,
|
||||
value: it.name,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
t,
|
||||
dashboardPanelData,
|
||||
@@ -927,6 +958,8 @@ export default defineComponent({
|
||||
setUnit,
|
||||
handleBlur,
|
||||
legendWidthValue,
|
||||
dashboardSelectfieldPromQlList,
|
||||
selectPromQlNameOption,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -500,9 +500,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@show="(e) => loadFilterItem(filteredItem.column)"
|
||||
:data-test="`dashboard-filter-item-${filteredItem.column}-menu`"
|
||||
>
|
||||
<div>
|
||||
<div class="q-pa-xs">
|
||||
<div class="q-gutter-xs">
|
||||
<div style="height: 100%">
|
||||
<div class="q-pa-xs" style="height: 100%">
|
||||
<div class="q-gutter-xs" style="height: 100%">
|
||||
<q-tabs
|
||||
v-model="
|
||||
dashboardPanelData.data.queries[
|
||||
@@ -535,6 +535,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
].fields.filter[index].type
|
||||
"
|
||||
animated
|
||||
style="height: 100%"
|
||||
>
|
||||
<q-tab-panel
|
||||
data-test="dashboard-filter-condition-panel"
|
||||
@@ -542,7 +543,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
name="condition"
|
||||
class="q-pa-none"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<div class="flex column" style="height: 220px;">
|
||||
<q-select
|
||||
dense
|
||||
filled
|
||||
@@ -557,9 +558,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
style="width: 100%"
|
||||
:rules="[(val) => !!val || 'Required']"
|
||||
/>
|
||||
<q-input
|
||||
dense
|
||||
filled
|
||||
<CommonAutoComplete
|
||||
v-if="
|
||||
!['Is Null', 'Is Not Null'].includes(
|
||||
dashboardPanelData.data.queries[
|
||||
@@ -567,16 +566,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
].fields?.filter[index]?.operator
|
||||
)
|
||||
"
|
||||
:label="t('common.value')"
|
||||
v-model="
|
||||
dashboardPanelData.data.queries[
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].fields.filter[index].value
|
||||
"
|
||||
data-test="dashboard-filter-condition-input"
|
||||
:label="t('common.value')"
|
||||
style="width: 100%; margin-top: 5px"
|
||||
:rules="[(val) => val?.length > 0 || 'Required']"
|
||||
/>
|
||||
:items="dashboardVariablesFilterItems"
|
||||
searchRegex="(?:^|[^$])\$?(\w+)"
|
||||
:rules="[(val: any) => val?.length > 0 || 'Required']"
|
||||
></CommonAutoComplete>
|
||||
</div>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel
|
||||
@@ -699,11 +698,13 @@ import useDashboardPanelData from "../../../composables/useDashboardPanel";
|
||||
import { getImageURL } from "../../../utils/zincutils";
|
||||
import SortByBtnGrp from "@/components/dashboards/addPanel/SortByBtnGrp.vue";
|
||||
import { useQuasar } from "quasar";
|
||||
import CommonAutoComplete from "@/components/dashboards/addPanel/CommonAutoComplete.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "DashboardMapQueryBuilder",
|
||||
components: { SortByBtnGrp },
|
||||
setup() {
|
||||
components: { SortByBtnGrp, CommonAutoComplete },
|
||||
props: ["dashboardData"],
|
||||
setup(props) {
|
||||
const { t } = useI18n();
|
||||
const $q = useQuasar();
|
||||
const expansionItems = reactive({
|
||||
@@ -712,7 +713,6 @@ export default defineComponent({
|
||||
weight: true,
|
||||
filter: false,
|
||||
});
|
||||
|
||||
const {
|
||||
dashboardPanelData,
|
||||
addLatitude,
|
||||
@@ -911,6 +911,13 @@ export default defineComponent({
|
||||
return commonBtnLabel(weightField);
|
||||
});
|
||||
|
||||
const dashboardVariablesFilterItems = computed(() =>
|
||||
(props.dashboardData?.variables?.list ?? []).map((it: any) => ({
|
||||
label: it.name,
|
||||
value: "'" + "$" + it.name + "'",
|
||||
}))
|
||||
);
|
||||
|
||||
return {
|
||||
t,
|
||||
dashboardPanelData,
|
||||
@@ -937,6 +944,7 @@ export default defineComponent({
|
||||
promqlMode,
|
||||
weightLabel,
|
||||
onFieldDragStart,
|
||||
dashboardVariablesFilterItems,
|
||||
options: [
|
||||
"=",
|
||||
"<>",
|
||||
|
||||
@@ -702,9 +702,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@show="(e) => loadFilterItem(filteredItem.column)"
|
||||
:data-test="`dashboard-filter-item-${filteredItem.column}-menu`"
|
||||
>
|
||||
<div>
|
||||
<div class="q-pa-xs">
|
||||
<div class="q-gutter-xs">
|
||||
<div style="height: 100%">
|
||||
<div class="q-pa-xs" style="height: 100%">
|
||||
<div class="q-gutter-xs" style="height: 100%">
|
||||
<q-tabs
|
||||
v-model="
|
||||
dashboardPanelData.data.queries[
|
||||
@@ -737,6 +737,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
].fields.filter[index].type
|
||||
"
|
||||
animated
|
||||
style="height: 100%"
|
||||
>
|
||||
<q-tab-panel
|
||||
data-test="dashboard-filter-condition-panel"
|
||||
@@ -744,7 +745,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
name="condition"
|
||||
class="q-pa-none"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<div class="flex column" style="height: 220px;">
|
||||
<q-select
|
||||
dense
|
||||
filled
|
||||
@@ -759,9 +760,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
style="width: 100%"
|
||||
:rules="[(val) => !!val || 'Required']"
|
||||
/>
|
||||
<q-input
|
||||
dense
|
||||
filled
|
||||
<CommonAutoComplete
|
||||
v-if="
|
||||
!['Is Null', 'Is Not Null'].includes(
|
||||
dashboardPanelData.data.queries[
|
||||
@@ -769,16 +768,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
].fields?.filter[index]?.operator
|
||||
)
|
||||
"
|
||||
:label="t('common.value')"
|
||||
v-model="
|
||||
dashboardPanelData.data.queries[
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].fields.filter[index].value
|
||||
"
|
||||
data-test="dashboard-filter-condition-input"
|
||||
:label="t('common.value')"
|
||||
style="width: 100%; margin-top: 5px"
|
||||
:rules="[(val) => val?.length > 0 || 'Required']"
|
||||
/>
|
||||
:items="dashboardVariablesFilterItems"
|
||||
searchRegex="(?:^|[^$])\$?(\w+)"
|
||||
:rules="[(val: any) => val?.length > 0 || 'Required']"
|
||||
></CommonAutoComplete>
|
||||
</div>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel
|
||||
@@ -892,8 +891,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<DashboardMapQueryBuilder />
|
||||
<DashboardSankeyChartBuilder />
|
||||
<DashboardMapQueryBuilder :dashboardData="dashboardData" />
|
||||
<DashboardSankeyChartBuilder :dashboardData="dashboardData" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -906,6 +905,7 @@ import DashboardSankeyChartBuilder from "./DashboardSankeyChartBuilder.vue";
|
||||
import SortByBtnGrp from "@/components/dashboards/addPanel/SortByBtnGrp.vue";
|
||||
import HistogramIntervalDropDown from "@/components/dashboards/addPanel/HistogramIntervalDropDown.vue";
|
||||
import { useQuasar } from "quasar";
|
||||
import CommonAutoComplete from "@/components/dashboards/addPanel/CommonAutoComplete.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "DashboardQueryBuilder",
|
||||
@@ -913,9 +913,11 @@ export default defineComponent({
|
||||
DashboardMapQueryBuilder,
|
||||
SortByBtnGrp,
|
||||
HistogramIntervalDropDown,
|
||||
DashboardSankeyChartBuilder
|
||||
DashboardSankeyChartBuilder,
|
||||
CommonAutoComplete,
|
||||
},
|
||||
setup() {
|
||||
props: ["dashboardData"],
|
||||
setup(props) {
|
||||
const showXAxis = ref(true);
|
||||
const panelName = ref("");
|
||||
const panelDesc = ref("");
|
||||
@@ -928,7 +930,6 @@ export default defineComponent({
|
||||
config: true,
|
||||
filter: false,
|
||||
});
|
||||
|
||||
const {
|
||||
dashboardPanelData,
|
||||
addXAxisItem,
|
||||
@@ -1312,6 +1313,13 @@ export default defineComponent({
|
||||
return zFields.map(commonBtnLabel);
|
||||
});
|
||||
|
||||
const dashboardVariablesFilterItems = computed(() =>
|
||||
(props.dashboardData?.variables?.list ?? []).map((it: any) => ({
|
||||
label: it.name,
|
||||
value: "'" + "$" + it.name + "'",
|
||||
}))
|
||||
);
|
||||
|
||||
return {
|
||||
showXAxis,
|
||||
t,
|
||||
@@ -1358,6 +1366,7 @@ export default defineComponent({
|
||||
onFieldDragStart,
|
||||
getHistoramIntervalField,
|
||||
onDragEnd,
|
||||
dashboardVariablesFilterItems,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -500,9 +500,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@show="(e) => loadFilterItem(filteredItem.column)"
|
||||
:data-test="`dashboard-filter-item-${filteredItem.column}-menu`"
|
||||
>
|
||||
<div>
|
||||
<div class="q-pa-xs">
|
||||
<div class="q-gutter-xs">
|
||||
<div style="height: 100%">
|
||||
<div class="q-pa-xs" style="height: 100%">
|
||||
<div class="q-gutter-xs" style="height: 100%">
|
||||
<q-tabs
|
||||
v-model="
|
||||
dashboardPanelData.data.queries[
|
||||
@@ -535,6 +535,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
].fields.filter[index].type
|
||||
"
|
||||
animated
|
||||
style="height: 100%"
|
||||
>
|
||||
<q-tab-panel
|
||||
data-test="dashboard-filter-condition-panel"
|
||||
@@ -542,7 +543,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
name="condition"
|
||||
class="q-pa-none"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<div class="flex column" style="height: 220px">
|
||||
<q-select
|
||||
dense
|
||||
filled
|
||||
@@ -557,9 +558,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
style="width: 100%"
|
||||
:rules="[(val) => !!val || 'Required']"
|
||||
/>
|
||||
<q-input
|
||||
dense
|
||||
filled
|
||||
<CommonAutoComplete
|
||||
v-if="
|
||||
!['Is Null', 'Is Not Null'].includes(
|
||||
dashboardPanelData.data.queries[
|
||||
@@ -567,16 +566,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
].fields?.filter[index]?.operator
|
||||
)
|
||||
"
|
||||
:label="t('common.value')"
|
||||
v-model="
|
||||
dashboardPanelData.data.queries[
|
||||
dashboardPanelData.layout.currentQueryIndex
|
||||
].fields.filter[index].value
|
||||
"
|
||||
data-test="dashboard-filter-condition-input"
|
||||
:label="t('common.value')"
|
||||
style="width: 100%; margin-top: 5px"
|
||||
:rules="[(val) => val?.length > 0 || 'Required']"
|
||||
/>
|
||||
:items="dashboardVariablesFilterItems"
|
||||
searchRegex="(?:^|[^$])\$?(\w+)"
|
||||
:rules="[(val: any) => val?.length > 0 || 'Required']"
|
||||
></CommonAutoComplete>
|
||||
</div>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel
|
||||
@@ -699,11 +698,13 @@ import useDashboardPanelData from "../../../composables/useDashboardPanel";
|
||||
import { getImageURL } from "../../../utils/zincutils";
|
||||
import SortByBtnGrp from "@/components/dashboards/addPanel/SortByBtnGrp.vue";
|
||||
import { useQuasar } from "quasar";
|
||||
import CommonAutoComplete from "@/components/dashboards/addPanel/CommonAutoComplete.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "DashboardSankeyChartBuilder",
|
||||
components: { SortByBtnGrp },
|
||||
setup() {
|
||||
components: { SortByBtnGrp, CommonAutoComplete },
|
||||
props: ["dashboardData"],
|
||||
setup(props) {
|
||||
const { t } = useI18n();
|
||||
const $q = useQuasar();
|
||||
const expansionItems = reactive({
|
||||
@@ -712,7 +713,6 @@ export default defineComponent({
|
||||
value: true,
|
||||
filter: false,
|
||||
});
|
||||
|
||||
const {
|
||||
dashboardPanelData,
|
||||
addSource,
|
||||
@@ -902,6 +902,13 @@ export default defineComponent({
|
||||
return commonBtnLabel(valueField);
|
||||
});
|
||||
|
||||
const dashboardVariablesFilterItems = computed(() =>
|
||||
(props.dashboardData?.variables?.list ?? []).map((it: any) => ({
|
||||
label: it.name,
|
||||
value: "'" + "$" + it.name + "'",
|
||||
}))
|
||||
);
|
||||
|
||||
return {
|
||||
t,
|
||||
dashboardPanelData,
|
||||
@@ -927,6 +934,7 @@ export default defineComponent({
|
||||
promqlMode,
|
||||
valueLabel,
|
||||
onFieldDragStart,
|
||||
dashboardVariablesFilterItems,
|
||||
options: [
|
||||
"=",
|
||||
"<>",
|
||||
|
||||
@@ -213,25 +213,28 @@
|
||||
v-for="(variable, index) in drilldownData.data.variables"
|
||||
:key="index"
|
||||
>
|
||||
<div style="display: flex; gap: 10px; margin-bottom: 10px">
|
||||
<q-input
|
||||
v-model="variable.name"
|
||||
<div
|
||||
style="display: flex; gap: 10px; margin-bottom: 10px"
|
||||
:key="variableNamesFn?.toString()"
|
||||
>
|
||||
<CommonAutoComplete
|
||||
placeholder="Name"
|
||||
stack-label
|
||||
outlined
|
||||
filled
|
||||
dense
|
||||
:data-test="`dashboard-drilldown-variable-name-${index}`"
|
||||
/>
|
||||
<q-input
|
||||
v-model="variable.value"
|
||||
v-model="variable.name"
|
||||
searchRegex="(.*)"
|
||||
:items="variableNamesFn"
|
||||
style="width: auto !important; padding-top: 3px !important"
|
||||
></CommonAutoComplete>
|
||||
<CommonAutoComplete
|
||||
placeholder="Value"
|
||||
stack-label
|
||||
outlined
|
||||
filled
|
||||
dense
|
||||
:data-test="`dashboard-drilldown-variable-value-${index}`"
|
||||
/>
|
||||
searchRegex="(.*)"
|
||||
v-model="variable.value"
|
||||
:items="options.selectedValue"
|
||||
style="
|
||||
width: auto !important;
|
||||
padding-top: 3px !important;
|
||||
"
|
||||
></CommonAutoComplete>
|
||||
|
||||
<q-icon
|
||||
class="q-mr-xs"
|
||||
size="20px"
|
||||
@@ -291,7 +294,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { reactive, ref, toRef } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
@@ -305,14 +308,16 @@ import {
|
||||
getAllDashboardsByFolderId,
|
||||
getFoldersList,
|
||||
} from "../../../utils/commons";
|
||||
import { onMounted } from "vue";
|
||||
import { onMounted, onUnmounted } from "vue";
|
||||
import useDashboardPanelData from "../../../composables/useDashboardPanel";
|
||||
import DrilldownUserGuide from "@/components/dashboards/addPanel/DrilldownUserGuide.vue";
|
||||
import CommonAutoComplete from "@/components/dashboards/addPanel/CommonAutoComplete.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "DrilldownPopUp",
|
||||
components: {
|
||||
DrilldownUserGuide,
|
||||
CommonAutoComplete,
|
||||
},
|
||||
props: {
|
||||
isEditMode: {
|
||||
@@ -329,6 +334,7 @@ export default defineComponent({
|
||||
const { t } = useI18n();
|
||||
const store = useStore();
|
||||
const { dashboardPanelData } = useDashboardPanelData();
|
||||
|
||||
const getDefaultDrilldownData = () => ({
|
||||
name: "",
|
||||
type: "byDashboard",
|
||||
@@ -542,6 +548,80 @@ export default defineComponent({
|
||||
drilldownData.value.type = type;
|
||||
};
|
||||
|
||||
const options: any = reactive({
|
||||
selectedValue: [],
|
||||
selectedName: [],
|
||||
});
|
||||
|
||||
//want label for dropdown in input and value for its input value
|
||||
const selectedValue = computed(() => {
|
||||
let selectedValues: any = [];
|
||||
if (dashboardPanelData.data.type === "sankey") {
|
||||
selectedValues = [
|
||||
{ label: "Edge Source", value: "${edge.__source}" },
|
||||
{ label: "Edge Target", value: "${edge.__target}" },
|
||||
{ label: "Edge Value", value: "${edge.__value}" },
|
||||
{ label: "Node Name", value: "${node.__name}" },
|
||||
{ label: "Node Value", value: "${node.__value}" },
|
||||
];
|
||||
} else if (dashboardPanelData.data.type === "table") {
|
||||
dashboardPanelData.data.queries.forEach((query: any) => {
|
||||
const panelFields = [
|
||||
...query.fields.x,
|
||||
...query.fields.y,
|
||||
...query.fields.z,
|
||||
];
|
||||
panelFields.forEach((field) => {
|
||||
selectedValues.push({
|
||||
label: field.label,
|
||||
value: '${row.field["' + field.label + '"]}',
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
selectedValues = [
|
||||
{ label: "Series Name", value: "${series.__name}" },
|
||||
{ label: "Series Value", value: "${series.__value}" },
|
||||
];
|
||||
}
|
||||
return selectedValues;
|
||||
});
|
||||
|
||||
// Assign selectedValue to options object
|
||||
options.selectedValue = selectedValue;
|
||||
|
||||
const variableNamesFn = ref([]);
|
||||
|
||||
watch(drilldownData.value, async (newData) => {
|
||||
if (newData.data.folder && newData.data.dashboard) {
|
||||
const folder = store.state.organizationData.folders.find(
|
||||
(folder: any) => folder.name === newData.data.folder
|
||||
);
|
||||
|
||||
const allDashboardData = await getAllDashboardsByFolderId(
|
||||
store,
|
||||
folder.folderId
|
||||
);
|
||||
const dashboardData = allDashboardData.find(
|
||||
(dashboard: any) => dashboard.title === newData.data.dashboard
|
||||
);
|
||||
|
||||
if (dashboardData) {
|
||||
const optionsList = dashboardData.variables.list.map(
|
||||
(variable: any) => ({
|
||||
label: variable.name,
|
||||
value: variable.name,
|
||||
})
|
||||
);
|
||||
variableNamesFn.value = optionsList;
|
||||
} else {
|
||||
variableNamesFn.value = [];
|
||||
}
|
||||
} else {
|
||||
variableNamesFn.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
t,
|
||||
outlinedDashboard,
|
||||
@@ -556,6 +636,8 @@ export default defineComponent({
|
||||
saveDrilldown,
|
||||
isFormURLValid,
|
||||
changeTypeOfDrilldown,
|
||||
options,
|
||||
variableNamesFn,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -42,10 +42,10 @@
|
||||
<p>You can reference a variable with the following format:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="bg-highlight">${var-variable_name}</span>
|
||||
<span class="bg-highlight">${variable_name}</span>
|
||||
<br />
|
||||
(For Example, if your variable name is "test", you can use
|
||||
<span class="bg-highlight">${var-test}</span>)
|
||||
<span class="bg-highlight">${test}</span>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -224,17 +224,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:rules="[(val: any) => !!(val.trim()) || 'Field is required!']"
|
||||
:options="['=', '!=']"
|
||||
/>
|
||||
<q-input
|
||||
<CommonAutoComplete
|
||||
v-model="filter.value"
|
||||
placeholder="Enter Value"
|
||||
dense
|
||||
filled
|
||||
:items="dashboardVariablesFilterItems"
|
||||
searchRegex="(?:^|[^$])\$?(\w+)"
|
||||
:rules="[(val: any) => val?.length > 0 || 'Required']"
|
||||
debounce="1000"
|
||||
style="width: 125px"
|
||||
class=""
|
||||
data-test="dashboard-query-values-filter-value"
|
||||
:rules="[(val: any) => !!(val.trim()) || 'Field is required!']"
|
||||
/>
|
||||
style="
|
||||
margin-top: none !important;
|
||||
width: auto !important;
|
||||
"
|
||||
placeholder="Enter Value"
|
||||
></CommonAutoComplete>
|
||||
<q-btn
|
||||
size="sm"
|
||||
padding="12px 5px"
|
||||
@@ -378,6 +379,7 @@ import {
|
||||
toRef,
|
||||
toRaw,
|
||||
type Ref,
|
||||
computed,
|
||||
} from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useSelectAutoComplete } from "../../../composables/useSelectAutocomplete";
|
||||
@@ -396,11 +398,12 @@ import {
|
||||
buildVariablesDependencyGraph,
|
||||
isGraphHasCycle,
|
||||
} from "@/utils/dashboard/variables/variablesDependencyUtils";
|
||||
import CommonAutoComplete from "@/components/dashboards/addPanel/CommonAutoComplete.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AddSettingVariable",
|
||||
props: ["variableName"],
|
||||
components: { DashboardHeader },
|
||||
props: ["variableName", "dashboardVariablesList"],
|
||||
components: { DashboardHeader, CommonAutoComplete },
|
||||
emits: ["close", "save"],
|
||||
setup(props, { emit }) {
|
||||
const $q = useQuasar();
|
||||
@@ -772,6 +775,14 @@ export default defineComponent({
|
||||
emit("close");
|
||||
};
|
||||
|
||||
const dashboardVariablesFilterItems = computed(() =>
|
||||
props.dashboardVariablesList
|
||||
.map((it: any) => ({
|
||||
label: it.name,
|
||||
value: "$" + it.name,
|
||||
}))
|
||||
.filter((it: any) => it.label !== variableData.name)
|
||||
);
|
||||
return {
|
||||
variableData,
|
||||
store,
|
||||
@@ -797,6 +808,7 @@ export default defineComponent({
|
||||
removeFilter,
|
||||
filterUpdated,
|
||||
filterCycleError,
|
||||
dashboardVariablesFilterItems,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@save="handleSaveVariable"
|
||||
:variableName="selectedVariable"
|
||||
@close="goBackToDashboardList"
|
||||
:dashboardVariablesList="dashboardVariablesList"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="column full-height">
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { ref, watch, type Ref } from "vue";
|
||||
|
||||
export const useSearchInputUsingRegex = (
|
||||
options: Ref<any>,
|
||||
searchKey: string,
|
||||
searchRegex: string
|
||||
) => {
|
||||
const filteredOptions = ref(options.value);
|
||||
|
||||
watch(options, (newValue) => {
|
||||
filteredOptions.value = newValue;
|
||||
});
|
||||
|
||||
const filterFn = (val: any) => {
|
||||
if (val === "") {
|
||||
filteredOptions.value = options.value;
|
||||
return;
|
||||
}
|
||||
|
||||
const regex = new RegExp(searchRegex, "gi");
|
||||
let match = regex.exec(val)
|
||||
|
||||
if(!match) {
|
||||
filteredOptions.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
let needle: any = null;
|
||||
for (let i = 1; i < match.length; i++) {
|
||||
if (match[i] !== undefined) {
|
||||
needle = match[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
filteredOptions.value = options.value?.filter((option: any) => {
|
||||
const value =
|
||||
typeof option === "object" ? option[searchKey] : option.toString();
|
||||
const lowerCaseValue = value.toLowerCase();
|
||||
return lowerCaseValue.includes(needle);
|
||||
});
|
||||
};
|
||||
|
||||
return { filteredOptions, filterFn };
|
||||
};
|
||||
@@ -446,9 +446,9 @@ export default defineComponent({
|
||||
|
||||
// NOTE: after variables in variables feature, it works without changing the initial variable values
|
||||
// then, update the initial variable values
|
||||
// await variablesValueSelectorRef.value.changeInitialVariableValues(
|
||||
// ...args
|
||||
// );
|
||||
await variablesValueSelectorRef.value.changeInitialVariableValues(
|
||||
...args
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -173,7 +173,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
class="layout-panel-container col"
|
||||
style="height: 100%"
|
||||
>
|
||||
<DashboardQueryBuilder />
|
||||
<DashboardQueryBuilder :dashboardData="currentDashboardData.data"/>
|
||||
<q-separator />
|
||||
<VariablesValueSelector
|
||||
:variablesConfig="currentDashboardData.data?.variables"
|
||||
@@ -252,7 +252,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:title="t('dashboard.configLabel')"
|
||||
v-model="dashboardPanelData.layout.isConfigPanelOpen"
|
||||
>
|
||||
<ConfigPanel />
|
||||
<ConfigPanel :dashboardPanelData="dashboardPanelData" />
|
||||
</PanelSidebar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user