From b3c9fed740461490d1730b5c4fb92283223add57 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 3 Oct 2025 15:23:04 +0200 Subject: [PATCH 01/13] feat --- src/lib/Filters.js | 8 +++++++- src/lib/queryFromFilters.js | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lib/Filters.js b/src/lib/Filters.js index 4c8ddf7f2..8e4270066 100644 --- a/src/lib/Filters.js +++ b/src/lib/Filters.js @@ -64,6 +64,12 @@ export const Constraints = { composable: true, comparable: true, }, + matches: { + name: 'matches regex', + field: 'String', + composable: true, + comparable: true, + }, before: { name: 'is before', field: 'Date', @@ -185,7 +191,7 @@ export const FieldConstraints = { Pointer: ['exists', 'dne', 'eq', 'neq', 'starts', 'containedIn', 'unique'], Boolean: ['exists', 'dne', 'eq', 'neq', 'containedIn', 'unique'], Number: ['exists', 'dne', 'eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'containedIn', 'unique'], - String: ['exists', 'dne', 'eq', 'neq', 'starts', 'ends', 'stringContainsString', 'containedIn', 'unique'], + String: ['exists', 'dne', 'eq', 'neq', 'starts', 'ends', 'stringContainsString', 'matches', 'containedIn', 'unique'], Date: [ 'exists', 'dne', diff --git a/src/lib/queryFromFilters.js b/src/lib/queryFromFilters.js index 24d5c729d..ff15289e0 100644 --- a/src/lib/queryFromFilters.js +++ b/src/lib/queryFromFilters.js @@ -171,6 +171,9 @@ function addConstraint(query, filter) { case 'stringContainsString': query.matches(filter.get('field'), filter.get('compareTo'), 'i'); break; + case 'matches': + query.matches(filter.get('field'), filter.get('compareTo')); + break; case 'keyExists': query.exists(filter.get('field') + '.' + filter.get('compareTo')); break; From 36198ac18da7af1a6a0d77bcf312de76bf5bd13c Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:49:39 +0200 Subject: [PATCH 02/13] regex options text field --- .../BrowserFilter/FilterRow.react.js | 35 +++++++++++++++++-- src/components/Filter/Filter.react.js | 10 ++++++ src/lib/queryFromFilters.js | 2 +- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/components/BrowserFilter/FilterRow.react.js b/src/components/BrowserFilter/FilterRow.react.js index 71c0e2ef3..ee3e82f7c 100644 --- a/src/components/BrowserFilter/FilterRow.react.js +++ b/src/components/BrowserFilter/FilterRow.react.js @@ -29,7 +29,9 @@ function compareValue( active, parentContentId, setFocus, - currentConstraint + currentConstraint, + modifiers, + onChangeModifiers ) { if (currentConstraint === 'containedIn') { return ( @@ -60,6 +62,29 @@ function compareValue( return null; case 'Object': case 'String': + if (currentConstraint === 'matches') { + return ( + <> + onChangeCompareTo(e.target.value)} + onKeyDown={onKeyDown} + ref={setFocus} + style={{ width: '140px' }} + /> + onChangeModifiers(e.target.value)} + onKeyDown={onKeyDown} + style={{ width: '60px', marginLeft: '4px' }} + /> + + ); + } return ( @@ -267,4 +296,6 @@ FilterRow.propTypes = { currentConstraint: PropTypes.string.isRequired, compareTo: PropTypes.any, compareInfo: PropTypes.object, + modifiers: PropTypes.string, + onChangeModifiers: PropTypes.func, }; diff --git a/src/components/Filter/Filter.react.js b/src/components/Filter/Filter.react.js index 2bea58a07..f4f105529 100644 --- a/src/components/Filter/Filter.react.js +++ b/src/components/Filter/Filter.react.js @@ -63,6 +63,7 @@ function changeConstraint(schema, currentClassName, filters, index, newConstrain constraint: newConstraint, compareTo: compareType && prevCompareTo ? prevCompareTo : newConstraint === 'containedIn' ? [] : Filters.DefaultComparisons[compareType], + modifiers: newConstraint === 'matches' ? 'i' : undefined, }); return filters.set(index, newFilter); } @@ -72,6 +73,10 @@ function changeCompareTo(schema, filters, index, type, newCompare) { return filters.set(index, filters.get(index).set('compareTo', newValue)); } +function changeModifiers(filters, index, newModifiers) { + return filters.set(index, filters.get(index).set('modifiers', newModifiers)); +} + function deleteRow(filters, index) { return filters.delete(index); } @@ -124,6 +129,7 @@ const Filter = ({ const field = filter.get('field'); const constraint = filter.get('constraint'); const compareTo = filter.get('compareTo'); + const modifiers = filter.get('modifiers'); let fields = []; if (available[currentClassName]) { fields = Object.keys(available[currentClassName]).concat([]); @@ -182,6 +188,7 @@ const Filter = ({ currentField: field, currentConstraint: constraint, compareTo, + modifiers, key: field + '-' + constraint + '-' + i, onChangeClass: newClassName => { onChange(changeClass(schema, filters, i, newClassName)); @@ -197,6 +204,9 @@ const Filter = ({ onChangeCompareTo: newCompare => { onChange(changeCompareTo(schema, filters, i, compareType, newCompare)); }, + onChangeModifiers: newModifiers => { + onChange(changeModifiers(filters, i, newModifiers)); + }, onKeyDown: ({ key }) => { if (key === 'Enter') { onSearch(); diff --git a/src/lib/queryFromFilters.js b/src/lib/queryFromFilters.js index ff15289e0..9211612c1 100644 --- a/src/lib/queryFromFilters.js +++ b/src/lib/queryFromFilters.js @@ -172,7 +172,7 @@ function addConstraint(query, filter) { query.matches(filter.get('field'), filter.get('compareTo'), 'i'); break; case 'matches': - query.matches(filter.get('field'), filter.get('compareTo')); + query.matches(filter.get('field'), filter.get('compareTo'), filter.get('modifiers')); break; case 'keyExists': query.exists(filter.get('field') + '.' + filter.get('compareTo')); From 4192ed3774f9e8e862219ced30915c8086544088 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:49:50 +0200 Subject: [PATCH 03/13] query error handling --- src/dashboard/Data/Browser/Browser.react.js | 77 ++++++++++++------- .../Data/Browser/ObjectPickerDialog.react.js | 48 ++++++++---- 2 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 014473bc2..78e6cbd41 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -1114,40 +1114,60 @@ class Browser extends DashboardView { async fetchData(source, filters = new List()) { this.loadingFilters = JSON.stringify(filters.toJSON()); - const data = await this.fetchParseData(source, filters); - if (this.loadingFilters !== JSON.stringify(filters.toJSON())) { - return; - } + try { + const data = await this.fetchParseData(source, filters); + if (this.loadingFilters !== JSON.stringify(filters.toJSON())) { + return; + } - const filteredCounts = { ...this.state.filteredCounts }; - if (filters.size > 0) { - if (this.state.isUnique) { - filteredCounts[source] = data.length; + const filteredCounts = { ...this.state.filteredCounts }; + if (filters.size > 0) { + if (this.state.isUnique) { + filteredCounts[source] = data.length; + } else { + filteredCounts[source] = await this.fetchParseDataCount(source, filters); + } } else { - filteredCounts[source] = await this.fetchParseDataCount(source, filters); + delete filteredCounts[source]; } - } else { - delete filteredCounts[source]; + this.setState({ + data: data, + filters, + lastMax: this.state.limit, + filteredCounts: filteredCounts, + }); + } catch (error) { + const msg = typeof error === 'string' ? error : error.message; + this.setState({ + data: [], + filters, + lastMax: this.state.limit, + }); + this.showNote(msg, true); } - this.setState({ - data: data, - filters, - lastMax: this.state.limit, - filteredCounts: filteredCounts, - }); } async fetchRelation(relation, filters = new List()) { - const data = await this.fetchParseData(relation, filters); - const relationCount = await this.fetchRelationCount(relation); - this.setState({ - relation, - relationCount, - selection: {}, - data, - filters, - lastMax: this.state.limit, - }); + try { + const data = await this.fetchParseData(relation, filters); + const relationCount = await this.fetchRelationCount(relation); + this.setState({ + relation, + relationCount, + selection: {}, + data, + filters, + lastMax: this.state.limit, + }); + } catch (error) { + const msg = typeof error === 'string' ? error : error.message; + this.setState({ + data: [], + filters, + lastMax: this.state.limit, + }); + this.showNote(msg, true); + } } async fetchRelationCount(relation) { @@ -1209,6 +1229,9 @@ class Browser extends DashboardView { data: state.data.concat(nextPage), })); } + }).catch(error => { + const msg = typeof error === 'string' ? error : error.message; + this.showNote(msg, true); }); this.setState({ lastMax: this.state.lastMax + this.state.limit }); } diff --git a/src/dashboard/Data/Browser/ObjectPickerDialog.react.js b/src/dashboard/Data/Browser/ObjectPickerDialog.react.js index a722d5a5c..2403790bf 100644 --- a/src/dashboard/Data/Browser/ObjectPickerDialog.react.js +++ b/src/dashboard/Data/Browser/ObjectPickerDialog.react.js @@ -73,25 +73,40 @@ export default class ObjectPickerDialog extends React.Component { } async selectRelationRows(relation, filters) { - const relationData = await this.fetchParseData(relation, filters); - this.setState({ initialRelationData: relationData }); - relationData.forEach(({ id }) => this.selectRow(id, true)); + try { + const relationData = await this.fetchParseData(relation, filters); + this.setState({ initialRelationData: relationData }); + relationData.forEach(({ id }) => this.selectRow(id, true)); + } catch (error) { + const msg = typeof error === 'string' ? error : error.message; + this.props.showNote(msg, true); + } } async fetchData(source, filters = new List()) { - const data = await this.fetchParseData(source, filters); - const filteredCounts = { ...this.state.filteredCounts }; - if (filters.size > 0) { - filteredCounts[source] = await this.fetchParseDataCount(source, filters); - } else { - delete filteredCounts[source]; + try { + const data = await this.fetchParseData(source, filters); + const filteredCounts = { ...this.state.filteredCounts }; + if (filters.size > 0) { + filteredCounts[source] = await this.fetchParseDataCount(source, filters); + } else { + delete filteredCounts[source]; + } + this.setState({ + data: data, + filters, + lastMax: this.props.limit, + filteredCounts: filteredCounts, + }); + } catch (error) { + const msg = typeof error === 'string' ? error : error.message; + this.setState({ + data: [], + filters, + lastMax: this.props.limit, + }); + this.props.showNote(msg, true); } - this.setState({ - data: data, - filters, - lastMax: this.props.limit, - filteredCounts: filteredCounts, - }); } async fetchParseData(source, filters) { @@ -175,6 +190,9 @@ export default class ObjectPickerDialog extends React.Component { data: state.data.concat(nextPage), })); } + }).catch(error => { + const msg = typeof error === 'string' ? error : error.message; + this.props.showNote(msg, true); }); this.setState({ lastMax: this.state.lastMax + this.props.limit }); } From f79e19fef893703a1e9f4b3c867cc7c21c1fa9e1 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 4 Oct 2025 02:58:54 +0200 Subject: [PATCH 04/13] options icon --- .../BrowserFilter/FilterRow.react.js | 202 ++++++++++++++++-- src/components/Filter/Filter.react.js | 3 +- 2 files changed, 185 insertions(+), 20 deletions(-) diff --git a/src/components/BrowserFilter/FilterRow.react.js b/src/components/BrowserFilter/FilterRow.react.js index ee3e82f7c..6519109bc 100644 --- a/src/components/BrowserFilter/FilterRow.react.js +++ b/src/components/BrowserFilter/FilterRow.react.js @@ -11,8 +11,10 @@ import { Constraints } from 'lib/Filters'; import DateTimeEntry from 'components/DateTimeEntry/DateTimeEntry.react'; import Icon from 'components/Icon/Icon.react'; import Parse from 'parse'; +import Popover from 'components/Popover/Popover.react'; +import Position from 'lib/Position'; import PropTypes from 'lib/PropTypes'; -import React, { useCallback } from 'react'; +import React, { useCallback, useState, useRef } from 'react'; import styles from 'components/BrowserFilter/BrowserFilter.scss'; import validateNumeric from 'lib/validateNumeric'; @@ -21,6 +23,164 @@ for (const c in Constraints) { constraintLookup[Constraints[c].name] = c; } +const RegexOptionsButton = ({ modifiers, onChangeModifiers }) => { + const [showOptions, setShowOptions] = useState(false); + const buttonRef = useRef(null); + const dropdownRef = useRef(null); + + // Parse modifiers string into individual flags + const modifiersArray = modifiers ? modifiers.split('') : []; + const hasI = modifiersArray.includes('i'); + const hasU = modifiersArray.includes('u'); + const hasM = modifiersArray.includes('m'); + const hasX = modifiersArray.includes('x'); + const hasS = modifiersArray.includes('s'); + + const toggleModifier = (modifier) => { + let newModifiers = [...modifiersArray]; + if (newModifiers.includes(modifier)) { + newModifiers = newModifiers.filter(m => m !== modifier); + } else { + newModifiers.push(modifier); + } + onChangeModifiers(newModifiers.join('')); + }; + + React.useEffect(() => { + const handleClickOutside = (event) => { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target) && + buttonRef.current && + !buttonRef.current.contains(event.target) + ) { + setShowOptions(false); + } + }; + + if (showOptions) { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + } + }, [showOptions]); + + const optionsDropdown = showOptions ? ( + +
+
+ Regex Options +
+ + + + + +
+
+ ) : null; + + return ( + <> + + {optionsDropdown} + + ); +}; + function compareValue( info, value, @@ -64,7 +224,7 @@ function compareValue( case 'String': if (currentConstraint === 'matches') { return ( - <> +
onChangeCompareTo(e.target.value)} onKeyDown={onKeyDown} ref={setFocus} - style={{ width: '140px' }} - /> - onChangeModifiers(e.target.value)} - onKeyDown={onKeyDown} - style={{ width: '60px', marginLeft: '4px' }} + style={{ flex: 1 }} /> - + +
); } return ( @@ -261,13 +414,24 @@ const FilterRow = ({ buildSuggestions={buildFieldSuggestions} buildLabel={() => ''} /> - Constraints[c].name)} - onChange={c => onChangeConstraint(constraintLookup[c], compareTo)} - /> + {compareInfo.type ? ( + Constraints[c].name)} + onChange={c => onChangeConstraint(constraintLookup[c], compareTo)} + /> + ) : ( +
+ Constraints[c].name)} + onChange={c => onChangeConstraint(constraintLookup[c], compareTo)} + /> +
+ )} {compareValue( compareInfo, compareTo, diff --git a/src/components/Filter/Filter.react.js b/src/components/Filter/Filter.react.js index f4f105529..22edf92b1 100644 --- a/src/components/Filter/Filter.react.js +++ b/src/components/Filter/Filter.react.js @@ -97,6 +97,7 @@ const Filter = ({ if (compare !== hasCompareTo) { setCompare(hasCompareTo); } + const currentApp = React.useContext(CurrentApp); blacklist = blacklist || []; const available = Filters.findRelatedClasses(className, allClasses, blacklist, filters); @@ -119,7 +120,7 @@ const Filter = ({ >
Class
Field
-
Condition
+
Condition
{compare &&
Value
}
From 556b96de50e8d94dd9c2a70f1708e949d9b06cd9 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 4 Oct 2025 03:15:46 +0200 Subject: [PATCH 05/13] ui --- .../BrowserFilter/FilterRow.react.js | 18 ++++-------------- src/lib/Filters.js | 2 +- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/components/BrowserFilter/FilterRow.react.js b/src/components/BrowserFilter/FilterRow.react.js index 6519109bc..b2ca0ef0d 100644 --- a/src/components/BrowserFilter/FilterRow.react.js +++ b/src/components/BrowserFilter/FilterRow.react.js @@ -224,7 +224,7 @@ function compareValue( case 'String': if (currentConstraint === 'matches') { return ( -
+
onChangeCompareTo(e.target.value)} onKeyDown={onKeyDown} ref={setFocus} - style={{ flex: 1 }} + style={{ width: '106px' }} />
@@ -414,24 +414,14 @@ const FilterRow = ({ buildSuggestions={buildFieldSuggestions} buildLabel={() => ''} /> - {compareInfo.type ? ( +
Constraints[c].name)} onChange={c => onChangeConstraint(constraintLookup[c], compareTo)} /> - ) : ( -
- Constraints[c].name)} - onChange={c => onChangeConstraint(constraintLookup[c], compareTo)} - /> -
- )} +
{compareValue( compareInfo, compareTo, diff --git a/src/lib/Filters.js b/src/lib/Filters.js index 8e4270066..36deeb62f 100644 --- a/src/lib/Filters.js +++ b/src/lib/Filters.js @@ -59,7 +59,7 @@ export const Constraints = { comparable: true, }, stringContainsString: { - name: 'string contains string', + name: 'contains string', field: 'String', composable: true, comparable: true, From 0f120dd189e36d56e4b4c8dad9798797bb1ef722 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 4 Oct 2025 03:32:14 +0200 Subject: [PATCH 06/13] ui condition width --- src/components/ChromeDropdown/ChromeDropdown.react.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ChromeDropdown/ChromeDropdown.react.js b/src/components/ChromeDropdown/ChromeDropdown.react.js index 38caf3365..d0df783c4 100644 --- a/src/components/ChromeDropdown/ChromeDropdown.react.js +++ b/src/components/ChromeDropdown/ChromeDropdown.react.js @@ -53,7 +53,8 @@ export default class ChromeDropdown extends React.Component { } render() { - let widthStyle = { width: parseFloat(this.props.width || 140) }; + const width = this.props.width ? parseFloat(this.props.width) : '100%'; + let widthStyle = { width }; const styles = this.styles; const color = this.props.color || 'purple'; From 51d4fbbb7851b91a05379bce54aa46c107d9253f Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 4 Oct 2025 03:33:01 +0200 Subject: [PATCH 07/13] placeholder --- src/components/BrowserFilter/FilterRow.react.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/BrowserFilter/FilterRow.react.js b/src/components/BrowserFilter/FilterRow.react.js index b2ca0ef0d..b7a9d1f7a 100644 --- a/src/components/BrowserFilter/FilterRow.react.js +++ b/src/components/BrowserFilter/FilterRow.react.js @@ -228,7 +228,6 @@ function compareValue( onChangeCompareTo(e.target.value)} onKeyDown={onKeyDown} ref={setFocus} From f651cd1d42c641c90379f4dcab9c4b803bfaccc4 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 4 Oct 2025 03:35:25 +0200 Subject: [PATCH 08/13] regex icon --- src/components/BrowserFilter/FilterRow.react.js | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/components/BrowserFilter/FilterRow.react.js b/src/components/BrowserFilter/FilterRow.react.js index b7a9d1f7a..e10c25afb 100644 --- a/src/components/BrowserFilter/FilterRow.react.js +++ b/src/components/BrowserFilter/FilterRow.react.js @@ -155,26 +155,13 @@ const RegexOptionsButton = ({ modifiers, onChangeModifiers }) => { {optionsDropdown} From 41fc6a66c83166c0acc8316d4d17e35bb6e788bf Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 4 Oct 2025 03:43:40 +0200 Subject: [PATCH 09/13] style --- src/components/BrowserFilter/FilterRow.react.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/BrowserFilter/FilterRow.react.js b/src/components/BrowserFilter/FilterRow.react.js index e10c25afb..9f53c5d29 100644 --- a/src/components/BrowserFilter/FilterRow.react.js +++ b/src/components/BrowserFilter/FilterRow.react.js @@ -75,12 +75,14 @@ const RegexOptionsButton = ({ modifiers, onChangeModifiers }) => {
From 0d62f21050325c25b1695d2b03a86b32d037bc48 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 4 Oct 2025 03:45:37 +0200 Subject: [PATCH 10/13] layout --- src/components/BrowserFilter/FilterRow.react.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/BrowserFilter/FilterRow.react.js b/src/components/BrowserFilter/FilterRow.react.js index 9f53c5d29..787cfd7f0 100644 --- a/src/components/BrowserFilter/FilterRow.react.js +++ b/src/components/BrowserFilter/FilterRow.react.js @@ -251,6 +251,7 @@ function compareValue( case 'Boolean': return ( Date: Sun, 5 Oct 2025 00:40:27 +0200 Subject: [PATCH 11/13] fix regex pattern --- src/components/Filter/Filter.react.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/components/Filter/Filter.react.js b/src/components/Filter/Filter.react.js index 22edf92b1..43455ac75 100644 --- a/src/components/Filter/Filter.react.js +++ b/src/components/Filter/Filter.react.js @@ -57,12 +57,26 @@ function changeConstraint(schema, currentClassName, filters, index, newConstrain if (Object.prototype.hasOwnProperty.call(Filters.Constraints[newConstraint], 'field')) { compareType = Filters.Constraints[newConstraint].field; } + + // Determine compareTo value + let compareTo; + if (newConstraint === 'containedIn') { + compareTo = []; + } else if (newConstraint === 'matches') { + // For matches constraint, always use empty string, don't reuse previous value + compareTo = ''; + } else if (compareType && prevCompareTo && typeof prevCompareTo === typeof Filters.DefaultComparisons[compareType]) { + // Only reuse prevCompareTo if types match + compareTo = prevCompareTo; + } else { + compareTo = Filters.DefaultComparisons[compareType]; + } + const newFilter = new Map({ class: currentClassName, field: field, constraint: newConstraint, - compareTo: - compareType && prevCompareTo ? prevCompareTo : newConstraint === 'containedIn' ? [] : Filters.DefaultComparisons[compareType], + compareTo, modifiers: newConstraint === 'matches' ? 'i' : undefined, }); return filters.set(index, newFilter); From e31108e2fe0e395faacac4626842b74b4dc425d2 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 5 Oct 2025 01:32:55 +0200 Subject: [PATCH 12/13] fix regex value type --- .../BrowserFilter/BrowserFilter.react.js | 18 ++++++++++--- src/dashboard/Data/Browser/Browser.react.js | 27 ++++++++++++------- src/lib/queryFromFilters.js | 5 +--- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/components/BrowserFilter/BrowserFilter.react.js b/src/components/BrowserFilter/BrowserFilter.react.js index ca6fdd337..2ec0524a3 100644 --- a/src/components/BrowserFilter/BrowserFilter.react.js +++ b/src/components/BrowserFilter/BrowserFilter.react.js @@ -491,9 +491,21 @@ export default class BrowserFilter extends React.Component { const date = new Date(compareTo.iso); return filter.set('compareTo', date); } else if (typeof compareTo === 'string' && !isNaN(Date.parse(compareTo))) { - // Convert date string to JavaScript Date - const date = new Date(compareTo); - return filter.set('compareTo', date); + // Only convert date strings to JavaScript Date if the field type is actually Date + const className = filter.get('class') || this.props.className; + const fieldName = filter.get('field'); + const schema = this.props.schema; + + if (schema && className && fieldName) { + const classSchema = schema[className]; + const fieldType = classSchema?.[fieldName]?.type; + + // Only convert to Date if the field type is actually Date + if (fieldType === 'Date') { + const date = new Date(compareTo); + return filter.set('compareTo', date); + } + } } // Leave JavaScript Date objects and other types unchanged return filter; diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 78e6cbd41..6e0e21abe 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -537,15 +537,24 @@ class Browser extends DashboardView { // Convert date strings to Parse Date objects for proper Parse query functionality const processedFilter = { ...filter, class: filter.class || props.params.className }; - // Check if compareTo is a date string and convert it to a Parse Date object - if (processedFilter.compareTo && - typeof processedFilter.compareTo === 'string' && - !isNaN(Date.parse(processedFilter.compareTo))) { - // Convert string date to Parse Date format for proper query functionality - processedFilter.compareTo = { - __type: 'Date', - iso: new Date(processedFilter.compareTo).toISOString() - }; + // Check the schema to see if this field is a Date type + const classes = props.schema?.data?.get('classes'); + const className = processedFilter.class || props.params.className; + const fieldName = processedFilter.field; + + if (classes && className && fieldName) { + const classSchema = classes.get(className); + const fieldType = classSchema?.get(fieldName)?.type; + + // If field type is Date and compareTo is not already a Date object, convert it + if (fieldType === 'Date' && + processedFilter.compareTo && + typeof processedFilter.compareTo !== 'object') { + processedFilter.compareTo = { + __type: 'Date', + iso: new Date(processedFilter.compareTo).toISOString() + }; + } } filters = filters.push(Map(processedFilter)); diff --git a/src/lib/queryFromFilters.js b/src/lib/queryFromFilters.js index 9211612c1..bfa147307 100644 --- a/src/lib/queryFromFilters.js +++ b/src/lib/queryFromFilters.js @@ -168,11 +168,8 @@ function addConstraint(query, filter) { case 'containedIn': query.containedIn(filter.get('field'), filter.get('compareTo')); break; - case 'stringContainsString': - query.matches(filter.get('field'), filter.get('compareTo'), 'i'); - break; case 'matches': - query.matches(filter.get('field'), filter.get('compareTo'), filter.get('modifiers')); + query.matches(filter.get('field'), String(filter.get('compareTo')), filter.get('modifiers')); break; case 'keyExists': query.exists(filter.get('field') + '.' + filter.get('compareTo')); From 80e4ac40e9e3f1756a00d519acc8c1984d3a25c9 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 5 Oct 2025 01:33:11 +0200 Subject: [PATCH 13/13] remove string contains string filter --- src/lib/Filters.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lib/Filters.js b/src/lib/Filters.js index 36deeb62f..56533e003 100644 --- a/src/lib/Filters.js +++ b/src/lib/Filters.js @@ -58,12 +58,6 @@ export const Constraints = { name: 'ends with', comparable: true, }, - stringContainsString: { - name: 'contains string', - field: 'String', - composable: true, - comparable: true, - }, matches: { name: 'matches regex', field: 'String', @@ -191,7 +185,7 @@ export const FieldConstraints = { Pointer: ['exists', 'dne', 'eq', 'neq', 'starts', 'containedIn', 'unique'], Boolean: ['exists', 'dne', 'eq', 'neq', 'containedIn', 'unique'], Number: ['exists', 'dne', 'eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'containedIn', 'unique'], - String: ['exists', 'dne', 'eq', 'neq', 'starts', 'ends', 'stringContainsString', 'matches', 'containedIn', 'unique'], + String: ['exists', 'dne', 'eq', 'neq', 'starts', 'ends', 'matches', 'containedIn', 'unique'], Date: [ 'exists', 'dne',