Skip to content

Commit e79f189

Browse files
Copilothotlong
andcommitted
fix: resolve view type switching - add list_views to CRM objects and fix console ObjectView to read listViews (camelCase)
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 62ade73 commit e79f189

4 files changed

Lines changed: 132 additions & 11 deletions

File tree

apps/console/objectstack.shared.ts

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,39 @@ if (crmApps.length > 0) {
2727
}
2828
}
2929

30+
// ---------------------------------------------------------------------------
31+
// Merge stack-level views into object definitions.
32+
// The @objectstack/spec defines views at the stack level (views[].listViews),
33+
// but the runtime protocol serves objects without listViews. This helper
34+
// merges listViews from the views array into the corresponding objects so
35+
// the console can render the correct view type when switching views.
36+
// ---------------------------------------------------------------------------
37+
function mergeViewsIntoObjects(objects: any[], configs: any[]): any[] {
38+
// Collect all listViews grouped by object name
39+
const viewsByObject: Record<string, Record<string, any>> = {};
40+
for (const config of configs) {
41+
if (!Array.isArray(config.views)) continue;
42+
for (const view of config.views) {
43+
if (!view.listViews) continue;
44+
for (const [key, listView] of Object.entries(view.listViews as Record<string, any>)) {
45+
const objectName = listView?.data?.object;
46+
if (!objectName) continue;
47+
if (!viewsByObject[objectName]) viewsByObject[objectName] = {};
48+
viewsByObject[objectName][key] = listView;
49+
}
50+
}
51+
}
52+
53+
// Merge into objects
54+
return objects.map((obj: any) => {
55+
const views = viewsByObject[obj.name];
56+
if (!views) return obj;
57+
return { ...obj, listViews: { ...(obj.listViews || {}), ...views } };
58+
});
59+
}
60+
61+
const allConfigs = [crmConfig, todoConfig, kitchenSinkConfig];
62+
3063
export const sharedConfig = {
3164
// ============================================================================
3265
// Project Metadata
@@ -39,13 +72,16 @@ export const sharedConfig = {
3972
// ============================================================================
4073
// Merged Stack Configuration (CRM + Todo + Kitchen Sink + HotCRM + Mock Metadata)
4174
// ============================================================================
42-
objects: mergeObjects(
43-
[
44-
...(crmConfig.objects || []),
45-
...(todoConfig.objects || []),
46-
...(kitchenSinkConfig.objects || []),
47-
],
48-
hotcrmObjects,
75+
objects: mergeViewsIntoObjects(
76+
mergeObjects(
77+
[
78+
...(crmConfig.objects || []),
79+
...(todoConfig.objects || []),
80+
...(kitchenSinkConfig.objects || []),
81+
],
82+
hotcrmObjects,
83+
),
84+
allConfigs,
4985
),
5086
apps: [
5187
...crmApps,

apps/console/src/components/ObjectView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
7272
);
7373
}
7474

75-
// Resolve Views from objectDef.list_views
75+
// Resolve Views from objectDef.listViews (camelCase per @objectstack/spec)
7676
const views = useMemo(() => {
77-
const definedViews = objectDef.list_views || {};
77+
const definedViews = objectDef.listViews || objectDef.list_views || {};
7878
const viewList = Object.entries(definedViews).map(([key, value]: [string, any]) => ({
7979
id: key,
8080
...value,

apps/console/src/components/ViewDesignerPage.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ export function ViewDesignerPage({ objects }: { objects: any[] }) {
4141

4242
// Resolve existing view for editing
4343
const existingView = useMemo(() => {
44-
if (!viewId || viewId === 'new' || !objectDef?.list_views) return null;
45-
return objectDef.list_views[viewId] || null;
44+
if (!viewId || viewId === 'new') return null;
45+
const views = objectDef?.listViews || objectDef?.list_views;
46+
if (!views) return null;
47+
return views[viewId] || null;
4648
}, [viewId, objectDef]);
4749

4850
const handleSave = useCallback(

examples/crm/objectstack.config.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,89 @@ export default defineStack({
2020
ProjectObject,
2121
EventObject
2222
],
23+
views: [
24+
{
25+
listViews: {
26+
all: {
27+
name: 'all',
28+
label: 'All Opportunities',
29+
type: 'grid',
30+
data: { provider: 'object', object: 'opportunity' },
31+
columns: ['name', 'amount', 'stage', 'close_date', 'probability'],
32+
},
33+
pipeline: {
34+
name: 'pipeline',
35+
label: 'Pipeline',
36+
type: 'kanban',
37+
data: { provider: 'object', object: 'opportunity' },
38+
columns: ['name', 'amount', 'close_date', 'probability'],
39+
kanban: {
40+
groupByField: 'stage',
41+
columns: ['name', 'amount', 'close_date'],
42+
},
43+
},
44+
},
45+
},
46+
{
47+
listViews: {
48+
all_events: {
49+
name: 'all_events',
50+
label: 'All Events',
51+
type: 'grid',
52+
data: { provider: 'object', object: 'event' },
53+
columns: ['subject', 'start', 'end', 'location', 'type'],
54+
},
55+
calendar: {
56+
name: 'calendar',
57+
label: 'Calendar',
58+
type: 'calendar',
59+
data: { provider: 'object', object: 'event' },
60+
columns: ['subject', 'start', 'end', 'type'],
61+
calendar: {
62+
startDateField: 'start',
63+
endDateField: 'end',
64+
titleField: 'subject',
65+
},
66+
},
67+
},
68+
},
69+
{
70+
listViews: {
71+
all_tasks: {
72+
name: 'all_tasks',
73+
label: 'All Tasks',
74+
type: 'grid',
75+
data: { provider: 'object', object: 'project_task' },
76+
columns: ['name', 'status', 'priority', 'start_date', 'end_date', 'progress'],
77+
},
78+
board: {
79+
name: 'board',
80+
label: 'Board',
81+
type: 'kanban',
82+
data: { provider: 'object', object: 'project_task' },
83+
columns: ['name', 'priority', 'start_date', 'end_date'],
84+
kanban: {
85+
groupByField: 'status',
86+
columns: ['name', 'priority', 'start_date', 'end_date'],
87+
},
88+
},
89+
gantt: {
90+
name: 'gantt',
91+
label: 'Gantt',
92+
type: 'gantt',
93+
data: { provider: 'object', object: 'project_task' },
94+
columns: ['name', 'start_date', 'end_date', 'progress', 'status'],
95+
},
96+
timeline: {
97+
name: 'timeline',
98+
label: 'Timeline',
99+
type: 'timeline',
100+
data: { provider: 'object', object: 'project_task' },
101+
columns: ['name', 'start_date', 'status'],
102+
},
103+
},
104+
},
105+
],
23106
reports: [],
24107
pages: [
25108
{

0 commit comments

Comments
 (0)