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 ? (
+
+
+
+ ) : 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',