Skip to content

Commit 3ec060b

Browse files
Copilothotlong
andcommitted
Add aggregate diversity (avg/max), cross-object order widget, rendering tests + edge cases
- Add 2 new dashboard widgets: "Avg Deal Size by Stage" (line, avg), "Orders by Status" (bar, max, order object — cross-object) - Add i18n keys for new widgets across all 10 locales - Add 6 new DashboardRenderer rendering tests: area/donut/line/cross-object provider:'object' schema validation + empty widgets + null data edge cases - Update CRM metadata test for avg/max diversity + cross-object validation - Update ROADMAP entry with full scope Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 679350d commit 3ec060b

File tree

14 files changed

+249
-9
lines changed

14 files changed

+249
-9
lines changed

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ The `FlowDesigner` is a canvas-based flow editor that bridges the gap between th
863863
- [x] **P2: Dashboard Widget Spec Alignment** — Added `id`, `title`, `object`, `categoryField`, `valueField`, `aggregate` to all dashboard widgets across CRM, Todo, and Kitchen Sink examples (5 new spec-compliance tests)
864864
- [x] **P2: i18n (10 locales)** — Full CRM metadata translations for en, zh, ja, ko, de, fr, es, pt, ru, ar — objects, fields, fieldOptions, navigation, actions, views, formSections, dashboard, reports, pages (24 tests)
865865
- [x] **P2: Full Examples Metadata Audit** — Systematic spec compliance audit across all 4 examples: added `type: 'dashboard'` + `description` to todo/kitchen-sink dashboards, refactored msw-todo to use `ObjectSchema.create` + `Field.*` with snake_case field names, added explicit views to kitchen-sink and msw-todo, added missing `successMessage` on CRM opportunity action, 21 automated compliance tests
866-
- [x] **P2: CRM Dashboard Full provider:'object' Adaptation** — Converted all chart and table widgets in CRM dashboard from static `provider: 'value'` to dynamic `provider: 'object'` with aggregation configs. Revenue Trends (sum expected_revenue by stage), Lead Source (count by lead_source), Pipeline by Stage (sum amount by stage), Top Products (sum price by category), Recent Opportunities table (dynamic fetch), Revenue by Account (already dynamic). Fixed table `close_date` field alignment. Added 7 new provider:'object' validation tests covering aggregate config, field alignment, and function diversity.
866+
- [x] **P2: CRM Dashboard Full provider:'object' Adaptation** — Converted all chart and table widgets in CRM dashboard from static `provider: 'value'` to dynamic `provider: 'object'` with aggregation configs. 12 widgets total: 4 KPI metrics (static), 7 charts (sum/count/avg/max aggregation across opportunity, product, order objects), 1 table (dynamic fetch). Cross-object coverage (order), diverse aggregate functions (sum, count, avg, max). Fixed table `close_date` field alignment. Added i18n for 2 new widgets (10 locales). 9 new CRM metadata tests, 6 new DashboardRenderer rendering tests (area/donut/line/cross-object + edge cases). All provider:'object' paths covered.
867867

868868
### Ecosystem & Marketplace
869869
- Plugin marketplace website with search, ratings, and install count

examples/crm/src/__tests__/crm-metadata.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,22 @@ describe('CRM Metadata Spec Compliance', () => {
272272
}
273273
});
274274

275-
it('dashboard covers diverse aggregate functions (sum, count)', () => {
275+
it('dashboard covers diverse aggregate functions (sum, count, avg, max)', () => {
276276
const chartTypes = ['bar', 'area', 'donut', 'line', 'pie'];
277277
const charts = CrmDashboard.widgets.filter((w) => chartTypes.includes(w.type));
278278
const aggFns = new Set(charts.map((w) => (w.options as any)?.data?.aggregate?.function));
279279
expect(aggFns.has('sum')).toBe(true);
280280
expect(aggFns.has('count')).toBe(true);
281+
expect(aggFns.has('avg')).toBe(true);
282+
expect(aggFns.has('max')).toBe(true);
283+
});
284+
285+
it('dashboard includes cross-object widgets (order)', () => {
286+
const orderWidgets = CrmDashboard.widgets.filter((w) => w.object === 'order');
287+
expect(orderWidgets.length).toBeGreaterThanOrEqual(1);
288+
const data = (orderWidgets[0].options as any)?.data;
289+
expect(data.provider).toBe('object');
290+
expect(data.object).toBe('order');
281291
});
282292
});
283293

@@ -450,6 +460,8 @@ describe('CRM i18n Completeness', () => {
450460
expect(enLocale.dashboard.widgets.topProducts).toBeDefined();
451461
expect(enLocale.dashboard.widgets.recentOpportunities).toBeDefined();
452462
expect(enLocale.dashboard.widgets.revenueByAccount).toBeDefined();
463+
expect(enLocale.dashboard.widgets.avgDealSizeByStage).toBeDefined();
464+
expect(enLocale.dashboard.widgets.ordersByStatus).toBeDefined();
453465
});
454466
});
455467
});

