Skip to content

Commit 8648e14

Browse files
committed
Update ObjectView.tsx
1 parent 0333909 commit 8648e14

1 file changed

Lines changed: 119 additions & 41 deletions

File tree

packages/studio/src/pages/ObjectView.tsx

Lines changed: 119 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,54 +13,130 @@ interface ObjectViewProps {
1313
}
1414

1515
export function ObjectView({ objectName }: ObjectViewProps) {
16-
const [rowData, setRowData] = useState<any[]>([]);
16+
// const [rowData, setRowData] = useState<any[]>([]); // Using Server Side
1717
const [columnDefs, setColumnDefs] = useState<ColDef[]>([]);
1818
const [loading, setLoading] = useState(false);
1919
const [activeTab, setActiveTab] = useState<'data' | 'schema'>('data');
2020
const [schemaFile, setSchemaFile] = useState<string | null>(null);
2121

22-
const fetchData = async () => {
22+
const [gridApi, setGridApi] = useState<any>(null);
23+
24+
// Initial load for columns
25+
useEffect(() => {
26+
const loadColumns = async () => {
27+
const metaRes = await fetch('/api/metadata');
28+
const meta = await metaRes.json();
29+
const objects = Array.isArray(meta) ? meta : meta.objects;
30+
const currentObj = objects.find((o: any) => o.name === objectName);
31+
32+
if (currentObj) {
33+
const cols: ColDef[] = [
34+
{ field: 'id', headerName: 'ID', width: 100, pinned: 'left', filter: 'agTextColumnFilter' }
35+
];
36+
37+
Object.entries(currentObj.fields).forEach(([key, field]: [string, any]) => {
38+
cols.push({
39+
field: key,
40+
headerName: field.label || key,
41+
flex: 1,
42+
filter: 'agTextColumnFilter', // Enforce text filter for simplicity in Infinite Model
43+
filterParams: {
44+
filterOptions: ['contains', 'equals'],
45+
suppressAndOrCondition: true
46+
}
47+
});
48+
});
49+
50+
setColumnDefs(cols);
51+
}
52+
};
53+
loadColumns();
54+
}, [objectName]);
55+
56+
const onGridReady = (params: any) => {
57+
setGridApi(params.api);
2358
setLoading(true);
24-
try {
25-
// 1. Fetch Schema to build columns
26-
// Ideally we should cache metadata, but fetching it here is safer for now
27-
const metaRes = await fetch('/api/metadata');
28-
const meta = await metaRes.json();
29-
const objects = Array.isArray(meta) ? meta : meta.objects;
30-
const currentObj = objects.find((o: any) => o.name === objectName);
31-
32-
if (currentObj) {
33-
const cols: ColDef[] = [
34-
{ field: 'id', headerName: 'ID', width: 100, pinned: 'left' }
35-
];
59+
60+
const datasource = {
61+
getRows: async (params: any) => {
62+
const { startRow, endRow, filterModel, sortModel } = params;
63+
setLoading(true);
3664

37-
Object.entries(currentObj.fields).forEach(([key, field]: [string, any]) => {
38-
cols.push({
39-
field: key,
40-
headerName: field.label || key,
41-
flex: 1,
42-
filter: true
65+
try {
66+
// 1. Convert Filters
67+
const filters: any[] = [];
68+
if (filterModel) {
69+
for (const key of Object.keys(filterModel)) {
70+
const model = filterModel[key];
71+
// agTextColumnFilter model: { filterType: 'text', type: 'contains', filter: 'value' }
72+
if (model.filterType === 'text') {
73+
if (model.type === 'contains') {
74+
filters.push([key, 'contains', model.filter]);
75+
} else if (model.type === 'equals') {
76+
filters.push([key, '=', model.filter]);
77+
}
78+
}
79+
}
80+
}
81+
82+
// 2. Convert Sort
83+
const sort = sortModel.map((s: any) => [s.colId, s.sort]);
84+
85+
// 3. Fetch Data
86+
const response = await fetch('/api/objectql', {
87+
method: 'POST',
88+
headers: { 'Content-Type': 'application/json' },
89+
body: JSON.stringify({
90+
op: 'find',
91+
object: objectName,
92+
args: {
93+
skip: startRow,
94+
limit: endRow - startRow,
95+
filters: filters.length > 0 ? filters : undefined,
96+
sort: sort.length > 0 ? sort : undefined
97+
}
98+
})
4399
});
44-
});
45-
46-
// Add system fields if not present
47-
if (!cols.find(c => c.field === 'createdAt')) cols.push({ field: 'createdAt', hide: true });
48-
if (!cols.find(c => c.field === 'updatedAt')) cols.push({ field: 'updatedAt', hide: true });
100+
const resJson = await response.json();
101+
const rows = resJson.data || resJson; // normalize
102+
103+
// 4. Fetch Count (for total pagination)
104+
// Optimization: Only fetch count if we don't know it or filter changed?
105+
// For Infinite Scroll simplicity, we fetch it.
106+
let lastRow = -1;
107+
if (rows.length < (endRow - startRow)) {
108+
lastRow = startRow + rows.length;
109+
} else {
110+
const countRes = await fetch('/api/objectql', {
111+
method: 'POST',
112+
headers: { 'Content-Type': 'application/json' },
113+
body: JSON.stringify({
114+
op: 'count',
115+
object: objectName,
116+
args: filters.length > 0 ? filters : {}
117+
})
118+
});
119+
const countJson = await countRes.json();
120+
lastRow = typeof countJson === 'number' ? countJson : countJson.data;
121+
}
49122

50-
setColumnDefs(cols);
123+
params.successCallback(rows, lastRow);
124+
125+
} catch (e) {
126+
console.error('Datasource getRows error:', e);
127+
params.failCallback();
128+
} finally {
129+
setLoading(false);
130+
}
51131
}
132+
};
52133

53-
// 2. Fetch Data
54-
const res = await fetch(`/api/data/${objectName}`);
55-
const result = await res.json();
56-
// Handle both array and { data: [] } formats
57-
const rows = Array.isArray(result) ? result : (result.data || []);
58-
setRowData(rows);
134+
params.api.setDatasource(datasource);
135+
};
59136

60-
} catch (e) {
61-
console.error(e);
62-
} finally {
63-
setLoading(false);
137+
const refreshData = () => {
138+
if (gridApi) {
139+
gridApi.refreshInfiniteCache();
64140
}
65141
};
66142

@@ -86,13 +162,12 @@ export function ObjectView({ objectName }: ObjectViewProps) {
86162
}, [objectName]);
87163

88164
useEffect(() => {
89-
if (activeTab === 'data') {
90-
fetchData();
91-
} else {
165+
if (activeTab === 'schema') {
92166
fetchSchemaFile();
93167
}
94168
}, [objectName, activeTab]);
95169

170+
96171
const defaultColDef = useMemo(() => {
97172
return {
98173
sortable: true,
@@ -138,7 +213,7 @@ export function ObjectView({ objectName }: ObjectViewProps) {
138213

139214
{activeTab === 'data' && (
140215
<div className="flex items-center space-x-2">
141-
<Button variant="outline" size="sm" onClick={fetchData} disabled={loading}>
216+
<Button variant="outline" size="sm" onClick={refreshData} disabled={loading}>
142217
<RefreshCw className={cn("mr-2 h-4 w-4", loading && "animate-spin")} />
143218
Refresh
144219
</Button>
@@ -156,12 +231,15 @@ export function ObjectView({ objectName }: ObjectViewProps) {
156231
<div className="rounded-md overflow-hidden bg-card h-full" style={{opacity: loading ? 0.6 : 1}}>
157232
<div className="ag-theme-quartz h-full w-full">
158233
<AgGridReact
159-
rowData={rowData}
234+
rowModelType="infinite"
235+
onGridReady={onGridReady}
160236
columnDefs={columnDefs}
161237
defaultColDef={defaultColDef}
162238
pagination={true}
163239
paginationPageSize={20}
240+
cacheBlockSize={20}
164241
rowSelection="multiple"
242+
maxBlocksInCache={10}
165243
/>
166244
</div>
167245
</div>

0 commit comments

Comments
 (0)