Skip to content

Commit

Permalink
Merge pull request #506 from noi-techpark/fix-table-filter
Browse files Browse the repository at this point in the history
Fix table filter
  • Loading branch information
RudiThoeni authored Jan 12, 2024
2 parents c3bc33f + aec66e8 commit 4aca089
Show file tree
Hide file tree
Showing 19 changed files with 442 additions and 481 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

import { acceptHMRUpdate, defineStore, storeToRefs } from 'pinia';
import { Ref, computed } from 'vue';
import { MaybeRef, Ref, computed, toValue } from 'vue';
import { useRouter } from 'vue-router';
import { useDatasetBaseInfoStore } from '../../config/store/datasetBaseInfoStore';

Expand Down Expand Up @@ -36,18 +36,22 @@ export const useDatasetQueryStore = defineStore('datasetRoutingStore', () => {
>((prev, name) => ({ ...prev, [name]: buildQueryHandler(name) }), {});
});

const handle = (name: string) =>
const handle = (name: MaybeRef<string>) =>
computed({
get: () => queryHandlers.value[name]?.value,
get: () => queryHandlers.value[toValue(name)]?.value,
set: (value: string | undefined) => {
const nameValue = toValue(name);
// Handle the case where the query handler is not defined, e.g. because
// the query parameter is not defined in the dataset config. Examples
// for this are the query parameters "searchfilter" or "rawfilter"
if (queryHandlers.value[name] == null) {
const query = { ...router.currentRoute.value.query, [name]: value };
if (queryHandlers.value[nameValue] == null) {
const query = {
...router.currentRoute.value.query,
[nameValue]: value,
};
router.push({ query });
} else {
queryHandlers.value[name].value = value;
queryHandlers.value[nameValue].value = value;
}
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,27 +79,26 @@ import IconSortDesc from '../../../../components/svg/IconSortDesc.vue';
import IconStrokedArrowDown from '../../../../components/svg/IconStrokedArrowDown.vue';
import { PropertyPath } from '../../../datasets/config/types';
import FilterPopoverContent from './filter/FilterPopoverContent.vue';
import { useTableFilterForPropertyPath } from './filter/useTableFilter';
import SortPopoverContent from './sort/SortPopoverContent.vue';
import { useTableSortForPropertyPath } from './sort/useTableSort';
import TooltipCustom from '../../../../components/tooltip/TooltipCustom.vue';
import { useTableFilterStore } from './filter/tableFilterStore';

const props = withDefaults(
defineProps<{
title: string;
propertyPath?: PropertyPath;
isDeprecated?: boolean;
}>(),
{ propertyPath: undefined }
{ propertyPath: undefined, isDeprecated: false }
);

const { title, propertyPath } = toRefs(props);

const { canSort, isCurrentSortAsc, isCurrentSortDesc } =
useTableSortForPropertyPath(propertyPath);

const { canFilter, isFilterActive } = useTableFilterForPropertyPath(
title,
propertyPath
);
const canFilter = useTableFilterStore().canFilter(propertyPath);

const isFilterActive = useTableFilterStore().isFilterActive(propertyPath);
</script>
2 changes: 1 addition & 1 deletion databrowser/src/domain/datasets/ui/tableView/TableView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
/>
<TableFooter :pagination="pagination" />
</div>
<TableToolBox :url="fullPath" :cols="cols" />
<TableToolBox :url="fullPath" />
</div>
</section>
<EditListDeleteDialog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
class="flex min-w-[20em] items-center gap-2 p-2"
:size="Size.xs"
:variant="Variant.ghost"
@click="removeFilter"
@click="removeFilterByPropertyPath(propertyPath)"
>
<IconDelete class="text-delete" /> Reset filter
</ButtonCustom>
Expand All @@ -36,7 +36,7 @@ import { Size, Variant } from '../../../../../components/button/types';
import PopoverContent from '../../../../../components/popover/PopoverContent.vue';
import PopoverContentDivider from '../../../../../components/popover/PopoverContentDivider.vue';
import IconDelete from '../../../../../components/svg/IconDelete.vue';
import { useTableFilterForPropertyPath } from './useTableFilter';
import { useTableFilterStore } from './tableFilterStore';

const emit = defineEmits(['addFilter', 'removeFilter']);

Expand All @@ -50,11 +50,12 @@ const props = withDefaults(

const { title, propertyPath } = toRefs(props);

const { isFilterActive, addFilter, removeFilter } =
useTableFilterForPropertyPath(title, propertyPath);
const { addFilter, removeFilterByPropertyPath } = useTableFilterStore();

const isFilterActive = useTableFilterStore().isFilterActive(propertyPath);

const addFilterInternal = () => {
addFilter();
addFilter(title.value, propertyPath.value);
emit('addFilter');
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ SPDX-License-Identifier: AGPL-3.0-or-later

<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { useTableFilter } from './useTableFilter';
import ResetAllFilters from './ResetAllFilters.vue';
import { useTableFilterStore } from './tableFilterStore';
import { storeToRefs } from 'pinia';

const { t } = useI18n();

const { areFiltersActive, removeAllFilters } = useTableFilter();
const { areFiltersActive } = storeToRefs(useTableFilterStore());

const { removeAllFilters } = useTableFilterStore();
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: NOI Techpark <[email protected]>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

import { DatasetDomain } from '../../../../config/types';
import { BaseFilter } from '../types';
import { buildMobilityFilterValues } from './mobilityFilterBuilder';
import { buildTourismFilterValues } from './tourismFilterBuilder';

export const buildFilterValues = (
datasetDomain: DatasetDomain | undefined,
updatedFilters: BaseFilter[]
): string[] => {
if (datasetDomain === 'tourism') {
return buildTourismFilterValues(updatedFilters);
}
if (datasetDomain === 'mobility') {
return buildMobilityFilterValues(updatedFilters);
}
console.debug(
`Can not build filter values for unknown dataset domain "${datasetDomain}", returning empty list`
);
return [];
};

export const buildFilterValuesString = (
datasetDomain: DatasetDomain | undefined,
updatedFilters: BaseFilter[]
): string | undefined => {
const filterValues = buildFilterValues(datasetDomain, updatedFilters);
return filterValues.length > 0 ? `and(${filterValues.join(',')})` : undefined;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: NOI Techpark <[email protected]>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

import { BaseFilter } from '../types';

export const buildMobilityFilterValues = (updatedFilters: BaseFilter[]) =>
updatedFilters.reduce<string[]>((prev, curr) => {
if (curr.value === '' || curr.value == null) {
return prev;
}

const value = toMobilityFilterValue(curr);

if (value === '' || value == null) {
return prev;
}

switch (curr.operator) {
case 'eq':
return [...prev, `${curr.propertyPath}.eq.${value}`];
case 'neq':
return [...prev, `${curr.propertyPath}.neq.${value}`];
case 'gt':
return [...prev, `${curr.propertyPath}.gt.${value}`];
case 'gteq':
return [...prev, `${curr.propertyPath}.gteq.${value}`];
case 'lt':
return [...prev, `${curr.propertyPath}.lt.${value}`];
case 'lteq':
return [...prev, `${curr.propertyPath}.lteq.${value}`];
case 'in':
return [...prev, `${curr.propertyPath}.in.(${value})`];
case 'nin':
return [...prev, `${curr.propertyPath}.nin.(${value})`];
case 're':
return [...prev, `${curr.propertyPath}.re.${value}`];
case 'ire':
return [...prev, `${curr.propertyPath}.ire.${value}`];
case 'nre':
return [...prev, `${curr.propertyPath}.nre.${value}`];
case 'nire':
return [...prev, `${curr.propertyPath}.nire.${value}`];
case 'bbi':
return [...prev, `${curr.propertyPath}.bbi.(${value})`];
case 'bbc':
return [...prev, `${curr.propertyPath}.bbc.(${value})`];
default:
return prev;
}
}, []);

// We need to surround the filter value with double quotes only if it is a string.
// If its a string, we need to further check if it can be converted to a number
// or boolean. If so, no double quotes must be added.
const toMobilityFilterValue = ({ operator, value }: BaseFilter) => {
if (typeof value !== 'string') {
return value;
}

const number = Number(value);
if (!isNaN(number)) {
return number;
}

const lowerCasedValue = value.toLowerCase();
if (lowerCasedValue === 'true' || lowerCasedValue === 'false') {
return value;
}

if (operator === 'bbi' || operator === 'bbc') {
return `${value}`;
}

return `"${value}"`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: NOI Techpark <[email protected]>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

import { BaseFilter } from '../types';

export const buildTourismFilterValues = (updatedFilters: BaseFilter[]) =>
updatedFilters.reduce<string[]>((prev, curr) => {
if (
curr.operator !== 'isnull' &&
curr.operator !== 'isnotnull' &&
(curr.value === '' || curr.value == null)
) {
return prev;
}

switch (curr.operator) {
case 'eq':
return [...prev, `eq(${curr.propertyPath},'${curr.value}')`];
case 'ne':
return [...prev, `ne(${curr.propertyPath},'${curr.value}')`];
case 'gt':
return [...prev, `gt(${curr.propertyPath},'${curr.value}')`];
case 'ge':
return [...prev, `ge(${curr.propertyPath},'${curr.value}')`];
case 'lt':
return [...prev, `lt(${curr.propertyPath},'${curr.value}')`];
case 'le':
return [...prev, `le(${curr.propertyPath},'${curr.value}')`];
case 'isnull':
return [...prev, `isnull(${curr.propertyPath})`];
case 'isnotnull':
return [...prev, `isnotnull(${curr.propertyPath})`];
case 'in':
return [...prev, `in(${curr.propertyPath}.[*],'${curr.value}')`];
case 'nin':
return [...prev, `nin(${curr.propertyPath}.[*],'${curr.value}')`];
case 'like':
return [...prev, `like(${curr.propertyPath},'${curr.value}')`];
case 'likein':
return [...prev, `likein(${curr.propertyPath}.[*],'${curr.value}')`];
default:
return prev;
}
}, []);
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: NOI Techpark <[email protected]>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

import { acceptHMRUpdate, defineStore, storeToRefs } from 'pinia';
import { computed } from 'vue';
import { useDatasetBaseInfoStore } from '../../../config/store/datasetBaseInfoStore';
import { useDatasetQueryStore } from '../../../location/store/datasetQueryStore';
import { buildFilterValuesString } from './builder/filterBuilder';
import { parseFilterWithRegex } from './parser/filterParser';
import { BaseFilter } from './types';

export const useDatasetFilterStore = defineStore('datasetFilterStore', () => {
const { datasetDomain } = storeToRefs(useDatasetBaseInfoStore());

const urlFilterParamName = computed(() => {
if (datasetDomain.value === 'tourism') {
return 'rawfilter';
}
if (datasetDomain.value === 'mobility') {
return 'where';
}
return '';
});

const urlFilter = useDatasetQueryStore().handle(urlFilterParamName);

const datasetFilters = computed<BaseFilter[]>({
get: () => parseFilterWithRegex(datasetDomain.value, urlFilter.value),
set: (updatedFilters) =>
(urlFilter.value = buildFilterValuesString(
datasetDomain.value,
updatedFilters
)),
});

return { datasetFilters };
});

// Add support for hot-module-reload
if (import.meta.hot) {
import.meta.hot.accept(
acceptHMRUpdate(useDatasetFilterStore, import.meta.hot)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { MobilityFilterOption, TourismFilterOption } from './types';

export const tourismFilterSelectOptions: TourismFilterOption[] = [
export const tourismFilterTypeSelectOptions: TourismFilterOption[] = [
{
label: 'equal to',
value: 'eq',
Expand Down Expand Up @@ -55,7 +55,7 @@ export const tourismFilterSelectOptions: TourismFilterOption[] = [
},
];

export const mobilityFilterSelectOptions: MobilityFilterOption[] = [
export const mobilityFilterTypeSelectOptions: MobilityFilterOption[] = [
{
label: 'equal to',
value: 'eq',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: NOI Techpark <[email protected]>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

import { DatasetDomain } from '../../../../config/types';
import { BaseFilter } from '../types';
import { mobilityParseFilterWithRegex } from './mobilityFilterParserWithRegex';
import { tourismParseFilterWithRegex } from './tourismFilterParserWithRegex';

export const parseFilterWithRegex = (
datasetDomain: DatasetDomain | undefined,
filterString?: string
): BaseFilter[] => {
if (datasetDomain === 'tourism') {
return tourismParseFilterWithRegex(filterString);
}
if (datasetDomain === 'mobility') {
return mobilityParseFilterWithRegex(filterString);
}
console.debug(
`Can not parse filters for unknown dataset domain "${datasetDomain}", returning empty list`
);
return [];
};
Loading

0 comments on commit 4aca089

Please sign in to comment.