Skip to content

Commit b10d608

Browse files
Improve widget styling with cleaner Power Apps-inspired layout
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/32d509ca-9783-48ab-a16a-0182b122bfc7 Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
1 parent 6825fc9 commit b10d608

File tree

1 file changed

+163
-120
lines changed

1 file changed

+163
-120
lines changed

apps/console/src/components/schema/objectDetailWidgets.tsx

Lines changed: 163 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import { useMemo } from 'react';
1515
import { Badge } from '@object-ui/components';
1616
import {
17-
Settings2,
1817
Link2,
1918
KeyRound,
2019
LayoutList,
@@ -75,52 +74,63 @@ export function ObjectPropertiesWidget({ schema }: { schema: ObjectWidgetSchema
7574
if (!object) return null;
7675

7776
return (
78-
<div className="rounded-lg border bg-card p-4 sm:p-6 space-y-4" data-testid="object-properties">
79-
<h2 className="text-sm font-semibold flex items-center gap-2">
80-
<Settings2 className="h-4 w-4" />
81-
Object Properties
82-
</h2>
83-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 text-sm">
84-
<div>
85-
<span className="text-muted-foreground">API Name</span>
86-
<p className="font-mono text-xs mt-0.5">{object.name}</p>
87-
</div>
88-
<div>
89-
<span className="text-muted-foreground">Label</span>
90-
<p className="mt-0.5">{object.label}</p>
91-
</div>
92-
{object.pluralLabel && (
93-
<div>
94-
<span className="text-muted-foreground">Plural Label</span>
95-
<p className="mt-0.5">{object.pluralLabel}</p>
77+
<div className="space-y-6" data-testid="object-properties">
78+
{/* Basic Information Section */}
79+
<div>
80+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">
81+
Basic Information
82+
</h3>
83+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
84+
<div className="space-y-1.5">
85+
<label className="text-xs font-medium text-muted-foreground">Display Name</label>
86+
<p className="text-sm font-medium">{object.label}</p>
9687
</div>
97-
)}
98-
{object.group && (
99-
<div>
100-
<span className="text-muted-foreground">Group</span>
101-
<p className="mt-0.5">{object.group}</p>
88+
<div className="space-y-1.5">
89+
<label className="text-xs font-medium text-muted-foreground">API Name</label>
90+
<p className="text-sm font-mono bg-muted/50 rounded px-2 py-1">{object.name}</p>
10291
</div>
103-
)}
104-
<div>
105-
<span className="text-muted-foreground">Status</span>
106-
<p className="mt-0.5">
107-
<Badge variant={object.enabled !== false ? 'default' : 'secondary'}>
108-
{object.enabled !== false ? 'Enabled' : 'Disabled'}
109-
</Badge>
110-
</p>
111-
</div>
112-
<div className="flex items-center gap-2">
113-
<span className="text-muted-foreground">Fields</span>
114-
<Badge variant="outline">{object.fieldCount ?? fields.length}</Badge>
92+
{object.pluralLabel && (
93+
<div className="space-y-1.5">
94+
<label className="text-xs font-medium text-muted-foreground">Plural Label</label>
95+
<p className="text-sm font-medium">{object.pluralLabel}</p>
96+
</div>
97+
)}
98+
{object.group && (
99+
<div className="space-y-1.5">
100+
<label className="text-xs font-medium text-muted-foreground">Group</label>
101+
<p className="text-sm font-medium">{object.group}</p>
102+
</div>
103+
)}
115104
</div>
116-
{object.isSystem && (
117-
<div>
118-
<span className="text-muted-foreground">Type</span>
119-
<p className="mt-0.5">
120-
<Badge variant="secondary">System Object</Badge>
121-
</p>
105+
</div>
106+
107+
{/* Configuration Section */}
108+
<div>
109+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">
110+
Configuration
111+
</h3>
112+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
113+
<div className="space-y-1.5">
114+
<label className="text-xs font-medium text-muted-foreground">Status</label>
115+
<div>
116+
<Badge variant={object.enabled !== false ? 'default' : 'secondary'} className="font-normal">
117+
{object.enabled !== false ? 'Enabled' : 'Disabled'}
118+
</Badge>
119+
</div>
122120
</div>
123-
)}
121+
<div className="space-y-1.5">
122+
<label className="text-xs font-medium text-muted-foreground">Field Count</label>
123+
<p className="text-sm font-medium">{object.fieldCount ?? fields.length} fields</p>
124+
</div>
125+
{object.isSystem && (
126+
<div className="space-y-1.5">
127+
<label className="text-xs font-medium text-muted-foreground">Type</label>
128+
<div>
129+
<Badge variant="secondary" className="font-normal">System Object</Badge>
130+
</div>
131+
</div>
132+
)}
133+
</div>
124134
</div>
125135
</div>
126136
);
@@ -137,34 +147,47 @@ export function ObjectRelationshipsWidget({ schema }: { schema: ObjectWidgetSche
137147

138148
if (!object) return null;
139149

150+
const hasRelationships = object.relationships && object.relationships.length > 0;
151+
140152
return (
141-
<div className="rounded-lg border bg-card p-4 sm:p-6 space-y-4" data-testid="relationships-section">
142-
<h2 className="text-sm font-semibold flex items-center gap-2">
143-
<Link2 className="h-4 w-4" />
144-
Relationships
145-
</h2>
146-
{object.relationships && object.relationships.length > 0 ? (
147-
<div className="space-y-2">
148-
{object.relationships.map((rel, i) => (
149-
<div key={i} className="flex items-center gap-3 p-2 rounded-md bg-muted/40">
150-
<Badge variant="outline" className="text-xs shrink-0">
151-
{rel.type}
152-
</Badge>
153-
<div className="min-w-0 flex-1 text-sm">
154-
<span className="font-medium">{rel.label || rel.relatedObject}</span>
155-
{rel.label && rel.label !== rel.relatedObject && (
156-
<span className="text-muted-foreground ml-1">{rel.relatedObject}</span>
157-
)}
158-
{rel.foreignKey && (
159-
<span className="text-muted-foreground text-xs ml-2">(FK: {rel.foreignKey})</span>
160-
)}
153+
<div className="space-y-4" data-testid="relationships-section">
154+
<div>
155+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">
156+
Relationships
157+
</h3>
158+
{hasRelationships ? (
159+
<div className="space-y-3">
160+
{object.relationships.map((rel, i) => (
161+
<div
162+
key={i}
163+
className="flex items-start gap-4 p-4 rounded-lg border bg-card hover:bg-accent/50 transition-colors"
164+
>
165+
<Badge variant="outline" className="text-xs shrink-0 mt-0.5">
166+
{rel.type}
167+
</Badge>
168+
<div className="min-w-0 flex-1">
169+
<p className="font-medium text-sm">{rel.label || rel.relatedObject}</p>
170+
{rel.label && rel.label !== rel.relatedObject && (
171+
<p className="text-sm text-muted-foreground mt-0.5">
172+
Related Object: {rel.relatedObject}
173+
</p>
174+
)}
175+
{rel.foreignKey && (
176+
<p className="text-xs text-muted-foreground mt-1">
177+
Foreign Key: <span className="font-mono">{rel.foreignKey}</span>
178+
</p>
179+
)}
180+
</div>
161181
</div>
162-
</div>
163-
))}
164-
</div>
165-
) : (
166-
<p className="text-sm text-muted-foreground">No relationships defined for this object.</p>
167-
)}
182+
))}
183+
</div>
184+
) : (
185+
<div className="text-center py-8 px-4 border border-dashed rounded-lg">
186+
<Link2 className="h-8 w-8 mx-auto mb-2 text-muted-foreground opacity-40" />
187+
<p className="text-sm text-muted-foreground">No relationships defined for this object.</p>
188+
</div>
189+
)}
190+
</div>
168191
</div>
169192
);
170193
}
@@ -184,28 +207,40 @@ export function ObjectKeysWidget({ schema }: { schema: ObjectWidgetSchema }) {
184207
);
185208

186209
return (
187-
<div className="rounded-lg border bg-card p-4 sm:p-6 space-y-4" data-testid="keys-section">
188-
<h2 className="text-sm font-semibold flex items-center gap-2">
189-
<KeyRound className="h-4 w-4" />
190-
Keys
191-
</h2>
192-
{keyFields.length > 0 ? (
193-
<div className="space-y-2">
194-
{keyFields.map((kf) => (
195-
<div key={kf.name} className="flex items-center gap-3 p-2 rounded-md bg-muted/40">
196-
<Badge variant={kf.name === 'id' ? 'default' : 'outline'} className="text-xs shrink-0">
197-
{kf.name === 'id' ? 'Primary Key' : kf.externalId ? 'External ID' : 'Unique'}
198-
</Badge>
199-
<div className="min-w-0 flex-1 text-sm">
200-
<span className="font-medium">{kf.label || kf.name}</span>
201-
<span className="text-muted-foreground text-xs ml-2">({kf.type})</span>
210+
<div className="space-y-4" data-testid="keys-section">
211+
<div>
212+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">
213+
Unique Keys
214+
</h3>
215+
{keyFields.length > 0 ? (
216+
<div className="space-y-3">
217+
{keyFields.map((kf) => (
218+
<div
219+
key={kf.name}
220+
className="flex items-start gap-4 p-4 rounded-lg border bg-card hover:bg-accent/50 transition-colors"
221+
>
222+
<Badge
223+
variant={kf.name === 'id' ? 'default' : 'outline'}
224+
className="text-xs shrink-0 mt-0.5"
225+
>
226+
{kf.name === 'id' ? 'Primary Key' : kf.externalId ? 'External ID' : 'Unique'}
227+
</Badge>
228+
<div className="min-w-0 flex-1">
229+
<p className="font-medium text-sm">{kf.label || kf.name}</p>
230+
<p className="text-xs text-muted-foreground mt-0.5">
231+
Type: <span className="font-mono">{kf.type}</span>
232+
</p>
233+
</div>
202234
</div>
203-
</div>
204-
))}
205-
</div>
206-
) : (
207-
<p className="text-sm text-muted-foreground">No unique keys or primary keys found.</p>
208-
)}
235+
))}
236+
</div>
237+
) : (
238+
<div className="text-center py-8 px-4 border border-dashed rounded-lg">
239+
<KeyRound className="h-8 w-8 mx-auto mb-2 text-muted-foreground opacity-40" />
240+
<p className="text-sm text-muted-foreground">No unique keys or primary keys found.</p>
241+
</div>
242+
)}
243+
</div>
209244
</div>
210245
);
211246
}
@@ -217,26 +252,30 @@ export function ObjectKeysWidget({ schema }: { schema: ObjectWidgetSchema }) {
217252

218253
export function ObjectDataExperienceWidget(_props: { schema: ObjectWidgetSchema }) {
219254
return (
220-
<div className="rounded-lg border bg-card p-4 sm:p-6 space-y-4" data-testid="data-experience-section">
221-
<h2 className="text-sm font-semibold flex items-center gap-2">
222-
<LayoutList className="h-4 w-4" />
223-
Data Experience
224-
</h2>
225-
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
226-
<div className="rounded-md border border-dashed p-4 text-center" data-testid="data-experience-forms">
227-
<PanelTop className="h-6 w-6 mx-auto mb-2 text-muted-foreground" />
228-
<p className="text-sm font-medium">Forms</p>
229-
<p className="text-xs text-muted-foreground mt-1">Design forms for data entry</p>
230-
</div>
231-
<div className="rounded-md border border-dashed p-4 text-center" data-testid="data-experience-views">
232-
<LayoutList className="h-6 w-6 mx-auto mb-2 text-muted-foreground" />
233-
<p className="text-sm font-medium">Views</p>
234-
<p className="text-xs text-muted-foreground mt-1">Configure list and detail views</p>
235-
</div>
236-
<div className="rounded-md border border-dashed p-4 text-center" data-testid="data-experience-dashboards">
237-
<BarChart3 className="h-6 w-6 mx-auto mb-2 text-muted-foreground" />
238-
<p className="text-sm font-medium">Dashboards</p>
239-
<p className="text-xs text-muted-foreground mt-1">Build visual dashboards</p>
255+
<div className="space-y-4" data-testid="data-experience-section">
256+
<div>
257+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">
258+
Data Experience
259+
</h3>
260+
<p className="text-sm text-muted-foreground mb-4">
261+
Configure how users interact with data in this object
262+
</p>
263+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
264+
<div className="rounded-lg border p-6 text-center hover:bg-accent/50 transition-colors cursor-pointer" data-testid="data-experience-forms">
265+
<PanelTop className="h-8 w-8 mx-auto mb-3 text-muted-foreground" />
266+
<p className="text-sm font-semibold mb-1">Forms</p>
267+
<p className="text-xs text-muted-foreground">Design forms for data entry</p>
268+
</div>
269+
<div className="rounded-lg border p-6 text-center hover:bg-accent/50 transition-colors cursor-pointer" data-testid="data-experience-views">
270+
<LayoutList className="h-8 w-8 mx-auto mb-3 text-muted-foreground" />
271+
<p className="text-sm font-semibold mb-1">Views</p>
272+
<p className="text-xs text-muted-foreground">Configure list and detail views</p>
273+
</div>
274+
<div className="rounded-lg border p-6 text-center hover:bg-accent/50 transition-colors cursor-pointer" data-testid="data-experience-dashboards">
275+
<BarChart3 className="h-8 w-8 mx-auto mb-3 text-muted-foreground" />
276+
<p className="text-sm font-semibold mb-1">Dashboards</p>
277+
<p className="text-xs text-muted-foreground">Build visual dashboards</p>
278+
</div>
240279
</div>
241280
</div>
242281
</div>
@@ -253,17 +292,21 @@ export function ObjectDataPreviewWidget({ schema }: { schema: ObjectWidgetSchema
253292
const { object } = useObjectData(objectName);
254293

255294
return (
256-
<div className="rounded-lg border bg-card p-4 sm:p-6 space-y-4" data-testid="data-preview-section">
257-
<h2 className="text-sm font-semibold flex items-center gap-2">
258-
<Table className="h-4 w-4" />
259-
Data Preview
260-
</h2>
261-
<div className="rounded-md border border-dashed p-8 text-center text-muted-foreground">
262-
<Table className="h-8 w-8 mx-auto mb-3 opacity-40" />
263-
<p className="text-sm font-medium">Sample Data</p>
264-
<p className="text-xs mt-1">
265-
Live data preview for &ldquo;{object?.label || objectName}&rdquo; will be available here
295+
<div className="space-y-4" data-testid="data-preview-section">
296+
<div>
297+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">
298+
Data Preview
299+
</h3>
300+
<p className="text-sm text-muted-foreground mb-4">
301+
View sample records from this object
266302
</p>
303+
<div className="rounded-lg border border-dashed p-12 text-center">
304+
<Table className="h-12 w-12 mx-auto mb-4 text-muted-foreground opacity-40" />
305+
<p className="text-sm font-medium mb-1">Sample Data</p>
306+
<p className="text-xs text-muted-foreground">
307+
Live data preview for &ldquo;{object?.label || objectName}&rdquo; will be available here
308+
</p>
309+
</div>
267310
</div>
268311
</div>
269312
);

0 commit comments

Comments
 (0)