-
Notifications
You must be signed in to change notification settings - Fork 2
fix(ListView): normalize filter AST, safe export, request debounce, data limit warning #659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -53,6 +53,49 @@ function mapOperator(op: string) { | |||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Normalize a single filter condition: convert `in`/`not in` operators | ||||||||||||||||||
| * into backend-compatible `or`/`and` of equality conditions. | ||||||||||||||||||
| * E.g., ['status', 'in', ['a','b']] → ['or', ['status','=','a'], ['status','=','b']] | ||||||||||||||||||
| */ | ||||||||||||||||||
| export function normalizeFilterCondition(condition: any[]): any[] { | ||||||||||||||||||
| if (!Array.isArray(condition) || condition.length < 3) return condition; | ||||||||||||||||||
|
|
||||||||||||||||||
| const [field, op, value] = condition; | ||||||||||||||||||
|
|
||||||||||||||||||
| // Recurse into logical groups | ||||||||||||||||||
| if (typeof field === 'string' && (field === 'and' || field === 'or')) { | ||||||||||||||||||
| return [field, ...condition.slice(1).map((c: any) => | ||||||||||||||||||
| Array.isArray(c) ? normalizeFilterCondition(c) : c | ||||||||||||||||||
| )]; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (op === 'in' && Array.isArray(value)) { | ||||||||||||||||||
| if (value.length === 0) return []; | ||||||||||||||||||
| if (value.length === 1) return [field, '=', value[0]]; | ||||||||||||||||||
| return ['or', ...value.map((v: any) => [field, '=', v])]; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (op === 'not in' && Array.isArray(value)) { | ||||||||||||||||||
| if (value.length === 0) return []; | ||||||||||||||||||
| if (value.length === 1) return [field, '!=', value[0]]; | ||||||||||||||||||
| return ['and', ...value.map((v: any) => [field, '!=', v])]; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return condition; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Normalize an array of filter conditions, expanding `in`/`not in` operators | ||||||||||||||||||
| * and ensuring consistent AST structure. | ||||||||||||||||||
| */ | ||||||||||||||||||
| export function normalizeFilters(filters: any[]): any[] { | ||||||||||||||||||
| if (!Array.isArray(filters) || filters.length === 0) return []; | ||||||||||||||||||
| return filters | ||||||||||||||||||
| .map(f => Array.isArray(f) ? normalizeFilterCondition(f) : f) | ||||||||||||||||||
| .filter(f => Array.isArray(f) && f.length > 0); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| function convertFilterGroupToAST(group: FilterGroup): any[] { | ||||||||||||||||||
| if (!group || !group.conditions || group.conditions.length === 0) return []; | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -62,9 +105,12 @@ function convertFilterGroupToAST(group: FilterGroup): any[] { | |||||||||||||||||
| return [c.field, mapOperator(c.operator), c.value]; | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (conditions.length === 1) return conditions[0]; | ||||||||||||||||||
| // Normalize in/not-in conditions for backend compatibility | ||||||||||||||||||
| const normalized = normalizeFilters(conditions); | ||||||||||||||||||
| if (normalized.length === 0) return []; | ||||||||||||||||||
| if (normalized.length === 1) return normalized[0]; | ||||||||||||||||||
|
|
||||||||||||||||||
| return [group.logic, ...conditions]; | ||||||||||||||||||
| return [group.logic, ...normalized]; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
|
|
@@ -132,6 +178,17 @@ export function evaluateConditionalFormatting( | |||||||||||||||||
| const LIST_DEFAULT_TRANSLATIONS: Record<string, string> = { | ||||||||||||||||||
| 'list.recordCount': '{{count}} records', | ||||||||||||||||||
| 'list.recordCountOne': '{{count}} record', | ||||||||||||||||||
| 'list.noItems': 'No items found', | ||||||||||||||||||
| 'list.noItemsMessage': 'There are no records to display. Try adjusting your filters or adding new data.', | ||||||||||||||||||
| 'list.search': 'Search', | ||||||||||||||||||
| 'list.filter': 'Filter', | ||||||||||||||||||
| 'list.sort': 'Sort', | ||||||||||||||||||
| 'list.export': 'Export', | ||||||||||||||||||
| 'list.hideFields': 'Hide fields', | ||||||||||||||||||
| 'list.showAll': 'Show all', | ||||||||||||||||||
| 'list.pullToRefresh': 'Pull to refresh', | ||||||||||||||||||
| 'list.refreshing': 'Refreshing…', | ||||||||||||||||||
|
Comment on lines
+183
to
+190
|
||||||||||||||||||
| 'list.search': 'Search', | |
| 'list.filter': 'Filter', | |
| 'list.sort': 'Sort', | |
| 'list.export': 'Export', | |
| 'list.hideFields': 'Hide fields', | |
| 'list.showAll': 'Show all', | |
| 'list.pullToRefresh': 'Pull to refresh', | |
| 'list.refreshing': 'Refreshing…', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The filter normalization functions use
any[]for the condition parameter and return type. While this is pragmatic for heterogeneous filter AST structures, consider creating a type alias to make the intent clearer and improve maintainability:This would make the function signatures more self-documenting and allow for gradual type refinement in the future without breaking changes to the API.