Skip to content

Commit a0c8250

Browse files
authored
Merge pull request #416 from objectstack-ai/copilot/complete-roadmap-development
2 parents 24fd4c7 + 9ca5da4 commit a0c8250

33 files changed

Lines changed: 3886 additions & 26 deletions

ROADMAP.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,10 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
143143

144144
#### 2.4 Enterprise-Grade Features (4 weeks)
145145

146-
- [ ] Advanced Grid (tree grid, grouping, Excel export)
147-
- [ ] Reporting Engine (visual designer, PDF export, scheduling)
148-
- [ ] Workflow Engine (visual designer, approval processes)
149-
- [ ] AI Integration (form filling, recommendations, NL queries)
146+
- [x] Advanced Grid (tree grid, grouping, Excel export)
147+
- [x] Reporting Engine (visual designer, PDF export, scheduling)
148+
- [x] Workflow Engine (visual designer, approval processes)
149+
- [x] AI Integration (form filling, recommendations, NL queries)
150150

151151
#### 2.5 Mobile Optimization (3 weeks)
152152

packages/plugin-aggrid/src/AgGridImpl.tsx

Lines changed: 95 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import React, { useMemo, useRef, useCallback } from 'react';
1010
import { AgGridReact } from 'ag-grid-react';
1111
import type { ColDef, GridOptions, GridReadyEvent, CellClickedEvent, RowClickedEvent, SelectionChangedEvent, CellValueChangedEvent, StatusPanelDef, GetContextMenuItemsParams, MenuItemDef } from 'ag-grid-community';
12-
import type { AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig } from './types';
12+
import type { AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig, TreeDataConfig, RowGroupingConfig, ExcelExportConfig } from './types';
1313