examples/crm/src/dashboards/crm.dashboard.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,44 @@ export const CrmDashboard = {
167167
aggregate: { field: 'amount', function: 'sum' as const, groupBy: 'account' }
168168
}
169169
},
170+
},
171+
172+
// --- Row 6: Additional aggregate functions (avg, max) + cross-object ---
173+
{
174+
title: 'Avg Deal Size by Stage',
175+
type: 'line' as const,
176+
object: 'opportunity',
177+
categoryField: 'stage',
178+
valueField: 'amount',
179+
aggregate: 'avg',
180+
layout: { x: 0, y: 9, w: 2, h: 2 },
181+
options: {
182+
xField: 'stage',
183+
yField: 'amount',
184+
data: {
185+
provider: 'object' as const,
186+
object: 'opportunity',
187+
aggregate: { field: 'amount', function: 'avg' as const, groupBy: 'stage' }
188+
}
189+
},
190+
},
191+
{
192+
title: 'Orders by Status',
193+
type: 'bar' as const,
194+
object: 'order',
195+
categoryField: 'status',
196+
valueField: 'amount',
197+
aggregate: 'max',
198+
layout: { x: 2, y: 9, w: 2, h: 2 },
199+
options: {
200+
xField: 'status',
201+
yField: 'amount',
202+
data: {
203+
provider: 'object' as const,
204+
object: 'order',
205+
aggregate: { field: 'amount', function: 'max' as const, groupBy: 'status' }
206+
}
207+
},
170208
}
171209
]
172210
};

examples/crm/src/i18n/ar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ const ar = {
9696
},
9797
dashboard: {
9898
title: 'نظرة عامة على CRM',
99-
widgets: { totalRevenue: 'إجمالي الإيرادات', activeDeals: 'الصفقات النشطة', winRate: 'معدل الفوز', avgDealSize: 'متوسط حجم الصفقة', revenueTrends: 'اتجاهات الإيرادات', leadSource: 'مصدر العملاء المحتملين', pipelineByStage: 'خط الأنابيب حسب المرحلة', topProducts: 'أفضل المنتجات', recentOpportunities: 'الفرص الأخيرة', revenueByAccount: 'الإيرادات حسب الحساب' },
99+
widgets: { totalRevenue: 'إجمالي الإيرادات', activeDeals: 'الصفقات النشطة', winRate: 'معدل الفوز', avgDealSize: 'متوسط حجم الصفقة', revenueTrends: 'اتجاهات الإيرادات', leadSource: 'مصدر العملاء المحتملين', pipelineByStage: 'خط الأنابيب حسب المرحلة', topProducts: 'أفضل المنتجات', recentOpportunities: 'الفرص الأخيرة', revenueByAccount: 'الإيرادات حسب الحساب', avgDealSizeByStage: 'متوسط حجم الصفقة حسب المرحلة', ordersByStatus: 'الطلبات حسب الحالة' },
100100
trendLabel: 'مقارنة بالشهر الماضي',
101101
columns: { opportunityName: 'اسم الفرصة', amount: 'المبلغ', stage: 'المرحلة', closeDate: 'تاريخ الإغلاق' },
102102
},

examples/crm/src/i18n/de.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ const de = {
118118
},
119119
dashboard: {
120120
title: 'CRM-Übersicht',
121-
widgets: { totalRevenue: 'Gesamtumsatz', activeDeals: 'Aktive Geschäfte', winRate: 'Gewinnrate', avgDealSize: 'Ø Geschäftsgröße', revenueTrends: 'Umsatztrends', leadSource: 'Lead-Quelle', pipelineByStage: 'Pipeline nach Phase', topProducts: 'Top-Produkte', recentOpportunities: 'Aktuelle Chancen', revenueByAccount: 'Umsatz nach Konto' },
121+
widgets: { totalRevenue: 'Gesamtumsatz', activeDeals: 'Aktive Geschäfte', winRate: 'Gewinnrate', avgDealSize: 'Ø Geschäftsgröße', revenueTrends: 'Umsatztrends', leadSource: 'Lead-Quelle', pipelineByStage: 'Pipeline nach Phase', topProducts: 'Top-Produkte', recentOpportunities: 'Aktuelle Chancen', revenueByAccount: 'Umsatz nach Konto', avgDealSizeByStage: 'Ø Geschäftsgröße nach Phase', ordersByStatus: 'Bestellungen nach Status' },
122122
trendLabel: 'ggü. Vormonat',
123123
columns: { opportunityName: 'Chancenname', amount: 'Betrag', stage: 'Phase', closeDate: 'Abschlussdatum' },
124124
},

