Skip to content

Commit 9623580

Browse files
authored
feat: Allow editing filter without loading data in data browser (#2949)
1 parent 58b8394 commit 9623580

File tree

5 files changed

+250
-33
lines changed

5 files changed

+250
-33
lines changed

src/components/BrowserFilter/BrowserFilter.react.js

Lines changed: 148 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,63 @@ export default class BrowserFilter extends React.Component {
4747
this.wrapRef = React.createRef();
4848
}
4949

50+
getClassNameFromURL() {
51+
const pathParts = window.location.pathname.split('/');
52+
const browserIndex = pathParts.indexOf('browser');
53+
return browserIndex >= 0 && pathParts[browserIndex + 1]
54+
? pathParts[browserIndex + 1]
55+
: this.props.className;
56+
}
57+
58+
initializeEditFilterMode() {
59+
const urlParams = new URLSearchParams(window.location.search);
60+
const isEditFilterMode = urlParams.get('editFilter') === 'true';
61+
62+
if (isEditFilterMode && !this.state.open) {
63+
const currentFilter = this.getCurrentFilterInfo();
64+
let filtersToDisplay = this.props.filters;
65+
if (this.props.filters.size === 0) {
66+
filtersToDisplay = this.loadFiltersFromURL();
67+
}
68+
69+
const filters = this.convertDatesForDisplay(filtersToDisplay);
70+
this.setState({
71+
open: true,
72+
showMore: true,
73+
filters: filters,
74+
editMode: true,
75+
name: currentFilter.name || '',
76+
originalFilterName: currentFilter.name || '',
77+
relativeDates: currentFilter.hasRelativeDates || false,
78+
originalRelativeDates: currentFilter.hasRelativeDates || false,
79+
originalFilters: filtersToDisplay,
80+
});
81+
}
82+
}
83+
5084
componentWillReceiveProps(props) {
5185
if (props.className !== this.props.className) {
5286
this.setState({ open: false });
5387
}
88+
89+
this.initializeEditFilterMode();
90+
}
91+
92+
componentDidMount() {
93+
this.initializeEditFilterMode();
5494
}
5595

5696
isCurrentFilterSaved() {
5797
// First check if there's a filterId in the URL (means we're definitely viewing a saved filter)
5898
const urlParams = new URLSearchParams(window.location.search);
5999
const filterId = urlParams.get('filterId');
60100

101+
const urlClassName = this.getClassNameFromURL();
102+
61103
if (filterId) {
62104
const preferences = ClassPreferences.getPreferences(
63105
this.context.applicationId,
64-
this.props.className
106+
urlClassName
65107
);
66108

67109
if (preferences.filters) {
@@ -77,36 +119,44 @@ export default class BrowserFilter extends React.Component {
77119

78120
// Check for legacy filters (filters parameter without filterId)
79121
const filtersParam = urlParams.get('filters');
80-
if (filtersParam && this.props.filters.size > 0) {
122+
if (filtersParam) {
81123
const preferences = ClassPreferences.getPreferences(
82124
this.context.applicationId,
83-
this.props.className
125+
urlClassName
84126
);
85127

86128
if (preferences.filters) {
87-
// Normalize current filters for comparison (remove class property if it matches current className)
88-
const currentFilters = this.props.filters.toJS().map(filter => {
129+
// Parse the URL filters parameter to get the actual filter data
130+
let urlFilters;
131+
try {
132+
urlFilters = JSON.parse(filtersParam);
133+
} catch {
134+
return false;
135+
}
136+
137+
// Normalize URL filters for comparison (remove class property if it matches current className)
138+
const normalizedUrlFilters = urlFilters.map(filter => {
89139
const normalizedFilter = { ...filter };
90-
if (normalizedFilter.class === this.props.className) {
140+
if (normalizedFilter.class === urlClassName) {
91141
delete normalizedFilter.class;
92142
}
93143
return normalizedFilter;
94144
});
95-
const currentFiltersString = JSON.stringify(currentFilters);
145+
const urlFiltersString = JSON.stringify(normalizedUrlFilters);
96146

97147
const matchingFilter = preferences.filters.find(savedFilter => {
98148
try {
99149
const savedFilters = JSON.parse(savedFilter.filter);
100150
// Normalize saved filters for comparison (remove class property if it matches current className)
101151
const normalizedSavedFilters = savedFilters.map(filter => {
102152
const normalizedFilter = { ...filter };
103-
if (normalizedFilter.class === this.props.className) {
153+
if (normalizedFilter.class === urlClassName) {
104154
delete normalizedFilter.class;
105155
}
106156
return normalizedFilter;
107157
});
108158
const savedFiltersString = JSON.stringify(normalizedSavedFilters);
109-
return savedFiltersString === currentFiltersString;
159+
return savedFiltersString === urlFiltersString;
110160
} catch {
111161
return false;
112162
}
@@ -117,16 +167,20 @@ export default class BrowserFilter extends React.Component {
117167
}
118168

119169
return false;
120-
} getCurrentFilterInfo() {
170+
}
171+
172+
getCurrentFilterInfo() {
121173
// Extract filterId from URL if present
122174
const urlParams = new URLSearchParams(window.location.search);
123175
const filterId = urlParams.get('filterId');
124176
const filtersParam = urlParams.get('filters');
125177

178+
const urlClassName = this.getClassNameFromURL();
179+
126180
if (filterId) {
127181
const preferences = ClassPreferences.getPreferences(
128182
this.context.applicationId,
129-
this.props.className
183+
urlClassName
130184
);
131185

132186
if (preferences.filters) {
@@ -156,36 +210,51 @@ export default class BrowserFilter extends React.Component {
156210
}
157211

158212
// Check for legacy filters (filters parameter without filterId)
159-
if (filtersParam && this.props.filters.size > 0) {
213+
if (filtersParam) {
160214
const preferences = ClassPreferences.getPreferences(
161215
this.context.applicationId,
162-
this.props.className
216+
urlClassName
163217
);
164218

165219
if (preferences.filters) {
166-
// Normalize current filters for comparison (remove class property if it matches current className)
167-
const currentFilters = this.props.filters.toJS().map(filter => {
220+
// Parse the URL filters parameter to get the actual filter data
221+
let urlFilters;
222+
try {
223+
urlFilters = JSON.parse(filtersParam);
224+
} catch (error) {
225+
console.warn('Failed to parse URL filters:', error);
226+
return {
227+
id: null,
228+
name: '',
229+
isApplied: false,
230+
hasRelativeDates: false,
231+
isLegacy: false
232+
};
233+
}
234+
235+
// Normalize URL filters for comparison (remove class property if it matches current className)
236+
const normalizedUrlFilters = urlFilters.map(filter => {
168237
const normalizedFilter = { ...filter };
169-
if (normalizedFilter.class === this.props.className) {
238+
if (normalizedFilter.class === urlClassName) {
170239
delete normalizedFilter.class;
171240
}
172241
return normalizedFilter;
173242
});
174-
const currentFiltersString = JSON.stringify(currentFilters);
243+
const urlFiltersString = JSON.stringify(normalizedUrlFilters);
175244

176245
const matchingFilter = preferences.filters.find(savedFilter => {
177246
try {
178247
const savedFilters = JSON.parse(savedFilter.filter);
179248
// Normalize saved filters for comparison (remove class property if it matches current className)
180249
const normalizedSavedFilters = savedFilters.map(filter => {
181250
const normalizedFilter = { ...filter };
182-
if (normalizedFilter.class === this.props.className) {
251+
if (normalizedFilter.class === urlClassName) {
183252
delete normalizedFilter.class;
184253
}
185254
return normalizedFilter;
186255
});
187256
const savedFiltersString = JSON.stringify(normalizedSavedFilters);
188-
return savedFiltersString === currentFiltersString;
257+
return savedFiltersString === urlFiltersString;
189258
} catch {
190259
return false;
191260
}
@@ -224,6 +293,52 @@ export default class BrowserFilter extends React.Component {
224293
};
225294
}
226295

296+
loadFiltersFromURL() {
297+
const urlParams = new URLSearchParams(window.location.search);
298+
const filtersParam = urlParams.get('filters');
299+
const filterId = urlParams.get('filterId');
300+
301+
const urlClassName = this.getClassNameFromURL();
302+
303+
// If we have a filterId, load from saved filters
304+
if (filterId) {
305+
const preferences = ClassPreferences.getPreferences(
306+
this.context.applicationId,
307+
urlClassName
308+
);
309+
310+
if (preferences.filters) {
311+
const savedFilter = preferences.filters.find(filter => filter.id === filterId);
312+
if (savedFilter) {
313+
try {
314+
const filterData = JSON.parse(savedFilter.filter);
315+
return new List(filterData.map(filter => {
316+
const processedFilter = { ...filter, class: filter.class || urlClassName };
317+
return new ImmutableMap(processedFilter);
318+
}));
319+
} catch (error) {
320+
console.warn('Failed to parse saved filter:', error);
321+
}
322+
}
323+
}
324+
}
325+
326+
// If we have filters in URL but no filterId, parse them directly
327+
if (filtersParam) {
328+
try {
329+
const queryFilters = JSON.parse(filtersParam);
330+
return new List(queryFilters.map(filter => {
331+
const processedFilter = { ...filter, class: filter.class || urlClassName };
332+
return new ImmutableMap(processedFilter);
333+
}));
334+
} catch (error) {
335+
console.warn('Failed to parse URL filters:', error);
336+
}
337+
}
338+
339+
return new List();
340+
}
341+
227342
toggleMore() {
228343
const currentFilter = this.getCurrentFilterInfo();
229344

@@ -261,9 +376,11 @@ export default class BrowserFilter extends React.Component {
261376
}
262377

263378
isFilterNameExists(name) {
379+
const urlClassName = this.getClassNameFromURL();
380+
264381
const preferences = ClassPreferences.getPreferences(
265382
this.context.applicationId,
266-
this.props.className
383+
urlClassName
267384
);
268385

269386
if (preferences.filters && name) {
@@ -496,6 +613,17 @@ export default class BrowserFilter extends React.Component {
496613
// Convert only Parse Date objects to JavaScript Date objects, preserve RelativeDate objects
497614
filters = this.convertDatesForDisplay(filters);
498615
}
616+
617+
// If closing the dialog and we're in edit filter mode, remove the editFilter parameter
618+
const urlParams = new URLSearchParams(window.location.search);
619+
const isEditFilterMode = urlParams.get('editFilter') === 'true';
620+
621+
if (this.state.open && isEditFilterMode) {
622+
urlParams.delete('editFilter');
623+
const newUrl = `${window.location.pathname}${urlParams.toString() ? '?' + urlParams.toString() : ''}`;
624+
window.history.replaceState({}, '', newUrl);
625+
}
626+
499627
this.setState(prevState => ({
500628
open: !prevState.open,
501629
filters: filters,

src/components/BrowserFilter/FilterRow.react.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ const FilterRow = ({
118118
}) => {
119119
const setFocus = useCallback(input => {
120120
if (input !== null && editMode) {
121+
// For DateTimeEntry components, don't auto-focus as it opens the calendar
122+
// Check if the input has a focus method that opens a popover/calendar
123+
if (input.focus && input.open) {
124+
// This is likely a DateTimeEntry component, skip auto-focus
125+
return;
126+
}
121127
input.focus();
122128
}
123129
}, []);

src/components/CategoryList/CategoryList.react.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,13 @@ export default class CategoryList extends React.Component {
140140
return (
141141
<div key={id}>
142142
<div className={styles.link}>
143-
<Link title={c.name} to={{ pathname: link }} className={className} key={id} onClick={() => this.props.classClicked()}>
144-
<span>{count}</span>
145-
<span>{c.name}</span>
143+
<Link
144+
title={c.name}
145+
to={{ pathname: link }}
146+
className={className}
147+
onClick={() => this.props.classClicked()}
148+
>
149+
{c.name}
146150
</Link>
147151
{c.onEdit && (
148152
<a
@@ -155,6 +159,7 @@ export default class CategoryList extends React.Component {
155159
<Icon name="edit-solid" width={14} height={14} />
156160
</a>
157161
)}
162+
<span className={styles.count}>{count}</span>
158163
{(c.filters || []).length !== 0 && (
159164
<a
160165
className={styles.expand}
@@ -185,6 +190,17 @@ export default class CategoryList extends React.Component {
185190
>
186191
<span>{name}</span>
187192
</Link>
193+
{this.props.onEditFilter && (
194+
<a
195+
className={styles.editFilter}
196+
onClick={e => {
197+
e.preventDefault();
198+
this.props.onEditFilter(c.name, filterData);
199+
}}
200+
>
201+
<Icon name="edit-solid" width={14} height={14} />
202+
</a>
203+
)}
188204
</div>
189205
);
190206
})}
@@ -202,4 +218,5 @@ CategoryList.propTypes = {
202218
),
203219
current: PropTypes.string.describe('Id of current category to be highlighted.'),
204220
linkPrefix: PropTypes.string.describe('Link prefix used to generate link path.'),
221+
onEditFilter: PropTypes.func.describe('Callback function for editing a filter.'),
205222
};

0 commit comments

Comments
 (0)