From 30766d0fd7c95eba88353c2a429f4838130a9c5c Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 26 Jul 2025 01:01:55 +0200 Subject: [PATCH 01/23] buttons --- .../BrowserFilter/BrowserFilter.react.js | 131 ++++++++++++++++-- 1 file changed, 116 insertions(+), 15 deletions(-) diff --git a/src/components/BrowserFilter/BrowserFilter.react.js b/src/components/BrowserFilter/BrowserFilter.react.js index 9aaec47e5d..cee930d47d 100644 --- a/src/components/BrowserFilter/BrowserFilter.react.js +++ b/src/components/BrowserFilter/BrowserFilter.react.js @@ -5,24 +5,28 @@ * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ -import * as Filters from 'lib/Filters'; +import styles from 'components/BrowserFilter/BrowserFilter.scss'; +import FilterRow from 'components/BrowserFilter/FilterRow.react'; import Button from 'components/Button/Button.react'; +import Checkbox from 'components/Checkbox/Checkbox.react'; +import Field from 'components/Field/Field.react'; import Filter from 'components/Filter/Filter.react'; -import FilterRow from 'components/BrowserFilter/FilterRow.react'; import Icon from 'components/Icon/Icon.react'; +import Label from 'components/Label/Label.react'; import Popover from 'components/Popover/Popover.react'; -import Field from 'components/Field/Field.react'; import TextInput from 'components/TextInput/TextInput.react'; -import Label from 'components/Label/Label.react'; +import { CurrentApp } from 'context/currentApp'; +import { List, Map } from 'immutable'; +import * as ClassPreferences from 'lib/ClassPreferences'; +import * as Filters from 'lib/Filters'; import Position from 'lib/Position'; import React from 'react'; -import styles from 'components/BrowserFilter/BrowserFilter.scss'; -import Checkbox from 'components/Checkbox/Checkbox.react'; -import { List, Map } from 'immutable'; const POPOVER_CONTENT_ID = 'browserFilterPopover'; export default class BrowserFilter extends React.Component { + static contextType = CurrentApp; + constructor(props) { super(props); @@ -34,6 +38,8 @@ export default class BrowserFilter extends React.Component { name: '', blacklistedFilters: Filters.BLACKLISTED_FILTERS.concat(props.blacklistedFilters), relativeDates: false, + showMore: false, + originalFilterName: '', }; this.toggle = this.toggle.bind(this); this.wrapRef = React.createRef(); @@ -45,6 +51,59 @@ export default class BrowserFilter extends React.Component { } } + getCurrentFilterInfo() { + // Extract filterId from URL if present + const urlParams = new URLSearchParams(window.location.search); + const filterId = urlParams.get('filterId'); + + if (filterId) { + const preferences = ClassPreferences.getPreferences( + this.context.applicationId, + this.props.className + ); + + if (preferences.filters) { + const savedFilter = preferences.filters.find(filter => filter.id === filterId); + if (savedFilter) { + return { + id: savedFilter.id, + name: savedFilter.name, + isApplied: true + }; + } + } + } + + return { + id: null, + name: '', + isApplied: false + }; + } + + toggleMore() { + const currentFilter = this.getCurrentFilterInfo(); + this.setState(prevState => ({ + showMore: !prevState.showMore, + name: currentFilter.name, + originalFilterName: currentFilter.name, + })); + } + + isFilterNameExists(name) { + const preferences = ClassPreferences.getPreferences( + this.context.applicationId, + this.props.className + ); + + if (preferences.filters && name) { + return preferences.filters.some(filter => + filter.name === name && filter.id !== this.getCurrentFilterInfo().id + ); + } + return false; + } + toggle() { let filters = this.props.filters; if (this.props.filters.size === 0) { @@ -66,6 +125,7 @@ export default class BrowserFilter extends React.Component { confirmName: false, editMode: this.props.filters.size === 0, relativeDates: false, // Reset relative dates state when opening/closing + showMore: false, // Reset showMore state when opening/closing })); this.props.setCurrent(null); } @@ -211,6 +271,19 @@ export default class BrowserFilter extends React.Component { )} + {this.state.showMore && ( + } + input={ + this.setState({ name })} + /> + } + /> + )} + {this.state.confirmName && (
)} - {!this.state.confirmName && ( + {this.state.confirmDelete && (
-
-
+ )} + {!this.state.confirmName && !this.state.confirmDelete && ( +
+ {this.state.showMore && ( +
+
+ )} +
+ {!this.state.showMore && ( +
diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 2953969b1a..ed4681af1b 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -227,6 +227,7 @@ class Browser extends DashboardView { this.getClassRelationColumns = this.getClassRelationColumns.bind(this); this.showCreateClass = this.showCreateClass.bind(this); this.refresh = this.refresh.bind(this); + this.deleteFilter = this.deleteFilter.bind(this); this.selectRow = this.selectRow.bind(this); this.updateRow = this.updateRow.bind(this); this.updateOrdering = this.updateOrdering.bind(this); @@ -1281,6 +1282,24 @@ class Browser extends DashboardView { super.forceUpdate(); } + deleteFilter(filterId) { + const preferences = ClassPreferences.getPreferences( + this.context.applicationId, + this.props.params.className + ); + + if (preferences.filters) { + const updatedFilters = preferences.filters.filter(filter => filter.id !== filterId); + ClassPreferences.updatePreferences( + { ...preferences, filters: updatedFilters }, + this.context.applicationId, + this.props.params.className + ); + } + + super.forceUpdate(); + } + updateOrdering(ordering) { const source = this.state.relation || this.props.params.className; this.setState( @@ -2278,6 +2297,7 @@ class Browser extends DashboardView { filters={this.state.filters} onFilterChange={this.updateFilters} onFilterSave={(...args) => this.saveFilters(...args)} + onDeleteFilter={this.deleteFilter} onRemoveColumn={this.showRemoveColumn} onDeleteRows={this.showDeleteRows} onDropClass={this.showDropClass} diff --git a/src/dashboard/Data/Browser/BrowserToolbar.react.js b/src/dashboard/Data/Browser/BrowserToolbar.react.js index b28aad55c8..a311ce5188 100644 --- a/src/dashboard/Data/Browser/BrowserToolbar.react.js +++ b/src/dashboard/Data/Browser/BrowserToolbar.react.js @@ -32,6 +32,7 @@ const BrowserToolbar = ({ setCurrent, onFilterChange, onFilterSave, + onDeleteFilter, onAddColumn, onAddRow, onAddRowWithModal, @@ -401,6 +402,7 @@ const BrowserToolbar = ({ filters={filters} onChange={onFilterChange} onSaveFilter={onFilterSave} + onDeleteFilter={onDeleteFilter} className={classNameForEditors} blacklistedFilters={onAddRow ? [] : ['unique']} disabled={isPendingEditCloneRows} From 294ce66586bd6e7eab0ff3ce8320b6a2c569cd15 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 26 Jul 2025 03:50:56 +0200 Subject: [PATCH 06/23] Update Browser.react.js --- src/dashboard/Data/Browser/Browser.react.js | 36 ++++++++++++--------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index ed4681af1b..b4cc87268e 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -1263,23 +1263,29 @@ class Browser extends DashboardView { } removeFilter(filter) { - const preferences = ClassPreferences.getPreferences( - this.context.applicationId, - this.props.params.className - ); - let i = preferences.filters.length; - while (i--) { - const item = preferences.filters[i]; - if (JSON.stringify(item) === JSON.stringify(filter)) { - preferences.filters.splice(i, 1); + // Use the new deleteFilter method for consistency + if (filter && filter.id) { + this.deleteFilter(filter.id); + } else { + // Fallback to old method if no ID is available + const preferences = ClassPreferences.getPreferences( + this.context.applicationId, + this.props.params.className + ); + let i = preferences.filters.length; + while (i--) { + const item = preferences.filters[i]; + if (JSON.stringify(item) === JSON.stringify(filter)) { + preferences.filters.splice(i, 1); + } } + ClassPreferences.updatePreferences( + preferences, + this.context.applicationId, + this.props.params.className + ); + super.forceUpdate(); } - ClassPreferences.updatePreferences( - preferences, - this.context.applicationId, - this.props.params.className - ); - super.forceUpdate(); } deleteFilter(filterId) { From 2b9267ef3c9f098aaaed225412859bafa3860051 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 26 Jul 2025 03:55:55 +0200 Subject: [PATCH 07/23] remove X to delete filter --- .../CategoryList/CategoryList.react.js | 9 ---- src/dashboard/Data/Browser/Browser.react.js | 49 +++++++------------ 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/src/components/CategoryList/CategoryList.react.js b/src/components/CategoryList/CategoryList.react.js index b7409030bf..01a01bf9e4 100644 --- a/src/components/CategoryList/CategoryList.react.js +++ b/src/components/CategoryList/CategoryList.react.js @@ -168,15 +168,6 @@ export default class CategoryList extends React.Component { > {name} - { - e.preventDefault(); - this.props.removeFilter(filterData); - }} - > - × -
); })} diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index b4cc87268e..7494cca870 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -1262,32 +1262,6 @@ class Browser extends DashboardView { super.forceUpdate(); } - removeFilter(filter) { - // Use the new deleteFilter method for consistency - if (filter && filter.id) { - this.deleteFilter(filter.id); - } else { - // Fallback to old method if no ID is available - const preferences = ClassPreferences.getPreferences( - this.context.applicationId, - this.props.params.className - ); - let i = preferences.filters.length; - while (i--) { - const item = preferences.filters[i]; - if (JSON.stringify(item) === JSON.stringify(filter)) { - preferences.filters.splice(i, 1); - } - } - ClassPreferences.updatePreferences( - preferences, - this.context.applicationId, - this.props.params.className - ); - super.forceUpdate(); - } - } - deleteFilter(filterId) { const preferences = ClassPreferences.getPreferences( this.context.applicationId, @@ -1295,7 +1269,24 @@ class Browser extends DashboardView { ); if (preferences.filters) { - const updatedFilters = preferences.filters.filter(filter => filter.id !== filterId); + // Try to find by ID first (modern approach) + let updatedFilters = preferences.filters.filter(filter => filter.id !== filterId); + + // If no filter was removed (ID not found), use fallback method + if (updatedFilters.length === preferences.filters.length && filterId) { + // Fallback: try to find by comparing the entire filter object if filterId is actually a filter object + if (typeof filterId === 'object') { + let i = preferences.filters.length; + updatedFilters = [...preferences.filters]; + while (i--) { + const item = updatedFilters[i]; + if (JSON.stringify(item) === JSON.stringify(filterId)) { + updatedFilters.splice(i, 1); + } + } + } + } + ClassPreferences.updatePreferences( { ...preferences, filters: updatedFilters }, this.context.applicationId, @@ -2121,10 +2112,6 @@ class Browser extends DashboardView { this.resetPage(); this.props.navigate(generatePath(this.context, url)); }} - removeFilter={filter => { - this.resetPage(); - this.removeFilter(filter); - }} classClicked={() => { this.resetPage(); }} From 02da43772267e10d38b1f6039ec53779cbe61279 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 26 Jul 2025 04:38:00 +0200 Subject: [PATCH 08/23] arrows --- .../BrowserFilter/BrowserFilter.react.js | 113 +++++++++++++----- 1 file changed, 81 insertions(+), 32 deletions(-) diff --git a/src/components/BrowserFilter/BrowserFilter.react.js b/src/components/BrowserFilter/BrowserFilter.react.js index 84351b69c0..46353c8de7 100644 --- a/src/components/BrowserFilter/BrowserFilter.react.js +++ b/src/components/BrowserFilter/BrowserFilter.react.js @@ -52,6 +52,29 @@ export default class BrowserFilter extends React.Component { } } + isCurrentFilterSaved() { + // Check if current filter structure matches any saved filter + const preferences = ClassPreferences.getPreferences( + this.context.applicationId, + this.props.className + ); + + if (!preferences.filters || this.props.filters.size === 0) { + return false; + } + + const currentFiltersString = JSON.stringify(this.props.filters.toJS()); + + return preferences.filters.some(savedFilter => { + try { + const savedFiltersString = JSON.stringify(JSON.parse(savedFilter.filter)); + return savedFiltersString === currentFiltersString; + } catch { + return false; + } + }); + } + getCurrentFilterInfo() { // Extract filterId from URL if present const urlParams = new URLSearchParams(window.location.search); @@ -581,12 +604,17 @@ export default class BrowserFilter extends React.Component {
{this.state.showMore && (
-
)}
- {!this.state.showMore && ( -
- )} {this.state.confirmDelete && (
)} - {!this.state.confirmName && !this.state.confirmDelete && ( + {!this.state.confirmDelete && (
{this.state.showMore && (
From 80a74d435c1723d1f81b6fa10aecd446a0e2d205 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 26 Jul 2025 04:50:19 +0200 Subject: [PATCH 11/23] fix filter not deletable --- .../BrowserFilter/BrowserFilter.react.js | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/components/BrowserFilter/BrowserFilter.react.js b/src/components/BrowserFilter/BrowserFilter.react.js index ff7875025c..f56e63b58e 100644 --- a/src/components/BrowserFilter/BrowserFilter.react.js +++ b/src/components/BrowserFilter/BrowserFilter.react.js @@ -52,7 +52,26 @@ export default class BrowserFilter extends React.Component { } isCurrentFilterSaved() { - // Check if current filter structure matches any saved filter + // First check if there's a filterId in the URL (means we're definitely viewing a saved filter) + const urlParams = new URLSearchParams(window.location.search); + const filterId = urlParams.get('filterId'); + + if (filterId) { + const preferences = ClassPreferences.getPreferences( + this.context.applicationId, + this.props.className + ); + + if (preferences.filters) { + // If filterId exists in saved filters, it's definitely a saved filter + const savedFilter = preferences.filters.find(filter => filter.id === filterId); + if (savedFilter) { + return true; + } + } + } + + // Fallback: Check if current filter structure matches any saved filter const preferences = ClassPreferences.getPreferences( this.context.applicationId, this.props.className From 6f8ff696357894145f3e1d5a0d0c8d2fe25f87cb Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 26 Jul 2025 05:10:09 +0200 Subject: [PATCH 12/23] fix --- .../BrowserFilter/BrowserFilter.react.js | 61 +++++++++++-------- src/dashboard/Data/Browser/Browser.react.js | 35 ++++++----- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/src/components/BrowserFilter/BrowserFilter.react.js b/src/components/BrowserFilter/BrowserFilter.react.js index f56e63b58e..1872230614 100644 --- a/src/components/BrowserFilter/BrowserFilter.react.js +++ b/src/components/BrowserFilter/BrowserFilter.react.js @@ -55,13 +55,13 @@ export default class BrowserFilter extends React.Component { // First check if there's a filterId in the URL (means we're definitely viewing a saved filter) const urlParams = new URLSearchParams(window.location.search); const filterId = urlParams.get('filterId'); - + if (filterId) { const preferences = ClassPreferences.getPreferences( this.context.applicationId, this.props.className ); - + if (preferences.filters) { // If filterId exists in saved filters, it's definitely a saved filter const savedFilter = preferences.filters.find(filter => filter.id === filterId); @@ -70,19 +70,19 @@ export default class BrowserFilter extends React.Component { } } } - + // Fallback: Check if current filter structure matches any saved filter const preferences = ClassPreferences.getPreferences( this.context.applicationId, this.props.className ); - + if (!preferences.filters || this.props.filters.size === 0) { return false; } - + const currentFiltersString = JSON.stringify(this.props.filters.toJS()); - + return preferences.filters.some(savedFilter => { try { const savedFiltersString = JSON.stringify(JSON.parse(savedFilter.filter)); @@ -97,13 +97,13 @@ export default class BrowserFilter extends React.Component { // Extract filterId from URL if present const urlParams = new URLSearchParams(window.location.search); const filterId = urlParams.get('filterId'); - + if (filterId) { const preferences = ClassPreferences.getPreferences( this.context.applicationId, this.props.className ); - + if (preferences.filters) { const savedFilter = preferences.filters.find(filter => filter.id === filterId); if (savedFilter) { @@ -118,7 +118,7 @@ export default class BrowserFilter extends React.Component { // If parsing fails, assume no relative dates hasRelativeDates = false; } - + return { id: savedFilter.id, name: savedFilter.name, @@ -128,7 +128,7 @@ export default class BrowserFilter extends React.Component { } } } - + return { id: null, name: '', @@ -139,7 +139,7 @@ export default class BrowserFilter extends React.Component { toggleMore() { const currentFilter = this.getCurrentFilterInfo(); - + this.setState(prevState => { let filtersToUse; if (!prevState.showMore) { @@ -155,7 +155,7 @@ export default class BrowserFilter extends React.Component { // Exiting edit mode - preserve current state filters filtersToUse = prevState.filters; } - + return { showMore: !prevState.showMore, name: prevState.showMore ? prevState.name : currentFilter.name, @@ -171,7 +171,7 @@ export default class BrowserFilter extends React.Component { this.context.applicationId, this.props.className ); - + if (preferences.filters && name) { return preferences.filters.some(filter => filter.name === name && filter.id !== this.getCurrentFilterInfo().id @@ -366,7 +366,7 @@ export default class BrowserFilter extends React.Component { save() { // Store the original UI-friendly filters before any conversion const originalUIFilters = this.state.filters; - + let formatted = this.state.filters.map(filter => { const isComparable = Filters.Constraints[filter.get('constraint')].comparable; if (!isComparable) { @@ -402,21 +402,32 @@ export default class BrowserFilter extends React.Component { return filter; }); } - + // If we're in showMore mode, we're editing an existing filter const currentFilterInfo = this.getCurrentFilterInfo(); const filterId = this.state.showMore ? currentFilterInfo.id : null; - - this.props.onSaveFilter(formatted, this.state.name, this.state.relativeDates, filterId); - + + const savedFilterId = this.props.onSaveFilter(formatted, this.state.name, this.state.relativeDates, filterId); + // Only close the dialog if we're not in edit mode (showMore) if (!this.state.showMore) { + // For new filters, apply the saved filter and update URL + this.props.onChange(formatted); + + // Update URL with the new filter ID if we got one back + if (savedFilterId) { + const urlParams = new URLSearchParams(window.location.search); + urlParams.set('filterId', savedFilterId); + const newUrl = `${window.location.pathname}?${urlParams.toString()}`; + window.history.replaceState({}, '', newUrl); + } + this.toggle(); } else { // In edit mode, update the original filter name but keep the original UI-friendly filters // Convert any Parse Date objects in the UI filters to JavaScript Date objects for proper display const uiFilters = this.convertDatesForDisplay(originalUIFilters); - + this.setState({ originalFilterName: this.state.name, filters: uiFilters, // Ensure UI stays with JavaScript Date objects @@ -448,7 +459,7 @@ export default class BrowserFilter extends React.Component { const compareTo = filter.get('compareTo'); return compareTo && (compareTo instanceof Date || compareTo.__type === 'Date' || compareTo.__type === 'RelativeDate'); }); - + popover = ( filter.id !== currentFilterInfo.id); ClassPreferences.updatePreferences( @@ -548,18 +559,18 @@ export default class BrowserFilter extends React.Component { { ...preferences, filters: updatedFilters } ); } - + // Remove filterId from URL const urlParams = new URLSearchParams(window.location.search); urlParams.delete('filterId'); const newUrl = `${window.location.pathname}${urlParams.toString() ? '?' + urlParams.toString() : ''}`; window.history.replaceState({}, '', newUrl); - + // Clear current filters and close dialog this.props.onChange(new Map()); this.setState({ confirmDelete: false }); this.toggle(); - + // Call onDeleteFilter prop if provided if (this.props.onDeleteFilter) { this.props.onDeleteFilter(currentFilterInfo.id); @@ -609,7 +620,7 @@ export default class BrowserFilter extends React.Component { {(() => { const currentFilter = this.getCurrentFilterInfo(); const isAppliedSavedFilter = currentFilter.isApplied && this.props.filters.size > 0; - + if (isAppliedSavedFilter && !this.state.showMore) { return ( <> diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 7494cca870..c510eed03d 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -1224,7 +1224,9 @@ class Browser extends DashboardView { this.context.applicationId, this.props.params.className ); - + + let newFilterId = filterId; + if (filterId) { // Update existing filter const existingFilterIndex = preferences.filters.findIndex(filter => filter.id === filterId); @@ -1236,23 +1238,23 @@ class Browser extends DashboardView { }; } else { // Fallback: if filter not found, create new one + newFilterId = crypto.randomUUID(); preferences.filters.push({ name, - id: crypto.randomUUID(), + id: newFilterId, filter: _filters, }); } } else { - // Create new filter only if it doesn't already exist - if (!preferences.filters.find(filter => filter.filter === _filters)) { - preferences.filters.push({ - name, - id: crypto.randomUUID(), - filter: _filters, - }); - } + // Create new filter + newFilterId = crypto.randomUUID(); + preferences.filters.push({ + name, + id: newFilterId, + filter: _filters, + }); } - + ClassPreferences.updatePreferences( preferences, this.context.applicationId, @@ -1260,6 +1262,9 @@ class Browser extends DashboardView { ); super.forceUpdate(); + + // Return the filter ID for new filters so the caller can apply them + return newFilterId; } deleteFilter(filterId) { @@ -1267,11 +1272,11 @@ class Browser extends DashboardView { this.context.applicationId, this.props.params.className ); - + if (preferences.filters) { // Try to find by ID first (modern approach) let updatedFilters = preferences.filters.filter(filter => filter.id !== filterId); - + // If no filter was removed (ID not found), use fallback method if (updatedFilters.length === preferences.filters.length && filterId) { // Fallback: try to find by comparing the entire filter object if filterId is actually a filter object @@ -1286,14 +1291,14 @@ class Browser extends DashboardView { } } } - + ClassPreferences.updatePreferences( { ...preferences, filters: updatedFilters }, this.context.applicationId, this.props.params.className ); } - + super.forceUpdate(); } From e76cf089e7bb771c9b11ac823b34bdd9aba58c3a Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 26 Jul 2025 05:16:36 +0200 Subject: [PATCH 13/23] fix sidebar filter highlight --- .../BrowserFilter/BrowserFilter.react.js | 9 ++++---- .../CategoryList/CategoryList.react.js | 21 ++++++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/components/BrowserFilter/BrowserFilter.react.js b/src/components/BrowserFilter/BrowserFilter.react.js index 1872230614..5543b4daaf 100644 --- a/src/components/BrowserFilter/BrowserFilter.react.js +++ b/src/components/BrowserFilter/BrowserFilter.react.js @@ -69,9 +69,12 @@ export default class BrowserFilter extends React.Component { return true; } } + // If filterId is in URL but not found in saved filters, it's not saved + return false; } - // Fallback: Check if current filter structure matches any saved filter + // Fallback only when no filterId in URL: Check if current filter structure matches any saved filter + // This is for legacy compatibility const preferences = ClassPreferences.getPreferences( this.context.applicationId, this.props.className @@ -91,9 +94,7 @@ export default class BrowserFilter extends React.Component { return false; } }); - } - - getCurrentFilterInfo() { + } getCurrentFilterInfo() { // Extract filterId from URL if present const urlParams = new URLSearchParams(window.location.search); const filterId = urlParams.get('filterId'); diff --git a/src/components/CategoryList/CategoryList.react.js b/src/components/CategoryList/CategoryList.react.js index 01a01bf9e4..8c94ff3e76 100644 --- a/src/components/CategoryList/CategoryList.react.js +++ b/src/components/CategoryList/CategoryList.react.js @@ -6,12 +6,12 @@ * the root directory of this source tree. */ import styles from 'components/CategoryList/CategoryList.scss'; +import Icon from 'components/Icon/Icon.react'; import { CurrentApp } from 'context/currentApp'; import generatePath from 'lib/generatePath'; import PropTypes from 'lib/PropTypes'; import React from 'react'; import { Link } from 'react-router-dom'; -import Icon from 'components/Icon/Icon.react'; export default class CategoryList extends React.Component { static contextType = CurrentApp; @@ -57,7 +57,14 @@ export default class CategoryList extends React.Component { const filterId = query.get('filterId'); for (let i = 0; i < c.filters?.length; i++) { const filter = c.filters[i]; - if (queryFilter === filter.filter || filterId && filterId === filter.id) { + // Prioritize filterId matching, only fall back to content comparison if no filterId + if (filterId) { + if (filterId === filter.id) { + height += (i + 1) * 20; + break; + } + } else if (queryFilter === filter.filter) { + // Legacy fallback: match by filter content when no filterId is present height += (i + 1) * 20; break; } @@ -113,7 +120,15 @@ export default class CategoryList extends React.Component { const queryFilterId = query.get('filterId'); for (let i = 0; i < c.filters?.length; i++) { const filter = c.filters[i]; - if (queryFilter === filter.filter || queryFilterId && queryFilterId === filter.id) { + // Prioritize filterId matching, only fall back to content comparison if no filterId + if (queryFilterId) { + if (queryFilterId === filter.id) { + selectedFilter = i; + className = ''; + break; + } + } else if (queryFilter === filter.filter) { + // Legacy fallback: match by filter content when no filterId is present selectedFilter = i; className = ''; break; From 31ee8f03b343d9d658ab945e300417fc2b1077f0 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 26 Jul 2025 11:31:12 +0200 Subject: [PATCH 14/23] fix button style on click --- src/components/Button/Button.react.js | 4 ++++ src/components/Button/Button.scss | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Button/Button.react.js b/src/components/Button/Button.react.js index 267dbdabe0..20cb426270 100644 --- a/src/components/Button/Button.react.js +++ b/src/components/Button/Button.react.js @@ -53,6 +53,10 @@ const Button = props => { e.target.blur(); } }} + onMouseLeave={e => { + // Remove focus when mouse leaves to prevent sticky focus states + e.target.blur(); + }} > {props.value} diff --git a/src/components/Button/Button.scss b/src/components/Button/Button.scss index 6612992b9e..1b37c76540 100644 --- a/src/components/Button/Button.scss +++ b/src/components/Button/Button.scss @@ -77,7 +77,7 @@ border-color: $white; line-height: 28px; - &:hover, &:focus{ + &:hover, &:focus { background-color: $white; color: $blue; } From 864d583a975bb72fe9f9bb833e47838cd7a80ab7 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 26 Jul 2025 11:44:50 +0200 Subject: [PATCH 15/23] fix save button on change --- .../BrowserFilter/BrowserFilter.react.js | 94 +++++++++---------- src/components/Button/Button.react.js | 4 +- 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/src/components/BrowserFilter/BrowserFilter.react.js b/src/components/BrowserFilter/BrowserFilter.react.js index 5543b4daaf..a881362aa0 100644 --- a/src/components/BrowserFilter/BrowserFilter.react.js +++ b/src/components/BrowserFilter/BrowserFilter.react.js @@ -40,6 +40,7 @@ export default class BrowserFilter extends React.Component { showMore: false, originalFilterName: '', confirmDelete: false, + originalFilters: new List(), // Track original filters when entering edit mode }; this.toggle = this.toggle.bind(this); this.wrapRef = React.createRef(); @@ -143,8 +144,13 @@ export default class BrowserFilter extends React.Component { this.setState(prevState => { let filtersToUse; + let originalFiltersToStore = prevState.originalFilters; + if (!prevState.showMore) { // Entering edit mode + // Store the original applied filters for comparison + originalFiltersToStore = this.props.filters; + // If we already have filters in state (e.g., user added fields), use those but convert only Parse dates // Otherwise, convert the props filters for display (preserving RelativeDate objects) if (prevState.filters.size > 0) { @@ -163,6 +169,7 @@ export default class BrowserFilter extends React.Component { originalFilterName: currentFilter.name, relativeDates: currentFilter.hasRelativeDates, filters: filtersToUse, + originalFilters: originalFiltersToStore, }; }); } @@ -181,25 +188,49 @@ export default class BrowserFilter extends React.Component { return false; } + // Helper method to normalize filters for comparison + // Converts all date formats to a consistent format for comparison + normalizeFiltersForComparison(filters) { + return filters.map(filter => { + const compareTo = filter.get('compareTo'); + if (!compareTo) { + return filter; + } + + // Convert all date types to ISO string for consistent comparison + if (compareTo instanceof Date) { + return filter.set('compareTo', compareTo.toISOString()); + } else if (compareTo.__type === 'Date') { + return filter.set('compareTo', compareTo.iso); + } else if (compareTo.__type === 'RelativeDate') { + // Convert RelativeDate to ISO string + const now = new Date(); + const date = new Date(now.getTime() + compareTo.value * 1000); + return filter.set('compareTo', date.toISOString()); + } + return filter; + }); + } + hasFilterContentChanged() { // If we're not in showMore mode (editing a saved filter), return false if (!this.state.showMore) { return false; } - // Compare current state filters with the originally applied filters - const currentFilters = this.state.filters; - const appliedFilters = this.props.filters; + // Compare current state filters with the original filters stored when entering edit mode + const currentFilters = this.normalizeFiltersForComparison(this.state.filters); + const originalFilters = this.normalizeFiltersForComparison(this.state.originalFilters); // If the sizes are different, content has changed - if (currentFilters.size !== appliedFilters.size) { + if (currentFilters.size !== originalFilters.size) { return true; } // Compare each filter for (let i = 0; i < currentFilters.size; i++) { const currentFilter = currentFilters.get(i); - const appliedFilter = appliedFilters.get(i); + const originalFilter = originalFilters.get(i); // Compare each property of the filter const currentClass = currentFilter.get('class'); @@ -207,49 +238,16 @@ export default class BrowserFilter extends React.Component { const currentConstraint = currentFilter.get('constraint'); const currentCompareTo = currentFilter.get('compareTo'); - const appliedClass = appliedFilter.get('class'); - const appliedField = appliedFilter.get('field'); - const appliedConstraint = appliedFilter.get('constraint'); - const appliedCompareTo = appliedFilter.get('compareTo'); + const originalClass = originalFilter.get('class'); + const originalField = originalFilter.get('field'); + const originalConstraint = originalFilter.get('constraint'); + const originalCompareTo = originalFilter.get('compareTo'); - // Check basic properties - if (currentClass !== appliedClass || - currentField !== appliedField || - currentConstraint !== appliedConstraint) { - return true; - } - - // Special handling for date comparisons - if (currentCompareTo && currentCompareTo.__type === 'Date' && appliedCompareTo && appliedCompareTo.__type === 'RelativeDate') { - // Convert RelativeDate to Date for comparison - const now = new Date(); - const appliedDate = new Date(now.getTime() + appliedCompareTo.value * 1000); - const currentDate = new Date(currentCompareTo.iso); - if (Math.abs(currentDate.getTime() - appliedDate.getTime()) > 1000) { // Allow 1 second tolerance - return true; - } - } else if (currentCompareTo instanceof Date && appliedCompareTo && appliedCompareTo.__type === 'RelativeDate') { - // Convert RelativeDate to Date for comparison - const now = new Date(); - const appliedDate = new Date(now.getTime() + appliedCompareTo.value * 1000); - if (Math.abs(currentCompareTo.getTime() - appliedDate.getTime()) > 1000) { // Allow 1 second tolerance - return true; - } - } else if (!currentCompareTo && !appliedCompareTo) { - // Both are null/undefined, continue - continue; - } else if (currentCompareTo instanceof Date && appliedCompareTo instanceof Date) { - // Both are Date objects - if (currentCompareTo.getTime() !== appliedCompareTo.getTime()) { - return true; - } - } else if (currentCompareTo && currentCompareTo.__type === 'Date' && appliedCompareTo && appliedCompareTo.__type === 'Date') { - // Both are Parse Date objects - if (currentCompareTo.iso !== appliedCompareTo.iso) { - return true; - } - } else if (currentCompareTo !== appliedCompareTo) { - // Other types or one is Date and other is not + // Check all properties for equality + if (currentClass !== originalClass || + currentField !== originalField || + currentConstraint !== originalConstraint || + currentCompareTo !== originalCompareTo) { return true; } } @@ -600,7 +598,7 @@ export default class BrowserFilter extends React.Component { />
@@ -616,59 +621,28 @@ export default class BrowserFilter extends React.Component {
)}
- {(() => { - const currentFilter = this.getCurrentFilterInfo(); - const isAppliedSavedFilter = currentFilter.isApplied && this.props.filters.size > 0; - - if (isAppliedSavedFilter && !this.state.showMore) { - return ( - <> - this.toggleMore()} - > - - -
)} @@ -624,7 +672,7 @@ export default class BrowserFilter extends React.Component { {!this.state.showMore && ( <> this.toggleMore()} > Date: Sat, 26 Jul 2025 13:31:17 +0200 Subject: [PATCH 23/23] lint --- src/dashboard/Data/Browser/Browser.react.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 0119bac954..6faaf967d9 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -1201,11 +1201,11 @@ class Browser extends DashboardView { } else { const source = this.props.params.className; const _filters = JSON.stringify(filters.toJSON()); - + // Preserve filterId from current URL if it exists const currentUrlParams = new URLSearchParams(window.location.search); const currentFilterId = currentUrlParams.get('filterId'); - + let url = `browser/${source}`; if (filters.size === 0) { // If no filters, don't include any query parameters @@ -1214,15 +1214,15 @@ class Browser extends DashboardView { // Build query parameters const queryParams = new URLSearchParams(); queryParams.set('filters', _filters); - + // Preserve filterId if it exists in current URL if (currentFilterId) { queryParams.set('filterId', currentFilterId); } - + url = `browser/${source}?${queryParams.toString()}`; } - + // filters param change is making the fetch call this.props.navigate(generatePath(this.context, url)); } @@ -1371,20 +1371,20 @@ class Browser extends DashboardView { // Preserve filterId from current URL if it exists const currentUrlParams = new URLSearchParams(window.location.search); const currentFilterId = currentUrlParams.get('filterId'); - + let url = this.getRelationURL(); if (filters && filters.size) { const queryParams = new URLSearchParams(); queryParams.set('filters', JSON.stringify(filters.toJSON())); - + // Preserve filterId if it exists in current URL if (currentFilterId) { queryParams.set('filterId', currentFilterId); } - + url = `${url}?${queryParams.toString()}`; } - + this.props.navigate(url); } );