Skip to content

Commit

Permalink
do not perform query when invalid filters are present
Browse files Browse the repository at this point in the history
Modified the `Filtering.getValidFilters` function to return an `invalidFilters` object as well, which can be used to tell the user which filters were invalid.
  • Loading branch information
ahmedhamidawan committed Mar 12, 2024
1 parent 858289e commit 5b51c80
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 254 deletions.
22 changes: 1 addition & 21 deletions client/src/components/Common/FilterMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { faAngleDoubleUp, faQuestion, faRedo, faSearch } from "@fortawesome/free
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton, BModal } from "bootstrap-vue";
import { kebabCase } from "lodash";
import { computed, ref, watch } from "vue";
import { computed, ref } from "vue";
import type Filtering from "@/utils/filtering";
import { type Alias, getOperatorForAlias } from "@/utils/filtering";
Expand Down Expand Up @@ -68,7 +68,6 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits<{
(e: "update:filter-text", filter: string): void;
(e: "on-backend-filter", filter: string): void;
(e: "update:show-advanced", showAdvanced: boolean): void;
(e: "on-search", filters: Record<string, string | boolean>, filterText?: string, backendFilter?: string): void;
}>();
Expand Down Expand Up @@ -127,25 +126,6 @@ function onToggle() {
function updateFilterText(newFilterText: string) {
emit("update:filter-text", newFilterText);
}
// as the filterText changes, emit a backend-filter that can be used as a backend query
watch(
() => props.filterText,
(newFilterText: string) => {
const defaultBackendFilter = props.filterClass.getFilterText(props.filterClass.defaultFilters, true);
const currentBackendFilter = props.filterClass.getFilterText(filters.value, true);
const backendFilter =
defaultBackendFilter === currentBackendFilter
? `${
defaultBackendFilter && !newFilterText.includes(defaultBackendFilter)
? defaultBackendFilter + " "
: ""
}` + newFilterText
: props.filterClass.getFilterText(filters.value, true);
emit("on-backend-filter", backendFilter);
}
);
</script>

<template>
Expand Down
72 changes: 48 additions & 24 deletions client/src/components/Grid/GridList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,11 @@ const sortDesc = ref(props.gridConfig ? props.gridConfig.sortDesc : false);
// filtering refs and handlers
const filterText = ref("");
const searchTerm = ref("");
const showAdvanced = ref(false);
const filterClass = props.gridConfig.filtering;
const rawFilters = computed(() => Object.fromEntries(filterClass.getFiltersForText(filterText.value, true, false)));
const validFilters = computed(() => filterClass.getValidFilters(rawFilters.value, true).validFilters);
const invalidFilters = computed(() => filterClass.getValidFilters(rawFilters.value, true).invalidFilters);
// hide message helper
const hideMessage = useDebounceFn(() => {
Expand Down Expand Up @@ -111,12 +114,17 @@ function displayInitialMessage() {
async function getGridData() {
selected.value = new Set();
if (props.gridConfig) {
if (Object.keys(invalidFilters.value).length > 0) {
// there are invalid filters, so we don't want to search
loading.value = false;
return;
}
try {
const offset = props.limit * (currentPage.value - 1);
const [responseData, responseTotal] = await props.gridConfig.getData(
offset,
props.limit,
searchTerm.value,
validatedFilterText(),
sortBy.value,
sortDesc.value
);
Expand Down Expand Up @@ -159,13 +167,6 @@ function onRouterPush(route: string) {
router.push(route);
}
/**
* Apply backend formatted filter and execute grid search
*/
function onSearch(query: string) {
searchTerm.value = query;
}
/**
* User changes sorting
*/
Expand Down Expand Up @@ -211,14 +212,24 @@ function onSelectAll(current: boolean): void {
}
}
/**
* A valid filter/query for the backend
*/
function validatedFilterText() {
// there are no filters derived from the `filterText`; return the `filterText` as is
if (Object.keys(rawFilters.value).length === 0) {
return filterText.value;
}
// there are valid filters derived from the `filterText`
return filterClass.getFilterText(validFilters.value, false);
}
/**
* Initialize grid data
*/
onMounted(() => {
if (props.usernameSearch) {
const search_query = `user:${props.usernameSearch}`.trim();
filterText.value = search_query;
onSearch(search_query);
filterText.value = filterClass.setFilterValue(filterText.value, "user", props.usernameSearch);
}
getGridData();
eventBus.on(onRouterPush);
Expand All @@ -232,7 +243,7 @@ onUnmounted(() => {
/**
* Load current page
*/
watch([currentPage, searchTerm, sortDesc, sortBy], () => getGridData());
watch([currentPage, filterText, sortDesc, sortBy], () => getGridData());
/**
* Operation message timeout handler
Expand Down Expand Up @@ -269,21 +280,34 @@ watch(operationMessage, () => {
:class="{ 'py-2': !embedded }"
:name="gridConfig.plural"
:placeholder="`search ${gridConfig.plural.toLowerCase()}`"
:filter-class="gridConfig.filtering"
:filter-class="filterClass"
:filter-text.sync="filterText"
:loading="loading"
:show-advanced.sync="showAdvanced"
@on-backend-filter="onSearch" />
:show-advanced.sync="showAdvanced" />
<hr v-if="showAdvanced" />
</div>
<LoadingSpan v-if="loading" />
<BAlert v-else-if="!isAvailable" variant="info" show>
<span v-if="searchTerm">
<span v-localize>Nothing found with:</span>
<b>{{ searchTerm }}</b>
</span>
<span v-else v-localize> No entries found. </span>
</BAlert>
<span v-else-if="!isAvailable || Object.keys(invalidFilters).length > 0" variant="info" show>
<BAlert v-if="Object.keys(invalidFilters).length === 0" variant="info" show>
<span v-if="filterText">
<span v-localize>Nothing found with:</span>
<b>{{ filterText }}</b>
</span>
<span v-else v-localize> No entries found. </span>
</BAlert>
<BAlert v-else variant="danger" show>
<Heading h4 inline size="sm" class="flex-grow-1 mb-2">Invalid filters in query:</Heading>
<ul>
<li v-for="[invalidKey, value] in Object.entries(invalidFilters)" :key="invalidKey">
<b>{{ invalidKey }}</b
>: {{ value }}
</li>
</ul>
<a href="javascript:void(0)" class="ui-link" @click="filterText = validatedFilterText()">
Remove invalid filters from query
</a>
</BAlert>
</span>
<table v-else class="grid-table">
<thead>
<th v-if="!!gridConfig.batch">
Expand Down Expand Up @@ -354,7 +378,7 @@ watch(operationMessage, () => {
:value="rowData[fieldEntry.key]"
:disabled="fieldEntry.disabled"
@input="onTagInput(rowData, $event, fieldEntry.handler)"
@tag-click="applyFilter('tag', $event, true)" />
@tag-click="applyFilter('tag', $event)" />
<span v-else v-localize> Not available. </span>
</div>
<FontAwesomeIcon v-else icon="fa-shield-alt" />
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/History/CurrentHistory/HistoryPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,9 @@ async function onDrop(evt: any) {
}
}
function updateFilterValue(newFilterText: string, newValue: any) {
function updateFilterValue(filterKey: string, newValue: any) {
const currentFilterText = filterText.value;
filterText.value = filterClass.setFilterValue(currentFilterText, newFilterText, newValue);
filterText.value = filterClass.setFilterValue(currentFilterText, filterKey, newValue);
}
function getItemKey(item: HistoryItem) {
Expand Down
Loading

0 comments on commit 5b51c80

Please sign in to comment.