1414
export interface AgGridImplProps {
1515
rowData?: any[];
@@ -34,6 +34,9 @@ export interface AgGridImplProps {
3434
enableRangeSelection?: boolean;
3535
enableCharts?: boolean;
3636
contextMenu?: ContextMenuConfig;
37+
treeData?: TreeDataConfig;
38+
rowGrouping?: RowGroupingConfig;
39+
excelExport?: ExcelExportConfig;
3740
}
3841

3942
/**
@@ -63,6 +66,9 @@ export default function AgGridImpl({
6366
enableRangeSelection = false,
6467
enableCharts = false,
6568
contextMenu,
69+
treeData,
70+
rowGrouping,
71+
excelExport,
6672
}: AgGridImplProps) {
6773
const gridRef = useRef<any>(null);
6874

@@ -113,6 +119,31 @@ export default function AgGridImpl({
113119
}
114120
}, [exportConfig, callbacks, rowData]);
115121

122+
// Excel-compatible CSV Export handler
123+
// Exports CSV format which can be opened directly in Excel
124+
const handleExportExcel = useCallback(() => {
125+
if (!gridRef.current?.api) return;
126+
127+
const fileName = excelExport?.fileName || exportConfig?.fileName || 'export.csv';
128+
const includeHeaders = excelExport?.includeHeaders !== false;
129+
130+
const params = {
131+
fileName,
132+
skipColumnHeaders: !includeHeaders,
133+
allColumns: true,
134+
onlySelected: excelExport?.onlySelected || false,
135+
};
136+
137+
gridRef.current.api.exportDataAsCsv(params);
138+
139+
if (callbacks?.onExport) {
140+
const data = excelExport?.onlySelected
141+
? gridRef.current.api.getSelectedRows()
142+
: rowData;
143+
callbacks.onExport(data || [], 'excel');
144+
}
145+
}, [excelExport, exportConfig, callbacks, rowData]);
146+
116147
// Context Menu handler
117148
const getContextMenuItems = useCallback((params: GetContextMenuItemsParams): (string | MenuItemDef)[] => {
118149
if (!contextMenu?.enabled) return [];
@@ -127,6 +158,12 @@ export default function AgGridImpl({
127158
icon: '<span>📥</span>',
128159
action: () => handleExportCSV(),
129160
});
161+
} else if (item === 'export-excel') {
162+
items.push({
163+
name: 'Export Excel (CSV)',
164+
icon: '<span>📊</span>',
165+
action: () => handleExportExcel(),
166+
});
130167
} else if (item === 'autoSizeAll') {
131168
items.push({
132169
name: 'Auto-size All Columns',
@@ -170,7 +207,7 @@ export default function AgGridImpl({
170207
}
171208

172209
return items;
173-
}, [contextMenu, handleExportCSV, callbacks]);
210+
}, [contextMenu, handleExportCSV, handleExportExcel, callbacks]);
174211

175212
// Event handlers
176213
const handleCellClicked = useCallback((event: CellClickedEvent) => {
@@ -218,9 +255,20 @@ export default function AgGridImpl({
218255
}
219256
}
220257

258+
// Apply grouping
259+
if (rowGrouping?.enabled && rowGrouping.groupByFields?.includes(col.field || '')) {
260+
processed.rowGroup = true;
261+
processed.hide = true;
262+
}
263+
264+
// Apply aggregation
265+
if (rowGrouping?.aggregations && col.field && rowGrouping.aggregations[col.field]) {
266+
processed.aggFunc = rowGrouping.aggregations[col.field];
267+
}
268+
221269
return processed;
222270
});
223-
}, [columnDefs, editable, columnConfig]);
271+
}, [columnDefs, editable, columnConfig, rowGrouping]);
224272

225273
// Merge grid options with props
226274
const mergedGridOptions = useMemo(() => ({
@@ -237,6 +285,31 @@ export default function AgGridImpl({
237285
enableRangeSelection,
238286
enableCharts,
239287
getContextMenuItems: contextMenu?.enabled ? getContextMenuItems : undefined,
288+
// Tree data support
289+
...(treeData?.enabled ? {
290+
treeData: true,
291+
getDataPath: treeData.pathField
292+
? (data: any) => data[treeData.pathField as string]
293+
: undefined,
294+
autoGroupColumnDef: {
295+
headerName: 'Hierarchy',
296+
minWidth: 250,
297+
cellRendererParams: {
298+
suppressCount: false,
299+
},
300+
},
301+
groupDefaultExpanded: treeData.expandAll ? -1 : (treeData.expandDepth ?? 0),
302+
} : {}),
303+
// Row grouping
304+
...(rowGrouping?.enabled ? {
305+
groupDefaultExpanded: rowGrouping.groupByFields?.length ? 1 : 0,
306+
autoGroupColumnDef: {
307+
minWidth: 200,
308+
cellRendererParams: {
309+
suppressCount: !rowGrouping.showRowCount,
310+
},
311+
},
312+
} : {}),
240313
// Default options for better UX
241314
suppressCellFocus: !editable,
242315
enableCellTextSelection: true,
@@ -265,6 +338,8 @@ export default function AgGridImpl({
265338
enableCharts,
266339
contextMenu,
267340
getContextMenuItems,
341+
treeData,
342+
rowGrouping,
268343
editable,
269344
rowData.length,
270345
handleCellClicked,
@@ -294,14 +369,24 @@ export default function AgGridImpl({
294369

295370
return (
296371
<div className="ag-grid-container">
297-
{exportConfig?.enabled && (
372+
{(exportConfig?.enabled || excelExport?.enabled) && (
298373
<div className="mb-2 flex gap-2">
299-
<button
300-
onClick={handleExportCSV}
301-
className="px-3 py-1.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors"
302-
>
303-
Export CSV
304-
</button>
374+
{exportConfig?.enabled && (
375+
<button
376+
onClick={handleExportCSV}
377+
className="px-3 py-1.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors"
378+
>
379+
Export CSV
380+
</button>
381+
)}
382+
{excelExport?.enabled && (
383+
<button
384+
onClick={handleExportExcel}
385+
className="px-3 py-1.5 text-sm font-medium text-white bg-green-600 hover:bg-green-700 rounded-md transition-colors"
386+
>
387+
Export Excel (CSV)
388+
</button>
389+
)}
305390
</div>
306391
)}
307392
<div

packages/plugin-aggrid/src/index.tsx

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ import 'ag-grid-community/styles/ag-theme-balham.css';
1919
import 'ag-grid-community/styles/ag-theme-material.css';
2020

2121
// Export types for external use
22-
export type { AgGridSchema, SimpleColumnDef, AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig } from './types';
22+
export type { AgGridSchema, SimpleColumnDef, AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig, TreeDataConfig, RowGroupingConfig, ExcelExportConfig } from './types';
2323
export type { ObjectAgGridSchema } from './object-aggrid.types';
2424

25-
import type { AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig } from './types';
25+
import type { AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig, TreeDataConfig, RowGroupingConfig, ExcelExportConfig } from './types';
2626
import type { DataSource } from '@object-ui/types';
2727

2828
// 🚀 Lazy load the implementation file
@@ -56,6 +56,9 @@ export interface AgGridRendererProps {
5656
enableRangeSelection?: boolean;
5757
enableCharts?: boolean;
5858
contextMenu?: ContextMenuConfig;
59+
treeData?: TreeDataConfig;
60+
rowGrouping?: RowGroupingConfig;
61+
excelExport?: ExcelExportConfig;
5962
};
6063
}
6164

@@ -89,6 +92,9 @@ export const AgGridRenderer: React.FC<AgGridRendererProps> = ({ schema }) => {
8992
enableRangeSelection={schema.enableRangeSelection}
9093
enableCharts={schema.enableCharts}
9194
contextMenu={schema.contextMenu}
95+
treeData={schema.treeData}
96+
rowGrouping={schema.rowGrouping}
97+
excelExport={schema.excelExport}
9298
/>
9399
</Suspense>
94100
);
@@ -247,6 +253,27 @@ ComponentRegistry.register(
247253
description: 'Configure right-click menu: { enabled: true, items: ["copy", "export"] }',
248254
advanced: true
249255
},
256+
{
257+
name: 'treeData',
258+
type: 'code',
259+
label: 'Tree Data Config (JSON)',
260+
description: 'Configure tree data: { enabled: true, pathField: "orgHierarchy" }',
261+
advanced: true
262+
},
263+
{
264+
name: 'rowGrouping',
265+
type: 'code',
266+
label: 'Row Grouping Config (JSON)',
267+
description: 'Configure row grouping: { enabled: true, groupByFields: ["category"] }',
268+
advanced: true
269+
},
270+
{
271+
name: 'excelExport',
272+
type: 'code',
273+
label: 'Excel Export Config (JSON)',
274+
description: 'Configure Excel export: { enabled: true, fileName: "data.xlsx" }',
275+
advanced: true
276+
},
250277
{
251278
name: 'gridOptions',
252279
type: 'code',

packages/plugin-aggrid/src/types.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export interface AgGridCallbacks {
1616
onRowClicked?: (event: RowClickedEvent) => void;
1717
onSelectionChanged?: (event: SelectionChangedEvent) => void;
1818
onCellValueChanged?: (event: CellValueChangedEvent) => void;
19-
onExport?: (data: any[], format: 'csv') => void;
19+
onExport?: (data: any[], format: 'csv' | 'excel') => void;
2020
onContextMenuAction?: (action: string, rowData: any) => void;
2121
}
2222

@@ -29,6 +29,53 @@ export interface ExportConfig {
2929
skipColumnHeaders?: boolean;
3030
allColumns?: boolean;
3131
onlySelected?: boolean;
32+
format?: 'csv' | 'excel';
33+
}
34+
35+
/**
36+
* Tree data configuration
37+
*/
38+
export interface TreeDataConfig {
39+
enabled?: boolean;
40+
/** Field that contains the hierarchy path (array of strings) */
41+
pathField?: string;
42+
/** Field that contains parent ID for parent-child relationships */
43+
parentIdField?: string;
44+
/** Field that contains the unique ID */
45+
idField?: string;
46+
/** Whether to expand all rows by default */
47+
expandAll?: boolean;
48+
/** Depth to expand to by default */
49+
expandDepth?: number;
50+
}
51+
52+
/**
53+
* Row grouping configuration
54+
*/
55+
export interface RowGroupingConfig {
56+
enabled?: boolean;
57+
/** Fields to group by */
58+
groupByFields?: string[];
59+
/** Whether to show group row count */
60+
showRowCount?: boolean;
61+
/** Whether to allow user to change grouping */
62+
userGroupable?: boolean;
63+
/** Aggregation functions for grouped columns */
64+
aggregations?: Record<string, 'sum' | 'avg' | 'count' | 'min' | 'max' | 'first' | 'last'>;
65+
}
66+
67+
/**
68+
* Excel export configuration
69+
*/
70+
export interface ExcelExportConfig {
71+
enabled?: boolean;
72+
fileName?: string;
73+
sheetName?: string;
74+
includeHeaders?: boolean;
75+
includeGrouping?: boolean;
76+
onlySelected?: boolean;
77+
/** Custom column widths */
78+
columnWidths?: Record<string, number>;
3279
}
3380

3481
/**
@@ -105,6 +152,15 @@ export interface AgGridSchema {
105152
// Context menu
106153
contextMenu?: ContextMenuConfig;
107154

155+
// Tree data
156+
treeData?: TreeDataConfig;
157+
158+
// Row grouping
159+
rowGrouping?: RowGroupingConfig;
160+
161+
// Excel export
162+
excelExport?: ExcelExportConfig;
163+
108164
// Event callbacks
109165
callbacks?: AgGridCallbacks;
110166

packages/plugin-ai/package.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "@object-ui/plugin-ai",
3+
"version": "0.1.0",
4+
"type": "module",
5+
"main": "dist/index.umd.cjs",
6+
"module": "dist/index.js",
7+
"types": "dist/index.d.ts",
8+
"exports": {
9+
".": {
10+
"types": "./dist/index.d.ts",
11+
"import": "./dist/index.js",
12+
"require": "./dist/index.umd.cjs"
13+
}
14+
},
15+
"files": [
16+
"dist"
17+
],
18+
"scripts": {
19+
"build": "vite build",
20+
"clean": "rm -rf dist",
21+
"test": "vitest run",
22+
"lint": "eslint ."
23+
},
24+
"peerDependencies": {
25+
"react": "^18.0.0 || ^19.0.0",
26+
"react-dom": "^18.0.0 || ^19.0.0",
27+
"@object-ui/core": "workspace:*",
28+
"@object-ui/types": "workspace:*",
29+
"@object-ui/components": "workspace:*",
30+
"@object-ui/react": "workspace:*"
31+
},
32+
"dependencies": {
33+
"clsx": "^2.1.0",
34+
"lucide-react": "^0.344.0",
35+
"tailwind-merge": "^2.2.1"
36+
},
37+
"devDependencies": {
38+
"@types/node": "^20.11.24",
39+
"@types/react": "^19.2.13",
40+
"@types/react-dom": "^19.2.3",
41+
"@vitejs/plugin-react": "^5.1.3",
42+
"vite": "^7.3.1",
43+
"vite-plugin-dts": "^4.5.4",
44+
"vitest": "^4.0.18"
45+
}
46+
}

0 commit comments

Comments
 (0)