Skip to content

Commit e37590b

Browse files
committed
Refactor code structure for improved readability and maintainability
1 parent 112fa7c commit e37590b

3 files changed

Lines changed: 321 additions & 118 deletions

File tree

apps/console/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"@object-ui/example-todo": "workspace:*",
3131
"@object-ui/fields": "workspace:*",
3232
"@object-ui/layout": "workspace:*",
33+
"@object-ui/plugin-calendar": "^0.3.1",
3334
"@object-ui/plugin-dashboard": "workspace:*",
3435
"@object-ui/plugin-form": "workspace:*",
3536
"@object-ui/plugin-grid": "workspace:*",

apps/console/src/components/ObjectView.tsx

Lines changed: 148 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import { useState } from 'react';
1+
import { useState, useMemo } from 'react';
22
import { useParams, useSearchParams } from 'react-router-dom';
33
import { ObjectGrid } from '@object-ui/plugin-grid';
4-
import { Button } from '@object-ui/components';
5-
import { Plus } from 'lucide-react';
4+
import { ObjectKanban } from '@object-ui/plugin-kanban';
5+
import { ObjectCalendar } from '@object-ui/plugin-calendar';
6+
import { Button, Tabs, TabsList, TabsTrigger } from '@object-ui/components';
7+
import { Plus, LayoutTemplate, Calendar as CalendarIcon, Kanban as KanbanIcon, Table as TableIcon } from 'lucide-react';
68

