{translation('themeMode', { theme: t })}
diff --git a/web/utils/propertyColumn.tsx b/web/utils/propertyColumn.tsx
index 5310125..9edd7f9 100644
--- a/web/utils/propertyColumn.tsx
+++ b/web/utils/propertyColumn.tsx
@@ -49,7 +49,8 @@ function getFilterData(prop: PropertyDefinitionType) {
}
export function createPropertyColumn(
- prop: PropertyDefinitionType
+ prop: PropertyDefinitionType,
+ hasFilter?: boolean
): ColumnDef {
const columnId = `property_${prop.id}`
const filterFn = getPropertyFilterFn(prop.fieldType)
@@ -79,7 +80,7 @@ export function createPropertyColumn(
minSize: 220,
size: 220,
maxSize: 300,
- filterFn,
+ filterFn: hasFilter ? filterFn : undefined,
} as ColumnDef
}
@@ -89,11 +90,12 @@ type PropertyDefinitionsData = {
export function getPropertyColumnsForEntity(
propertyDefinitionsData: PropertyDefinitionsData,
- entity: PropertyEntity
+ entity: PropertyEntity,
+ hasFilter?: boolean
): ColumnDef[] {
if (!propertyDefinitionsData?.propertyDefinitions) return []
const properties = propertyDefinitionsData.propertyDefinitions.filter(
def => def.isActive && def.allowedEntities.includes(entity)
)
- return properties.map(prop => createPropertyColumn(prop))
+ return properties.map(prop => createPropertyColumn(prop, hasFilter))
}
diff --git a/web/utils/propertyFilterMapping.ts b/web/utils/propertyFilterMapping.ts
index a73a32e..06a3bfe 100644
--- a/web/utils/propertyFilterMapping.ts
+++ b/web/utils/propertyFilterMapping.ts
@@ -1,5 +1,5 @@
import { FieldType } from '@/api/gql/generated'
-import type { TableFilterCategory } from '@helpwave/hightide'
+import type { DataType } from '@helpwave/hightide'
/**
* Maps a FieldType to the appropriate filter function name for TanStack Table.
@@ -9,7 +9,7 @@ import type { TableFilterCategory } from '@helpwave/hightide'
* - date vs datetime (for proper date/time filtering)
* - tags (multi-select) vs tags_single (single select)
*/
-export function getPropertyFilterFn(fieldType: FieldType): TableFilterCategory {
+export function getPropertyFilterFn(fieldType: FieldType): DataType {
switch (fieldType) {
case FieldType.FieldTypeCheckbox:
return 'boolean'
diff --git a/web/utils/tableStateToApi.ts b/web/utils/tableStateToApi.ts
index c7342bc..69e0d74 100644
--- a/web/utils/tableStateToApi.ts
+++ b/web/utils/tableStateToApi.ts
@@ -1,97 +1,109 @@
import type { ColumnFiltersState, PaginationState, SortingState } from '@tanstack/react-table'
import type { FilterInput, FilterOperator, FilterParameter, SortInput } from '@/api/gql/generated'
import { ColumnType, SortDirection } from '@/api/gql/generated'
-import type { TableFilterValue } from '@helpwave/hightide'
+import type { DataType, FilterValue, FilterOperator as HightideFilterOperator } from '@helpwave/hightide'
-const TABLE_OPERATOR_TO_API: Record = {
- textEquals: 'TEXT_EQUALS' as FilterOperator,
- textNotEquals: 'TEXT_NOT_EQUALS' as FilterOperator,
- textContains: 'TEXT_CONTAINS' as FilterOperator,
- textNotContains: 'TEXT_NOT_CONTAINS' as FilterOperator,
- textStartsWith: 'TEXT_STARTS_WITH' as FilterOperator,
- textEndsWith: 'TEXT_ENDS_WITH' as FilterOperator,
- textNotWhitespace: 'TEXT_NOT_WHITESPACE' as FilterOperator,
- numberEquals: 'NUMBER_EQUALS' as FilterOperator,
- numberNotEquals: 'NUMBER_NOT_EQUALS' as FilterOperator,
- numberGreaterThan: 'NUMBER_GREATER_THAN' as FilterOperator,
- numberGreaterThanOrEqual: 'NUMBER_GREATER_THAN_OR_EQUAL' as FilterOperator,
- numberLessThan: 'NUMBER_LESS_THAN' as FilterOperator,
- numberLessThanOrEqual: 'NUMBER_LESS_THAN_OR_EQUAL' as FilterOperator,
- numberBetween: 'NUMBER_BETWEEN' as FilterOperator,
- numberNotBetween: 'NUMBER_NOT_BETWEEN' as FilterOperator,
- dateEquals: 'DATE_EQUALS' as FilterOperator,
- dateNotEquals: 'DATE_NOT_EQUALS' as FilterOperator,
- dateGreaterThan: 'DATE_GREATER_THAN' as FilterOperator,
- dateGreaterThanOrEqual: 'DATE_GREATER_THAN_OR_EQUAL' as FilterOperator,
- dateLessThan: 'DATE_LESS_THAN' as FilterOperator,
- dateLessThanOrEqual: 'DATE_LESS_THAN_OR_EQUAL' as FilterOperator,
- dateBetween: 'DATE_BETWEEN' as FilterOperator,
- dateNotBetween: 'DATE_NOT_BETWEEN' as FilterOperator,
- dateTimeEquals: 'DATETIME_EQUALS' as FilterOperator,
- dateTimeNotEquals: 'DATETIME_NOT_EQUALS' as FilterOperator,
- dateTimeGreaterThan: 'DATETIME_GREATER_THAN' as FilterOperator,
- dateTimeGreaterThanOrEqual: 'DATETIME_GREATER_THAN_OR_EQUAL' as FilterOperator,
- dateTimeLessThan: 'DATETIME_LESS_THAN' as FilterOperator,
- dateTimeLessThanOrEqual: 'DATETIME_LESS_THAN_OR_EQUAL' as FilterOperator,
- dateTimeBetween: 'DATETIME_BETWEEN' as FilterOperator,
- dateTimeNotBetween: 'DATETIME_NOT_BETWEEN' as FilterOperator,
- booleanIsTrue: 'BOOLEAN_IS_TRUE' as FilterOperator,
- booleanIsFalse: 'BOOLEAN_IS_FALSE' as FilterOperator,
- tagsEquals: 'TAGS_EQUALS' as FilterOperator,
- tagsNotEquals: 'TAGS_NOT_EQUALS' as FilterOperator,
- tagsContains: 'TAGS_CONTAINS' as FilterOperator,
- tagsNotContains: 'TAGS_NOT_CONTAINS' as FilterOperator,
- tagsSingleEquals: 'TAGS_SINGLE_EQUALS' as FilterOperator,
- tagsSingleNotEquals: 'TAGS_SINGLE_NOT_EQUALS' as FilterOperator,
- tagsSingleContains: 'TAGS_SINGLE_CONTAINS' as FilterOperator,
- tagsSingleNotContains: 'TAGS_SINGLE_NOT_CONTAINS' as FilterOperator,
- isNull: 'IS_NULL' as FilterOperator,
- isNotNull: 'IS_NOT_NULL' as FilterOperator,
+const TABLE_OPERATOR_TO_API: Record>> = {
+ text: {
+ equals: 'TEXT_EQUALS' as FilterOperator,
+ notEquals: 'TEXT_NOT_EQUALS' as FilterOperator,
+ contains: 'TEXT_CONTAINS' as FilterOperator,
+ notContains: 'TEXT_NOT_CONTAINS' as FilterOperator,
+ startsWith: 'TEXT_STARTS_WITH' as FilterOperator,
+ endsWith: 'TEXT_ENDS_WITH' as FilterOperator,
+ // TODO consider what to do with TEXT_NOT_WHITESPACE
+ isNotUndefined: 'IS_NOT_NULL' as FilterOperator,
+ isUndefined: 'IS_NULL' as FilterOperator,
+ },
+ number: {
+ equals: 'NUMBER_EQUALS' as FilterOperator,
+ notEquals: 'NUMBER_NOT_EQUALS' as FilterOperator,
+ greaterThan: 'NUMBER_GREATER_THAN' as FilterOperator,
+ greaterThanOrEqual: 'NUMBER_GREATER_THAN_OR_EQUAL' as FilterOperator,
+ lessThan: 'NUMBER_LESS_THAN' as FilterOperator,
+ lessThanOrEqual: 'NUMBER_LESS_THAN_OR_EQUAL' as FilterOperator,
+ between: 'NUMBER_BETWEEN' as FilterOperator,
+ notBetween: 'NUMBER_NOT_BETWEEN' as FilterOperator,
+ isNotUndefined: 'IS_NOT_NULL' as FilterOperator,
+ isUndefined: 'IS_NULL' as FilterOperator,
+ },
+ date: {
+ equals: 'DATE_EQUALS' as FilterOperator,
+ notEquals: 'DATE_NOT_EQUALS' as FilterOperator,
+ greaterThan: 'DATE_GREATER_THAN' as FilterOperator,
+ greaterThanOrEqual: 'DATE_GREATER_THAN_OR_EQUAL' as FilterOperator,
+ lessThan: 'DATE_LESS_THAN' as FilterOperator,
+ lessThanOrEqual: 'DATE_LESS_THAN_OR_EQUAL' as FilterOperator,
+ between: 'DATE_BETWEEN' as FilterOperator,
+ notBetween: 'DATE_NOT_BETWEEN' as FilterOperator,
+ isNotUndefined: 'IS_NOT_NULL' as FilterOperator,
+ isUndefined: 'IS_NULL' as FilterOperator,
+ },
+ dateTime: {
+ equals: 'DATETIME_EQUALS' as FilterOperator,
+ notEquals: 'DATETIME_NOT_EQUALS' as FilterOperator,
+ greaterThan: 'DATETIME_GREATER_THAN' as FilterOperator,
+ greaterThanOrEqual: 'DATETIME_GREATER_THAN_OR_EQUAL' as FilterOperator,
+ lessThan: 'DATETIME_LESS_THAN' as FilterOperator,
+ lessThanOrEqual: 'DATETIME_LESS_THAN_OR_EQUAL' as FilterOperator,
+ between: 'DATETIME_BETWEEN' as FilterOperator,
+ notBetween: 'DATETIME_NOT_BETWEEN' as FilterOperator,
+ isNotUndefined: 'IS_NOT_NULL' as FilterOperator,
+ isUndefined: 'IS_NULL' as FilterOperator,
+ },
+ boolean: {
+ isTrue: 'BOOLEAN_IS_TRUE' as FilterOperator,
+ isFalse: 'BOOLEAN_IS_FALSE' as FilterOperator,
+ isNotUndefined: 'IS_NOT_NULL' as FilterOperator,
+ isUndefined: 'IS_NULL' as FilterOperator,
+ },
+ singleTag: {
+ equals: 'TAGS_SINGLE_EQUALS' as FilterOperator,
+ notEquals: 'TAGS_SINGLE_NOT_EQUALS' as FilterOperator,
+ contains: 'TAGS_SINGLE_CONTAINS' as FilterOperator,
+ notContains: 'TAGS_SINGLE_NOT_CONTAINS' as FilterOperator,
+ isNotUndefined: 'IS_NOT_NULL' as FilterOperator,
+ isUndefined: 'IS_NULL' as FilterOperator,
+ },
+ multiTags: {
+ equals: 'TAGS_EQUALS' as FilterOperator,
+ notEquals: 'TAGS_NOT_EQUALS' as FilterOperator,
+ contains: 'TAGS_CONTAINS' as FilterOperator,
+ notContains: 'TAGS_NOT_CONTAINS' as FilterOperator,
+ isNotUndefined: 'IS_NOT_NULL' as FilterOperator,
+ isUndefined: 'IS_NULL' as FilterOperator,
+ },
+ unknownType: {
+ isNotUndefined: 'IS_NOT_NULL' as FilterOperator,
+ isUndefined: 'IS_NULL' as FilterOperator,
+ },
}
-function tableOperatorToApi(operator: string): FilterOperator | null {
- const normalized = operator.replace(/([A-Z])/g, (m) => m.toLowerCase())
- return TABLE_OPERATOR_TO_API[normalized] ?? TABLE_OPERATOR_TO_API[operator] ?? (operator in TABLE_OPERATOR_TO_API ? (operator as FilterOperator) : null)
+function tableOperatorToApi(dataType: DataType, operator: HightideFilterOperator): FilterOperator | null {
+ return TABLE_OPERATOR_TO_API[dataType][operator] ?? null
}
-function toFilterParameter(value: TableFilterValue): FilterParameter {
- const p = value.parameter as Record
+function toFilterParameter(value: FilterValue, propertyDefinitionId?: string): FilterParameter {
+ const parameter = value.parameter
+ const searchTagsUnknownType: unknown[] =
+ parameter.multiOptionSearch ? parameter.multiOptionSearch :
+ parameter.singleOptionSearch ? [parameter.singleOptionSearch] :
+ []
+ const searchTags: string[] = searchTagsUnknownType.map((t) => String(t))
const param: FilterParameter = {
- searchText: typeof p['searchText'] === 'string' ? p['searchText'] : undefined,
- isCaseSensitive: typeof p['isCaseSensitive'] === 'boolean' ? p['isCaseSensitive'] : false,
- compareValue: typeof p['compareValue'] === 'number' ? p['compareValue'] : undefined,
- min: typeof p['min'] === 'number' ? p['min'] : undefined,
- max: typeof p['max'] === 'number' ? p['max'] : undefined,
- }
- if (p['compareDate'] instanceof Date) {
- param.compareDate = (p['compareDate'] as Date).toISOString().slice(0, 10)
- } else if (typeof p['compareDate'] === 'string') {
- param.compareDate = p['compareDate']
- }
- if (p['min'] instanceof Date) param.minDate = (p['min'] as Date).toISOString().slice(0, 10)
- else if (typeof p['min'] === 'string' && (p['min'] as string).length === 10) param.minDate = p['min'] as string
- if (p['max'] instanceof Date) param.maxDate = (p['max'] as Date).toISOString().slice(0, 10)
- else if (typeof p['max'] === 'string' && (p['max'] as string).length === 10) param.maxDate = p['max'] as string
- if (p['compareDatetime'] instanceof Date) {
- param.compareDateTime = (p['compareDatetime'] as Date).toISOString()
- } else if (typeof p['compareDatetime'] === 'string') {
- param.compareDateTime = p['compareDatetime']
- }
- if (p['minDateTime'] instanceof Date) param.minDateTime = (p['minDateTime'] as Date).toISOString()
- else if (typeof p['minDateTime'] === 'string') param.minDateTime = p['minDateTime']
- if (p['maxDateTime'] instanceof Date) param.maxDateTime = (p['maxDateTime'] as Date).toISOString()
- else if (typeof p['maxDateTime'] === 'string') param.maxDateTime = p['maxDateTime']
- if (Array.isArray(p['searchTags'])) {
- param.searchTags = (p['searchTags'] as unknown[]).filter((t): t is string => typeof t === 'string')
- }
- if (Array.isArray(p['searchTagsContains']) && (param.searchTags == null || param.searchTags.length === 0)) {
- param.searchTags = (p['searchTagsContains'] as unknown[]).filter((t): t is string => typeof t === 'string')
- }
- if (param.searchTags == null && p['searchTag'] != null) {
- param.searchTags = [String(p['searchTag'])]
- }
- if (typeof p['propertyDefinitionId'] === 'string') {
- param.propertyDefinitionId = p['propertyDefinitionId']
+ searchText: parameter.searchText,
+ isCaseSensitive: parameter.isCaseSensitive,
+ compareValue: parameter.compareValue,
+ min: parameter.minNumber,
+ max: parameter.maxNumber,
+ compareDate: parameter.compareDate?.toISOString().split('T')[0],
+ minDate: parameter.minDate?.toISOString().split('T')[0],
+ maxDate: parameter.maxDate?.toISOString().split('T')[0],
+ compareDateTime: parameter.compareDate?.toISOString().split('Z')[0],
+ minDateTime: parameter.minDate?.toISOString().split('Z')[0],
+ maxDateTime: parameter.maxDate?.toISOString().split('Z')[0],
+ searchTags,
+ propertyDefinitionId: propertyDefinitionId,
}
return param
}
@@ -126,9 +138,9 @@ export function columnFiltersToFilterInput(
): FilterInput[] {
const result: FilterInput[] = []
for (const filter of filters) {
- const value = filter.value as TableFilterValue
- if (!value?.operator || !value?.parameter) continue
- const apiOperator = tableOperatorToApi(value.operator)
+ const value = filter.value as FilterValue
+ if (!value?.operator || !value?.parameter || !value?.dataType) continue
+ const apiOperator = tableOperatorToApi(value.dataType, value.operator)
if (!apiOperator) continue
const isProperty = isPropertyColumnId(filter.id)
const propertyDefinitionId = getPropertyDefinitionId(filter.id)