Skip to content

Commit 79bab86

Browse files
committed
feat: enhance ObjectView and DataTableRenderer with improved styling and layout adjustments
1 parent 6044450 commit 79bab86

File tree

3 files changed

+129
-93
lines changed

3 files changed

+129
-93
lines changed

apps/console/src/components/ObjectView.tsx

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -165,58 +165,68 @@ export function ObjectView({ dataSource, objects, onEdit }: any) {
165165
};
166166

167167
return (
168-
<div className="h-full flex flex-col">
169-
{/* Header Section */}
170-
<div className="flex justify-between items-center py-4 px-6 border-b shrink-0 bg-background/95 backdrop-blur z-10 sticky top-0">
171-
<div className="flex items-center gap-4">
172-
<div className="flex items-center gap-3">
173-
<div className="bg-primary/10 p-2 rounded-lg shrink-0">
174-
{/* Map icons based on object name if possible, fallback to generic */}
175-
<TableIcon className="h-5 w-5 text-primary" />
176-
</div>
177-
<div>
178-
<h1 className="text-xl font-bold tracking-tight text-foreground">{objectDef.label}</h1>
179-
<p className="text-sm text-muted-foreground">{activeView.label}</p>
180-
</div>
168+
<div className="h-full flex flex-col bg-background">
169+
{/* 1. Main Header */}
170+
<div className="flex justify-between items-center py-3 px-4 border-b shrink-0 bg-background z-10">
171+
<div className="flex items-center gap-3">
172+
<div className="bg-primary/10 p-2 rounded-md shrink-0">
173+
<TableIcon className="h-4 w-4 text-primary" />
174+
</div>
175+
<div>
176+
<h1 className="text-lg font-semibold tracking-tight text-foreground">{objectDef.label}</h1>
181177
</div>
182-
183-
{/* View Switcher - Re-styled as refined segmented control */}
184-
{views.length > 1 && (
185-
<div className="ml-4 h-8 bg-muted/50 rounded-lg p-1 flex items-center">
186-
{views.map((v: any) => (
187-
<button
188-
key={v.id}
189-
onClick={() => handleViewChange(v.id)}
190-
className={`
191-
flex items-center gap-2 px-3 h-full rounded-md text-sm font-medium transition-all
192-
${activeView.id === v.id
193-
? 'bg-background shadow-sm text-foreground'
194-
: 'text-muted-foreground hover:bg-background/50 hover:text-foreground/80'
195-
}
196-
`}
197-
>
198-
{v.type === 'kanban' && <KanbanIcon className="h-3.5 w-3.5" />}
199-
{v.type === 'calendar' && <CalendarIcon className="h-3.5 w-3.5" />}
200-
{v.type === 'grid' && <TableIcon className="h-3.5 w-3.5" />}
201-
{v.type === 'gantt' && <AlignLeft className="h-3.5 w-3.5" />}
202-
<span>{v.label}</span>
203-
</button>
204-
))}
205-
</div>
206-
)}
207178
</div>
208179

209180
<div className="flex items-center gap-2">
210-
<Button onClick={() => onEdit(null)} className="shadow-none gap-2">
181+
<Button size="sm" onClick={() => onEdit(null)} className="shadow-none gap-2">
211182
<Plus className="h-4 w-4" />
212183
<span className="hidden sm:inline">New</span>
213184
</Button>
214185
</div>
215186
</div>
216187

217-
{/* Content Area */}
218-
<div className="flex-1 bg-muted/5 p-4 sm:p-6 overflow-hidden">
219-
<div className="h-full w-full rounded-xl border bg-card text-card-foreground shadow-sm overflow-hidden relative">
188+
{/* 2. View Toolbar (Tabs & Controls) */}
189+
<div className="flex justify-between items-center py-2 px-4 border-b shrink-0 bg-muted/20">
190+
{/* Left: View Tabs */}
191+
<div className="flex items-center gap-1 overflow-x-auto no-scrollbar">
192+
{views.map((v: any) => {
193+
const isActive = activeView.id === v.id;
194+
return (
195+
<button
196+
key={v.id}
197+
onClick={() => handleViewChange(v.id)}
198+
className={`
199+
flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium transition-all whitespace-nowrap
200+
${isActive
201+
? 'bg-background shadow-sm text-foreground ring-1 ring-border/50'
202+
: 'text-muted-foreground hover:bg-background/50 hover:text-foreground'
203+
}
204+
`}
205+
>
206+
{v.type === 'kanban' && <KanbanIcon className="h-3.5 w-3.5 opacity-70" />}
207+
{v.type === 'calendar' && <CalendarIcon className="h-3.5 w-3.5 opacity-70" />}
208+
{v.type === 'grid' && <TableIcon className="h-3.5 w-3.5 opacity-70" />}
209+
{v.type === 'gantt' && <AlignLeft className="h-3.5 w-3.5 opacity-70" />}
210+
<span>{v.label}</span>
211+
</button>
212+
);
213+
})}
214+
</div>
215+
216+
{/* Right: View Options (Placeholder for now) */}
217+
<div className="flex items-center gap-2 hidden md:flex">
218+
<Button variant="ghost" size="sm" className="h-8 text-muted-foreground">
219+
<span className="text-xs">Filter</span>
220+
</Button>
221+
<Button variant="ghost" size="sm" className="h-8 text-muted-foreground">
222+
<span className="text-xs">Sort</span>
223+
</Button>
224+
</div>
225+
</div>
226+
227+
{/* 3. Content Area (Edge-to-Edge) */}
228+
<div className="flex-1 overflow-hidden relative">
229+
<div className="absolute inset-0">
220230
{renderCurrentView()}
221231
</div>
222232
</div>

packages/components/src/renderers/complex/data-table.tsx

Lines changed: 59 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -475,60 +475,68 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
475475
</TableCell>
476476
</TableRow>
477477
) : (
478-
paginatedData.map((row, rowIndex) => {
479-
const globalIndex = (currentPage - 1) * pageSize + rowIndex;
480-
const rowId = getRowId(row, globalIndex);
481-
const isSelected = selectedRowIds.has(rowId);
482-
483-
return (
484-
<TableRow key={rowId} data-state={isSelected ? 'selected' : undefined}>
485-
{selectable && (
486-
<TableCell>
487-
<Checkbox
488-
checked={isSelected}
489-
onCheckedChange={(checked) => handleSelectRow(rowId, checked as boolean)}
490-
/>
491-
</TableCell>
492-
)}
493-
{columns.map((col, colIndex) => {
494-
const columnWidth = columnWidths[col.accessorKey] || col.width;
495-
return (
496-
<TableCell
497-
key={colIndex}
498-
className={col.cellClassName}
499-
style={{
500-
width: columnWidth,
501-
minWidth: columnWidth,
502-
maxWidth: columnWidth
503-
}}
504-
>
505-
{row[col.accessorKey]}
478+
<>
479+
{paginatedData.map((row, rowIndex) => {
480+
const globalIndex = (currentPage - 1) * pageSize + rowIndex;
481+
const rowId = getRowId(row, globalIndex);
482+
const isSelected = selectedRowIds.has(rowId);
483+
484+
return (
485+
<TableRow key={rowId} data-state={isSelected ? 'selected' : undefined}>
486+
{selectable && (
487+
<TableCell>
488+
<Checkbox
489+
checked={isSelected}
490+
onCheckedChange={(checked) => handleSelectRow(rowId, checked as boolean)}
491+
/>
506492
</TableCell>
507-
);
508-
})}
509-
{rowActions && (
510-
<TableCell className="text-right">
511-
<div className="flex items-center justify-end gap-1">
512-
<Button
513-
variant="ghost"
514-
size="icon-sm"
515-
onClick={() => schema.onRowEdit?.(row)}
516-
>
517-
<Edit className="h-4 w-4" />
518-
</Button>
519-
<Button
520-
variant="ghost"
521-
size="icon-sm"
522-
onClick={() => schema.onRowDelete?.(row)}
493+
)}
494+
{columns.map((col, colIndex) => {
495+
const columnWidth = columnWidths[col.accessorKey] || col.width;
496+
return (
497+
<TableCell
498+
key={colIndex}
499+
className={col.cellClassName}
500+
style={{
501+
width: columnWidth,
502+
minWidth: columnWidth,
503+
maxWidth: columnWidth
504+
}}
523505
>
524-
<Trash2 className="h-4 w-4 text-destructive" />
525-
</Button>
526-
</div>
527-
</TableCell>
528-
)}
506+
{row[col.accessorKey]}
507+
</TableCell>
508+
);
509+
})}
510+
{rowActions && (
511+
<TableCell className="text-right">
512+
<div className="flex items-center justify-end gap-1">
513+
<Button
514+
variant="ghost"
515+
size="icon-sm"
516+
onClick={() => schema.onRowEdit?.(row)}
517+
>
518+
<Edit className="h-4 w-4" />
519+
</Button>
520+
<Button
521+
variant="ghost"
522+
size="icon-sm"
523+
onClick={() => schema.onRowDelete?.(row)}
524+
>
525+
<Trash2 className="h-4 w-4 text-destructive" />
526+
</Button>
527+
</div>
528+
</TableCell>
529+
)}
530+
</TableRow>
531+
);
532+
})}
533+
{/* Filler rows to maintain height consistency */}
534+
{paginatedData.length > 0 && Array.from({ length: Math.max(0, pageSize - paginatedData.length) }).map((_, i) => (
535+
<TableRow key={`empty-${i}`} className="hover:bg-transparent">
536+
<TableCell colSpan={columns.length + (selectable ? 1 : 0) + (rowActions ? 1 : 0)} className="h-[52px] p-0" />
529537
</TableRow>
530-
);
531-
})
538+
))}
539+
</>
532540
)}
533541
</TableBody>
534542
</Table>

packages/plugin-dashboard/src/DashboardRenderer.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,24 @@ export const DashboardRenderer = forwardRef<HTMLDivElement, { schema: DashboardS
7878
return widget; // Fallback to widget itself as schema
7979
}, [widget]);
8080

81+
// Check if the widget is self-contained (like a Metric Card) to avoid double borders
82+
const isSelfContained = widget.type === 'metric';
83+
84+
if (isSelfContained) {
85+
return (
86+
<div
87+
key={widget.id || widget.title}
88+
className="h-full w-full"
89+
style={widget.layout ? {
90+
gridColumn: `span ${widget.layout.w}`,
91+
gridRow: `span ${widget.layout.h}`
92+
}: undefined}
93+
>
94+
<SchemaRenderer schema={componentSchema} className="h-full w-full" />
95+
</div>
96+
);
97+
}
98+
8199
return (
82100
<Card
83101
key={widget.id || widget.title}

0 commit comments

Comments
 (0)