79
export function ObjectView({ dataSource, objects, onEdit }: any) {
810
const { objectName } = useParams();
9-
const [searchParams] = useSearchParams();
10-
const viewName = searchParams.get('view');
11+
const [searchParams, setSearchParams] = useSearchParams();
1112
const [refreshKey, setRefreshKey] = useState(0);
13+
14+
// Get Object Definition
1215
const objectDef = objects.find((o: any) => o.name === objectName);
1316

1417
if (!objectDef) {
@@ -20,26 +23,152 @@ export function ObjectView({ dataSource, objects, onEdit }: any) {
2023
);
2124
}
2225

23-
// Generate columns from fields if not specified
24-
const normalizedFields = Array.isArray(objectDef.fields)
25-
? objectDef.fields
26-
: Object.entries(objectDef.fields || {}).map(([key, value]: [string, any]) => ({ name: key, ...value }));
26+
// Resolve Views
27+
const views = useMemo(() => {
28+
const definedViews = objectDef.list_views || {};
29+
const viewList = Object.entries(definedViews).map(([key, value]: [string, any]) => ({
30+
id: key,
31+
...value,
32+
type: value.type || 'grid'
33+
}));
34+
35+
// Ensure at least one default view exists
36+
if (viewList.length === 0) {
37+
viewList.push({
38+
id: 'all',
39+
label: 'All Records',
40+
type: 'grid',
41+
columns: objectDef.fields ? Object.keys(objectDef.fields).slice(0, 5) : []
42+
});
43+
}
44+
return viewList;
45+
}, [objectDef]);
2746

28-
const columns = normalizedFields.map((f: any) => ({
29-
field: f.name,
30-
label: f.label || f.name,
31-
width: 150
32-
})).slice(0, 8);
47+
// Active View State
48+
const activeViewId = searchParams.get('view') || views[0]?.id;
49+
const activeView = views.find((v: any) => v.id === activeViewId) || views[0];
50+
51+
// Helper: Normalize Columns for Grid
52+
const getGridColumns = (view: any) => {
53+
if (!view.columns) return [];
54+
return view.columns.map((colName: string) => {
55+
// Find field definition
56+
const fieldDef = Array.isArray(objectDef.fields)
57+
? objectDef.fields.find((f: any) => f.name === colName)
58+
: objectDef.fields?.[colName];
59+
60+
return {
61+
field: colName,
62+
label: fieldDef?.label || colName,
63+
width: 150
64+
};
65+
});
66+
};
67+
68+
const handleViewChange = (viewId: string) => {
69+
setSearchParams({ view: viewId });
70+
};
71+
72+
const renderCurrentView = () => {
73+
const commonProps = {
74+
key: `${objectName}-${activeView.id}-${refreshKey}`,
75+
dataSource,
76+
className: "h-full border-none"
77+
};
78+
79+
// Define onRowClick/Edit handlers
80+
const interactionProps = {
81+
onEdit,
82+
onRowClick: (record: any) => onEdit(record), // Default to edit on click
83+
};
84+
85+
switch (activeView.type) {
86+
case 'kanban':
87+
return (
88+
<ObjectKanban
89+
{...commonProps}
90+
schema={{
91+
type: 'kanban',
92+
objectName: objectDef.name,
93+
groupBy: activeView.groupBy || 'status',
94+
columns: activeView.columns,
95+
cardTitle: objectDef.titleField || 'name', // Default title field
96+
cardFields: activeView.columns
97+
}}
98+
{...interactionProps}
99+
/>
100+
);
101+
case 'calendar':
102+
return (
103+
<ObjectCalendar
104+
{...commonProps}
105+
schema={{
106+
type: 'calendar',
107+
objectName: objectDef.name,
108+
dateField: activeView.dateField || 'due_date',
109+
endField: activeView.endField,
110+
titleField: activeView.titleField || 'name',
111+
}}
112+
{...interactionProps}
113+
/>
114+
);
115+
case 'grid':
116+
default:
117+
return (
118+
<ObjectGrid
119+
{...commonProps}
120+
schema={{
121+
type: 'object-grid',
122+
objectName: objectDef.name,
123+
filterable: true,
124+
columns: getGridColumns(activeView),
125+
filter: activeView.filter,
126+
sort: activeView.sort
127+
}}
128+
{...interactionProps}
129+
onDelete={async (record: any) => {
130+
if (confirm(`Delete record?`)) {
131+
await dataSource.delete(objectName, record.id || record._id);
132+
setRefreshKey(k => k + 1);
133+
}
134+
}}
135+
/>
136+
);
137+
}
138+
};
33139

34140
return (
35141
<div className="h-full flex flex-col gap-4">
36142
{/* Header Section */}
37143
<div className="flex justify-between items-start">
38144
<div className="space-y-1">
39145
<h1 className="text-2xl font-bold tracking-tight text-slate-900">{objectDef.label}</h1>
40-
<p className="text-slate-500 text-sm">
41-
{viewName ? `View: ${viewName}` : (objectDef.description || 'Manage your records')}
42-
</p>
146+
<div className="flex items-center gap-2">
147+
{/* View Switcher Tabs */}
148+
{views.length > 1 && (
149+
<Tabs value={activeView.id} onValueChange={handleViewChange} className="h-8">
150+
<TabsList className="h-8 p-0 bg-transparent border-0 gap-2">
151+
{views.map((v: any) => (
152+
<TabsTrigger
153+
key={v.id}
154+
value={v.id}
155+
className="h-8 px-3 data-[state=active]:bg-muted data-[state=active]:shadow-none border border-transparent data-[state=active]:border-border rounded-md transition-all"
156+
>
157+
{v.type === 'kanban' && <KanbanIcon className="mr-2 h-3.5 w-3.5" />}
158+
{v.type === 'calendar' && <CalendarIcon className="mr-2 h-3.5 w-3.5" />}
159+
{v.type === 'grid' && <TableIcon className="mr-2 h-3.5 w-3.5" />}
160+
{v.label}
161+
</TabsTrigger>
162+
))}
163+
</TabsList>
164+
</Tabs>
165+
)}
166+
{views.length <= 1 && (
167+
<p className="text-slate-500 text-sm">
168+
{objectDef.description || 'Manage your records'}
169+
</p>
170+
)}
171+
</div>
43172
</div>
44173
<div className="flex gap-2">
45174
<Button onClick={() => onEdit(null)} className="shadow-none">
@@ -48,27 +177,10 @@ export function ObjectView({ dataSource, objects, onEdit }: any) {
48177
</div>
49178
</div>
50179

51-
{/* Grid Section */}
180+
{/* Content Area */}
52181
<div className="flex-1 rounded-xl border bg-card text-card-foreground shadow-sm overflow-hidden p-0 relative">
53182
<div className="absolute inset-0">
54-
<ObjectGrid
55-
key={`${objectName}-${refreshKey}`}
56-
schema={{
57-
type: 'object-grid',
58-
objectName: objectDef.name,
59-
filterable: true,
60-
columns: columns,
61-
}}
62-
dataSource={dataSource}
63-
onEdit={onEdit}
64-
onDelete={async (record: any) => {
65-
if (confirm(`Delete record?`)) {
66-
await dataSource.delete(objectName, record.id);
67-
setRefreshKey(k => k + 1);
68-
}
69-
}}
70-
className="h-full border-none"
71-
/>
183+
{renderCurrentView()}
72184
</div>
73185
</div>
74186
</div>

0 commit comments

Comments
 (0)