diff --git a/content/docs/references/ui/analytics/Report.mdx b/content/docs/references/ui/analytics/Report.mdx index e16bac929..68954c597 100644 --- a/content/docs/references/ui/analytics/Report.mdx +++ b/content/docs/references/ui/analytics/Report.mdx @@ -15,6 +15,5 @@ description: Report Schema Reference | **columns** | `object[]` | āœ… | Columns to display | | **groupingsDown** | `object[]` | optional | Row groupings | | **groupingsAcross** | `object[]` | optional | Column groupings (Matrix only) | -| **filter** | `string` | optional | Filter logic (e.g. "1 AND (2 OR 3)") | -| **filterItems** | `object[]` | optional | Filter criteria lines | +| **filter** | `any` | optional | Filter criteria | | **chart** | `object` | optional | Embedded chart configuration | diff --git a/examples/crm/src/ui/dashboards.ts b/examples/crm/src/ui/dashboards.ts index 441fae9c4..78c594ccb 100644 --- a/examples/crm/src/ui/dashboards.ts +++ b/examples/crm/src/ui/dashboards.ts @@ -12,10 +12,9 @@ export const SalesDashboard: Dashboard = { title: 'Total Pipeline Value', type: 'metric', object: 'opportunity', - filter: [ - ['stage', '!=', 'closed_won'], - ['stage', '!=', 'closed_lost'], - ], + filter: { + stage: { $nin: ['closed_won', 'closed_lost'] } + }, valueField: 'amount', aggregate: 'sum', layout: { x: 0, y: 0, w: 3, h: 2 }, @@ -28,10 +27,10 @@ export const SalesDashboard: Dashboard = { title: 'Closed Won This Quarter', type: 'metric', object: 'opportunity', - filter: [ - ['stage', '=', 'closed_won'], - ['close_date', '>=', '{current_quarter_start}'], - ], + filter: { + stage: 'closed_won', + close_date: { $gte: '{current_quarter_start}' } + }, valueField: 'amount', aggregate: 'sum', layout: { x: 3, y: 0, w: 3, h: 2 }, @@ -44,10 +43,9 @@ export const SalesDashboard: Dashboard = { title: 'Open Opportunities', type: 'metric', object: 'opportunity', - filter: [ - ['stage', '!=', 'closed_won'], - ['stage', '!=', 'closed_lost'], - ], + filter: { + stage: { $nin: ['closed_won', 'closed_lost'] } + }, aggregate: 'count', layout: { x: 6, y: 0, w: 3, h: 2 }, options: { @@ -58,9 +56,9 @@ export const SalesDashboard: Dashboard = { title: 'Win Rate', type: 'metric', object: 'opportunity', - filter: [ - ['close_date', '>=', '{current_quarter_start}'], - ], + filter: { + close_date: { $gte: '{current_quarter_start}' } + }, valueField: 'stage', aggregate: 'count', layout: { x: 9, y: 0, w: 3, h: 2 }, @@ -75,10 +73,9 @@ export const SalesDashboard: Dashboard = { title: 'Pipeline by Stage', type: 'funnel', object: 'opportunity', - filter: [ - ['stage', '!=', 'closed_won'], - ['stage', '!=', 'closed_lost'], - ], + filter: { + stage: { $nin: ['closed_won', 'closed_lost'] } + }, categoryField: 'stage', valueField: 'amount', aggregate: 'sum', @@ -91,10 +88,9 @@ export const SalesDashboard: Dashboard = { title: 'Opportunities by Owner', type: 'bar', object: 'opportunity', - filter: [ - ['stage', '!=', 'closed_won'], - ['stage', '!=', 'closed_lost'], - ], + filter: { + stage: { $nin: ['closed_won', 'closed_lost'] } + }, categoryField: 'owner', valueField: 'amount', aggregate: 'sum', @@ -109,10 +105,10 @@ export const SalesDashboard: Dashboard = { title: 'Monthly Revenue Trend', type: 'line', object: 'opportunity', - filter: [ - ['stage', '=', 'closed_won'], - ['close_date', '>=', '{last_12_months}'], - ], + filter: { + stage: 'closed_won', + close_date: { $gte: '{last_12_months}' } + }, categoryField: 'close_date', valueField: 'amount', aggregate: 'sum', @@ -126,10 +122,9 @@ export const SalesDashboard: Dashboard = { title: 'Top Opportunities', type: 'table', object: 'opportunity', - filter: [ - ['stage', '!=', 'closed_won'], - ['stage', '!=', 'closed_lost'], - ], + filter: { + stage: { $nin: ['closed_won', 'closed_lost'] } + }, aggregate: 'count', layout: { x: 8, y: 6, w: 4, h: 4 }, options: { @@ -154,7 +149,7 @@ export const ServiceDashboard: Dashboard = { title: 'Open Cases', type: 'metric', object: 'case', - filter: [['is_closed', '=', false]], + filter: { is_closed: false }, aggregate: 'count', layout: { x: 0, y: 0, w: 3, h: 2 }, options: { @@ -165,10 +160,10 @@ export const ServiceDashboard: Dashboard = { title: 'Critical Cases', type: 'metric', object: 'case', - filter: [ - ['priority', '=', 'critical'], - ['is_closed', '=', false], - ], + filter: { + priority: 'critical', + is_closed: false + }, aggregate: 'count', layout: { x: 3, y: 0, w: 3, h: 2 }, options: { @@ -179,7 +174,7 @@ export const ServiceDashboard: Dashboard = { title: 'Avg Resolution Time (hrs)', type: 'metric', object: 'case', - filter: [['is_closed', '=', true]], + filter: { is_closed: true }, valueField: 'resolution_time_hours', aggregate: 'avg', layout: { x: 6, y: 0, w: 3, h: 2 }, @@ -192,7 +187,7 @@ export const ServiceDashboard: Dashboard = { title: 'SLA Violations', type: 'metric', object: 'case', - filter: [['is_sla_violated', '=', true]], + filter: { is_sla_violated: true }, aggregate: 'count', layout: { x: 9, y: 0, w: 3, h: 2 }, options: { @@ -205,7 +200,7 @@ export const ServiceDashboard: Dashboard = { title: 'Cases by Status', type: 'donut', object: 'case', - filter: [['is_closed', '=', false]], + filter: { is_closed: false }, categoryField: 'status', aggregate: 'count', layout: { x: 0, y: 2, w: 4, h: 4 }, @@ -217,7 +212,7 @@ export const ServiceDashboard: Dashboard = { title: 'Cases by Priority', type: 'pie', object: 'case', - filter: [['is_closed', '=', false]], + filter: { is_closed: false }, categoryField: 'priority', aggregate: 'count', layout: { x: 4, y: 2, w: 4, h: 4 }, @@ -239,9 +234,9 @@ export const ServiceDashboard: Dashboard = { title: 'Daily Case Volume', type: 'line', object: 'case', - filter: [ - ['created_date', '>=', '{last_30_days}'], - ], + filter: { + created_date: { $gte: '{last_30_days}' } + }, categoryField: 'created_date', aggregate: 'count', layout: { x: 0, y: 6, w: 8, h: 4 }, @@ -253,10 +248,10 @@ export const ServiceDashboard: Dashboard = { title: 'My Open Cases', type: 'table', object: 'case', - filter: [ - ['owner', '=', '{current_user}'], - ['is_closed', '=', false], - ], + filter: { + owner: '{current_user}', + is_closed: false + }, aggregate: 'count', layout: { x: 8, y: 6, w: 4, h: 4 }, options: { @@ -281,10 +276,10 @@ export const ExecutiveDashboard: Dashboard = { title: 'Total Revenue (YTD)', type: 'metric', object: 'opportunity', - filter: [ - ['stage', '=', 'closed_won'], - ['close_date', '>=', '{current_year_start}'], - ], + filter: { + stage: 'closed_won', + close_date: { $gte: '{current_year_start}' } + }, valueField: 'amount', aggregate: 'sum', layout: { x: 0, y: 0, w: 3, h: 2 }, @@ -297,7 +292,7 @@ export const ExecutiveDashboard: Dashboard = { title: 'Total Accounts', type: 'metric', object: 'account', - filter: [['is_active', '=', true]], + filter: { is_active: true }, aggregate: 'count', layout: { x: 3, y: 0, w: 3, h: 2 }, options: { @@ -318,7 +313,7 @@ export const ExecutiveDashboard: Dashboard = { title: 'Total Leads', type: 'metric', object: 'lead', - filter: [['is_converted', '=', false]], + filter: { is_converted: false }, aggregate: 'count', layout: { x: 9, y: 0, w: 3, h: 2 }, options: { @@ -331,10 +326,10 @@ export const ExecutiveDashboard: Dashboard = { title: 'Revenue by Industry', type: 'bar', object: 'opportunity', - filter: [ - ['stage', '=', 'closed_won'], - ['close_date', '>=', '{current_year_start}'], - ], + filter: { + stage: 'closed_won', + close_date: { $gte: '{current_year_start}' } + }, categoryField: 'account.industry', valueField: 'amount', aggregate: 'sum', @@ -344,10 +339,10 @@ export const ExecutiveDashboard: Dashboard = { title: 'Quarterly Revenue Trend', type: 'line', object: 'opportunity', - filter: [ - ['stage', '=', 'closed_won'], - ['close_date', '>=', '{last_4_quarters}'], - ], + filter: { + stage: 'closed_won', + close_date: { $gte: '{last_4_quarters}' } + }, categoryField: 'close_date', valueField: 'amount', aggregate: 'sum', @@ -362,9 +357,9 @@ export const ExecutiveDashboard: Dashboard = { title: 'New Accounts by Month', type: 'bar', object: 'account', - filter: [ - ['created_date', '>=', '{last_6_months}'], - ], + filter: { + created_date: { $gte: '{last_6_months}' } + }, categoryField: 'created_date', aggregate: 'count', layout: { x: 0, y: 6, w: 4, h: 4 }, diff --git a/examples/crm/src/ui/reports.ts b/examples/crm/src/ui/reports.ts index 65bad7dd0..b27957717 100644 --- a/examples/crm/src/ui/reports.ts +++ b/examples/crm/src/ui/reports.ts @@ -41,21 +41,10 @@ export const OpportunitiesByStageReport: Report = { } ], - filter: '1 AND 2', - filterItems: [ - { - id: 1, - field: 'stage', - operator: '!=', - value: 'closed_lost', - }, - { - id: 2, - field: 'close_date', - operator: '>=', - value: '{current_year_start}', - } - ], + filter: { + stage: { $ne: 'closed_lost' }, + close_date: { $gte: '{current_year_start}' } + }, chart: { type: 'bar', @@ -102,15 +91,9 @@ export const WonOpportunitiesByOwnerReport: Report = { } ], - filter: '1', - filterItems: [ - { - id: 1, - field: 'stage', - operator: '=', - value: 'closed_won', - } - ], + filter: { + stage: 'closed_won' + }, chart: { type: 'column', @@ -155,15 +138,9 @@ export const AccountsByIndustryTypeReport: Report = { } ], - filter: '1', - filterItems: [ - { - id: 1, - field: 'is_active', - operator: '=', - value: true, - } - ], + filter: { + is_active: true + }, }; // Support Report - Cases by Status and Priority @@ -252,15 +229,9 @@ export const SlaPerformanceReport: Report = { } ], - filter: '1', - filterItems: [ - { - id: 1, - field: 'is_closed', - operator: '=', - value: true, - } - ], + filter: { + is_closed: true + }, chart: { type: 'column', @@ -306,15 +277,9 @@ export const LeadsBySourceReport: Report = { } ], - filter: '1', - filterItems: [ - { - id: 1, - field: 'is_converted', - operator: '=', - value: false, - } - ], + filter: { + is_converted: false + }, chart: { type: 'pie', @@ -405,15 +370,9 @@ export const TasksByOwnerReport: Report = { } ], - filter: '1', - filterItems: [ - { - id: 1, - field: 'is_completed', - operator: '=', - value: false, - } - ], + filter: { + is_completed: false + }, }; export const CrmReports = { diff --git a/examples/todo/src/client-test.ts b/examples/todo/src/client-test.ts index e02cdb023..d47a8b4b0 100644 --- a/examples/todo/src/client-test.ts +++ b/examples/todo/src/client-test.ts @@ -55,11 +55,13 @@ async function main() { console.log('āœ… Deleted:', deleted); } - // 6. Advanced Query (AST) - console.log('\n🧠 Testing Advanced Query (Select & AST)...'); + // 6. Advanced Query (Modern Filter Syntax) + console.log('\n🧠 Testing Advanced Query (Select & Modern Filter)...'); const advancedResult = await client.data.find('todo_task', { select: ['subject', 'priority'], - filters: ['priority', '>=', 2], + filters: { + priority: { $gte: 2 } // Modern MongoDB-style filter syntax + }, sort: ['-priority'] }); console.log(`šŸŽ‰ Found ${advancedResult.count} high priority tasks:`); diff --git a/packages/spec/json-schema/Dashboard.json b/packages/spec/json-schema/Dashboard.json index 3f7376fd8..a0421fd29 100644 --- a/packages/spec/json-schema/Dashboard.json +++ b/packages/spec/json-schema/Dashboard.json @@ -46,6 +46,26 @@ "description": "Data source object name" }, "filter": { + "allOf": [ + { + "type": "object", + "additionalProperties": {} + }, + { + "type": "object", + "properties": { + "$and": { + "type": "array", + "items": {} + }, + "$or": { + "type": "array", + "items": {} + }, + "$not": {} + } + } + ], "description": "Data filter criteria" }, "categoryField": { diff --git a/packages/spec/json-schema/DashboardWidget.json b/packages/spec/json-schema/DashboardWidget.json index 3a9823f27..8f9288d24 100644 --- a/packages/spec/json-schema/DashboardWidget.json +++ b/packages/spec/json-schema/DashboardWidget.json @@ -28,6 +28,26 @@ "description": "Data source object name" }, "filter": { + "allOf": [ + { + "type": "object", + "additionalProperties": {} + }, + { + "type": "object", + "properties": { + "$and": { + "type": "array", + "items": {} + }, + "$or": { + "type": "array", + "items": {} + }, + "$not": {} + } + } + ], "description": "Data filter criteria" }, "categoryField": { diff --git a/packages/spec/json-schema/Report.json b/packages/spec/json-schema/Report.json index b877b54c2..24cd89c78 100644 --- a/packages/spec/json-schema/Report.json +++ b/packages/spec/json-schema/Report.json @@ -137,33 +137,27 @@ "description": "Column groupings (Matrix only)" }, "filter": { - "type": "string", - "description": "Filter logic (e.g. \"1 AND (2 OR 3)\")" - }, - "filterItems": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "number" - }, - "field": { - "type": "string" - }, - "operator": { - "type": "string" - }, - "value": {} + "allOf": [ + { + "type": "object", + "additionalProperties": {} }, - "required": [ - "id", - "field", - "operator" - ], - "additionalProperties": false - }, - "description": "Filter criteria lines" + { + "type": "object", + "properties": { + "$and": { + "type": "array", + "items": {} + }, + "$or": { + "type": "array", + "items": {} + }, + "$not": {} + } + } + ], + "description": "Filter criteria" }, "chart": { "type": "object", diff --git a/packages/spec/src/ui/dashboard.test.ts b/packages/spec/src/ui/dashboard.test.ts index 17ca2ae5b..35ff94103 100644 --- a/packages/spec/src/ui/dashboard.test.ts +++ b/packages/spec/src/ui/dashboard.test.ts @@ -97,7 +97,7 @@ describe('DashboardWidgetSchema', () => { title: 'Top Accounts', type: 'table', object: 'account', - filter: [{ field: 'annual_revenue', operator: '>', value: 1000000 }], + filter: { annual_revenue: { $gt: 1000000 } }, // Modern MongoDB-style filter layout: { x: 0, y: 6, w: 12, h: 4 }, }; @@ -109,7 +109,7 @@ describe('DashboardWidgetSchema', () => { title: 'Active Opportunities', type: 'metric', object: 'opportunity', - filter: { field: 'status', operator: 'equals', value: 'active' }, + filter: { status: 'active' }, aggregate: 'count', layout: { x: 0, y: 0, w: 3, h: 2 }, }; @@ -209,7 +209,7 @@ describe('DashboardSchema', () => { object: 'opportunity', valueField: 'amount', aggregate: 'sum', - filter: { field: 'is_closed', operator: 'equals', value: false }, + filter: { is_closed: false }, layout: { x: 0, y: 0, w: 3, h: 2 }, }, { @@ -217,7 +217,7 @@ describe('DashboardSchema', () => { type: 'metric', object: 'opportunity', aggregate: 'count', - filter: { field: 'is_closed', operator: 'equals', value: false }, + filter: { is_closed: false }, layout: { x: 3, y: 0, w: 3, h: 2 }, }, { @@ -236,7 +236,7 @@ describe('DashboardSchema', () => { object: 'opportunity', valueField: 'amount', aggregate: 'avg', - filter: { field: 'status', operator: 'equals', value: 'won' }, + filter: { status: 'won' }, layout: { x: 9, y: 0, w: 3, h: 2 }, }, { @@ -246,7 +246,7 @@ describe('DashboardSchema', () => { categoryField: 'stage', valueField: 'amount', aggregate: 'sum', - filter: { field: 'is_closed', operator: 'equals', value: false }, + filter: { is_closed: false }, layout: { x: 0, y: 2, w: 8, h: 4 }, options: { horizontal: true, @@ -268,7 +268,7 @@ describe('DashboardSchema', () => { categoryField: 'close_date', valueField: 'amount', aggregate: 'sum', - filter: { field: 'close_date', operator: 'last_n_months', value: 12 }, + filter: { close_date: '{last_12_months}' }, layout: { x: 0, y: 6, w: 12, h: 4 }, options: { smoothCurve: true, @@ -292,7 +292,7 @@ describe('DashboardSchema', () => { type: 'metric', object: 'case', aggregate: 'count', - filter: { field: 'status', operator: 'not_equals', value: 'closed' }, + filter: { status: { $ne: 'closed' } }, layout: { x: 0, y: 0, w: 3, h: 2 }, options: { color: '#FF6384', @@ -303,10 +303,10 @@ describe('DashboardSchema', () => { type: 'metric', object: 'case', aggregate: 'count', - filter: [ - { field: 'status', operator: 'equals', value: 'closed' }, - { field: 'closed_date', operator: 'today' }, - ], + filter: { // Modern MongoDB-style filter + status: 'closed', + closed_date: '{today}' + }, layout: { x: 3, y: 0, w: 3, h: 2 }, }, { @@ -326,7 +326,7 @@ describe('DashboardSchema', () => { object: 'case', valueField: 'satisfaction_rating', aggregate: 'avg', - filter: { field: 'satisfaction_rating', operator: 'not_null' }, + filter: { satisfaction_rating: { $null: false } }, layout: { x: 9, y: 0, w: 3, h: 2 }, options: { max: 5, @@ -339,7 +339,7 @@ describe('DashboardSchema', () => { object: 'case', categoryField: 'priority', aggregate: 'count', - filter: { field: 'status', operator: 'not_equals', value: 'closed' }, + filter: { status: { $ne: 'closed' } }, layout: { x: 0, y: 2, w: 6, h: 4 }, }, { @@ -354,7 +354,7 @@ describe('DashboardSchema', () => { title: 'Recent High Priority Cases', type: 'table', object: 'case', - filter: { field: 'priority', operator: 'equals', value: 'high' }, + filter: { priority: 'high' }, // Modern MongoDB-style filter layout: { x: 0, y: 6, w: 12, h: 4 }, options: { columns: ['case_number', 'subject', 'account', 'owner', 'created_date'], @@ -379,10 +379,10 @@ describe('DashboardSchema', () => { object: 'opportunity', valueField: 'amount', aggregate: 'sum', - filter: [ - { field: 'status', operator: 'equals', value: 'won' }, - { field: 'close_date', operator: 'this_quarter' }, - ], + filter: { // Modern MongoDB-style filter + status: 'won', + close_date: '{this_quarter}' + }, layout: { x: 0, y: 0, w: 4, h: 3 }, options: { prefix: '$', @@ -395,7 +395,7 @@ describe('DashboardSchema', () => { type: 'metric', object: 'account', aggregate: 'count', - filter: { field: 'created_date', operator: 'this_month' }, + filter: { created_date: '{this_month}' }, // Modern MongoDB-style filter layout: { x: 4, y: 0, w: 4, h: 3 }, }, { @@ -403,7 +403,7 @@ describe('DashboardSchema', () => { type: 'metric', object: 'user', aggregate: 'count', - filter: { field: 'is_active', operator: 'equals', value: true }, + filter: { is_active: true }, layout: { x: 8, y: 0, w: 4, h: 3 }, }, { @@ -413,7 +413,7 @@ describe('DashboardSchema', () => { categoryField: 'product_line', valueField: 'amount', aggregate: 'sum', - filter: { field: 'status', operator: 'equals', value: 'won' }, + filter: { status: 'won' }, layout: { x: 0, y: 3, w: 8, h: 4 }, }, { diff --git a/packages/spec/src/ui/dashboard.zod.ts b/packages/spec/src/ui/dashboard.zod.ts index 6b399491a..4689b7bef 100644 --- a/packages/spec/src/ui/dashboard.zod.ts +++ b/packages/spec/src/ui/dashboard.zod.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { FilterConditionSchema } from '../data/filter.zod'; /** * Chart Type Enum @@ -28,8 +29,8 @@ export const DashboardWidgetSchema = z.object({ /** Data Source Object */ object: z.string().optional().describe('Data source object name'), - /** Data Filter (ObjectQL JSON) */ - filter: z.any().optional().describe('Data filter criteria'), + /** Data Filter (MongoDB-style FilterCondition) */ + filter: FilterConditionSchema.optional().describe('Data filter criteria'), /** Category Field (X-Axis / Group By) */ categoryField: z.string().optional().describe('Field for grouping (X-Axis)'), diff --git a/packages/spec/src/ui/report.zod.ts b/packages/spec/src/ui/report.zod.ts index 44d601e6c..504f75bdb 100644 --- a/packages/spec/src/ui/report.zod.ts +++ b/packages/spec/src/ui/report.zod.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { FilterConditionSchema } from '../data/filter.zod'; /** * Report Type Enum @@ -62,14 +63,8 @@ export const ReportSchema = z.object({ groupingsDown: z.array(ReportGroupingSchema).optional().describe('Row groupings'), groupingsAcross: z.array(ReportGroupingSchema).optional().describe('Column groupings (Matrix only)'), - /** Filtering */ - filter: z.string().optional().describe('Filter logic (e.g. "1 AND (2 OR 3)")'), - filterItems: z.array(z.object({ - id: z.number(), - field: z.string(), - operator: z.string(), - value: z.any() - })).optional().describe('Filter criteria lines'), + /** Filtering (MongoDB-style FilterCondition) */ + filter: FilterConditionSchema.optional().describe('Filter criteria'), /** Visualization */ chart: ReportChartSchema.optional().describe('Embedded chart configuration'),