examples/crm/src/i18n/en.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ const en = {
381381
topProducts: 'Top Products',
382382
recentOpportunities: 'Recent Opportunities',
383383
revenueByAccount: 'Revenue by Account',
384+
avgDealSizeByStage: 'Avg Deal Size by Stage',
385+
ordersByStatus: 'Orders by Status',
384386
},
385387
trendLabel: 'vs last month',
386388
columns: {

examples/crm/src/i18n/es.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ const es = {
118118
},
119119
dashboard: {
120120
title: 'Resumen CRM',
121-
widgets: { totalRevenue: 'Ingresos totales', activeDeals: 'Negocios activos', winRate: 'Tasa de éxito', avgDealSize: 'Tamaño promedio', revenueTrends: 'Tendencias de ingresos', leadSource: 'Fuente de leads', pipelineByStage: 'Pipeline por etapa', topProducts: 'Productos principales', recentOpportunities: 'Oportunidades recientes', revenueByAccount: 'Ingresos por cuenta' },
121+
widgets: { totalRevenue: 'Ingresos totales', activeDeals: 'Negocios activos', winRate: 'Tasa de éxito', avgDealSize: 'Tamaño promedio', revenueTrends: 'Tendencias de ingresos', leadSource: 'Fuente de leads', pipelineByStage: 'Pipeline por etapa', topProducts: 'Productos principales', recentOpportunities: 'Oportunidades recientes', revenueByAccount: 'Ingresos por cuenta', avgDealSizeByStage: 'Tamaño promedio por etapa', ordersByStatus: 'Pedidos por estado' },
122122
trendLabel: 'vs mes anterior',
123123
columns: { opportunityName: 'Nombre de oportunidad', amount: 'Monto', stage: 'Etapa', closeDate: 'Fecha de cierre' },
124124
},

examples/crm/src/i18n/fr.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ const fr = {
118118
},
119119
dashboard: {
120120
title: 'Aperçu CRM',
121-
widgets: { totalRevenue: 'Chiffre d\'affaires total', activeDeals: 'Affaires actives', winRate: 'Taux de réussite', avgDealSize: 'Taille moyenne', revenueTrends: 'Tendances du CA', leadSource: 'Source des leads', pipelineByStage: 'Pipeline par étape', topProducts: 'Produits phares', recentOpportunities: 'Opportunités récentes', revenueByAccount: 'CA par compte' },
121+
widgets: { totalRevenue: 'Chiffre d\'affaires total', activeDeals: 'Affaires actives', winRate: 'Taux de réussite', avgDealSize: 'Taille moyenne', revenueTrends: 'Tendances du CA', leadSource: 'Source des leads', pipelineByStage: 'Pipeline par étape', topProducts: 'Produits phares', recentOpportunities: 'Opportunités récentes', revenueByAccount: 'CA par compte', avgDealSizeByStage: 'Taille moyenne par étape', ordersByStatus: 'Commandes par statut' },
122122
trendLabel: 'vs mois précédent',
123123
columns: { opportunityName: 'Nom de l\'opportunité', amount: 'Montant', stage: 'Étape', closeDate: 'Date de clôture' },
124124
},

examples/crm/src/i18n/ja.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ const ja = {
378378
topProducts: '人気商品',
379379
recentOpportunities: '最近の商談',
380380
revenueByAccount: '取引先別売上',
381+
avgDealSizeByStage: 'ステージ別平均取引規模',
382+
ordersByStatus: 'ステータス別注文',
381383
},
382384
trendLabel: '前月比',
383385
columns: {

examples/crm/src/i18n/ko.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ const ko = {
118118
},
119119
dashboard: {
120120
title: 'CRM 개요',
121-
widgets: { totalRevenue: '총 매출', activeDeals: '활성 거래', winRate: '수주율', avgDealSize: '평균 거래 규모', revenueTrends: '매출 추이', leadSource: '리드 소스', pipelineByStage: '단계별 파이프라인', topProducts: '인기 제품', recentOpportunities: '최근 영업기회', revenueByAccount: '거래처별 매출' },
121+
widgets: { totalRevenue: '총 매출', activeDeals: '활성 거래', winRate: '수주율', avgDealSize: '평균 거래 규모', revenueTrends: '매출 추이', leadSource: '리드 소스', pipelineByStage: '단계별 파이프라인', topProducts: '인기 제품', recentOpportunities: '최근 영업기회', revenueByAccount: '거래처별 매출', avgDealSizeByStage: '단계별 평균 거래 규모', ordersByStatus: '상태별 주문' },
122122
trendLabel: '전월 대비',
123123
columns: { opportunityName: '영업기회명', amount: '금액', stage: '단계', closeDate: '종료일' },
124124
},

0 commit comments

Comments
 (0)