Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ function MetadataResourceEditPageImpl({
// `object`; other types fall back to their own `object`/`objectName`.
const sourceObjectName: string | undefined =
((draft as any)?.interfaceConfig?.source as string | undefined) ||
((draft as any)?.data?.object as string | undefined) ||
((draft as any)?.object as string | undefined) ||
((draft as any)?.objectName as string | undefined);
const [objectFields, setObjectFields] = React.useState<Array<{ name: string; label?: string; type?: string }>>([]);
Expand Down
36 changes: 25 additions & 11 deletions packages/app-shell/src/views/metadata-admin/widgets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1668,22 +1668,34 @@ function ActionMultiWidget({ id, value, onChange, readOnly, context }: WidgetPro

/* -------------------------------------------------------------------------- */
/* filter-builder — the SAME runtime FilterBuilder used by the list toolbar, */
/* reused in Studio for tab presets and the page base filter (unified UX). */
/* Stored format stays spec ViewFilterRule[] ({field,operator,value}); the */
/* builder's camelCase operators are mapped at the boundary so the runtime */
/* (specOperatorToAst) keeps working unchanged. */
/* reused in Studio for view/tab filters and the page base filter (unified UX).*/
/* Stored format stays spec ViewFilterRule[] ({field,operator,value}). Writes */
/* use the CANONICAL @objectstack/spec operator vocabulary (the enum enforced */
/* by ViewFilterRuleSchema); reads also accept legacy shorthand/camelCase */
/* spellings still present in already-stored view metadata (gt / eq / isNull). */
/* -------------------------------------------------------------------------- */
const FB_TO_SPEC: Record<string, string> = {
equals: 'equals', notEquals: 'not_equals', contains: 'contains', notContains: 'not_contains',
isEmpty: 'is_empty', isNotEmpty: 'is_not_empty', greaterThan: 'gt', lessThan: 'lt',
greaterOrEqual: 'gte', lessOrEqual: 'lte', before: 'lt', after: 'gt', between: 'between',
isEmpty: 'is_empty', isNotEmpty: 'is_not_empty',
greaterThan: 'greater_than', lessThan: 'less_than',
greaterOrEqual: 'greater_than_or_equal', lessOrEqual: 'less_than_or_equal',
before: 'before', after: 'after', between: 'between',
in: 'in', notIn: 'not_in',
};
/** Spec operator → FilterBuilder camelCase. Keys cover both the canonical
* vocabulary and legacy spellings (shorthand + snake/camel) so stored view
* metadata written before canonicalization still seeds the builder. */
const SPEC_TO_FB: Record<string, string> = {
equals: 'equals', eq: 'equals', not_equals: 'notEquals', ne: 'notEquals', neq: 'notEquals',
contains: 'contains', not_contains: 'notContains', is_empty: 'isEmpty', is_not_empty: 'isNotEmpty',
gt: 'greaterThan', greater_than: 'greaterThan', lt: 'lessThan', less_than: 'lessThan',
gte: 'greaterOrEqual', lte: 'lessOrEqual', in: 'in', not_in: 'notIn', nin: 'notIn',
equals: 'equals', eq: 'equals',
not_equals: 'notEquals', ne: 'notEquals', neq: 'notEquals', notEquals: 'notEquals',
contains: 'contains', not_contains: 'notContains', notContains: 'notContains',
is_empty: 'isEmpty', isEmpty: 'isEmpty', is_not_empty: 'isNotEmpty', isNotEmpty: 'isNotEmpty',
greater_than: 'greaterThan', gt: 'greaterThan', greaterThan: 'greaterThan',
less_than: 'lessThan', lt: 'lessThan', lessThan: 'lessThan',
greater_than_or_equal: 'greaterOrEqual', gte: 'greaterOrEqual', greaterOrEqual: 'greaterOrEqual',
less_than_or_equal: 'lessOrEqual', lte: 'lessOrEqual', lessOrEqual: 'lessOrEqual',
before: 'before', after: 'after', between: 'between',
in: 'in', not_in: 'notIn', nin: 'notIn', notIn: 'notIn',
};

interface FilterRuleLite { field: string; operator: string; value?: unknown }
Expand All @@ -1700,8 +1712,10 @@ function FilterBuilderField({ value, onChange, fields, readOnly }: {
logic: 'and' as const,
conditions: rules.map((r, i) => ({
id: `c${i}`,
field: r.field,
// Keep the raw operator verbatim if the builder has no camelCase
// equivalent, so it round-trips on save instead of being rewritten.
operator: SPEC_TO_FB[r.operator] ?? r.operator ?? 'equals',
field: r.field,
value: (r.value as any) ?? '',
})),
};
Expand Down
11 changes: 6 additions & 5 deletions packages/plugin-list/src/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,12 @@ function mapOperator(op: string) {
case 'equals': case 'eq': return '=';
case 'notEquals': case 'not_equals': case 'ne': case 'neq': return '!=';
case 'contains': return 'contains';
case 'notContains': case 'notcontains': return 'notcontains';
case 'greaterThan': case 'gt': return '>';
case 'greaterOrEqual': case 'gte': return '>=';
case 'lessThan': case 'lt': return '<';
case 'lessOrEqual': case 'lte': return '<=';
case 'notContains': case 'not_contains': case 'notcontains': return 'notcontains';
case 'startsWith': case 'starts_with': return 'startswith';
case 'greaterThan': case 'greater_than': case 'gt': return '>';
case 'greaterOrEqual': case 'greater_than_or_equal': case 'gte': return '>=';
case 'lessThan': case 'less_than': case 'lt': return '<';
case 'lessOrEqual': case 'less_than_or_equal': case 'lte': return '<=';
case 'in': return 'in';
case 'notIn': case 'not_in': case 'nin': return 'not in';
case 'before': return '<';
Expand Down
15 changes: 12 additions & 3 deletions packages/plugin-list/src/UserFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,22 @@ export interface UserFiltersProps {
onSelectionsChange?: (selections: Record<string, Array<string | number | boolean>>) => void;
}

/** Map @objectstack/spec ViewFilterRule operators to ObjectQL AST operators. */
/**
* Map @objectstack/spec ViewFilterRule operators to ObjectQL AST operators.
* Accepts the canonical vocabulary (`greater_than`, `not_equals`, `before`, …)
* that the Studio filter builder now writes, plus legacy shorthand spellings
* (`gt`, `eq`, `nin`) still present in already-stored view metadata.
*/
function specOperatorToAst(op: string | undefined): string {
switch (op) {
case undefined: case 'equals': case 'eq': return '=';
case 'not_equals': case 'ne': case 'neq': return '!=';
case 'gte': return '>='; case 'lte': return '<=';
case 'gt': return '>'; case 'lt': return '<';
case 'greater_than_or_equal': case 'gte': return '>=';
case 'less_than_or_equal': case 'lte': return '<=';
case 'greater_than': case 'gt': case 'after': return '>';
case 'less_than': case 'lt': case 'before': return '<';
case 'not_contains': return 'notcontains';
case 'starts_with': return 'startswith';
case 'not_in': case 'nin': return 'not in';
default: return op;
}
Expand Down
Loading