Arranging Widgets
In Edit Mode, widgets can be dragged to any grid position, resized to span multiple columns, and removed. The grid snaps to column positions. All changes are saved when you exit Edit Mode.
Entering Edit Mode
Click the Edit Layout button (pencil icon) in the top-right of the dashboard. In Edit Mode:
- Each widget shows a drag handle (⠿) in its top-left corner
- Each widget shows a resize grip in its bottom-right corner
- Each widget shows a ✕ Remove button in its top-right corner
- The + Add Widget button becomes more prominent
- A Save Layout button appears — clicking it saves and exits Edit Mode
- A Cancel button discards changes and restores the previous layout
Drag to Reorder
Click and hold the drag handle on any widget, then drag it to a new position in the grid. Other widgets shift automatically to accommodate the moved widget. The grid is 3 columns wide by default (responsive — narrows to 2 columns on tablets, 1 column on mobile).
Resize Widgets
Drag the resize grip in the bottom-right corner of a widget to change its column span:
| Width Setting | Columns Spanned | Best For |
|---|---|---|
| 1 unit (default) | 1 column | Compact metric tiles (PendingTasksCount, small Quick Launch) |
| 2 units | 2 columns | Recent Workflows, wider Quick Launch lists |
| 3 units (full width) | 3 columns | Announcement banners, wide Grafana metrics panels |
Removing a Widget
Click the ✕ button on a widget in Edit Mode to remove it from the dashboard. The widget disappears immediately. If removed accidentally, click Cancel to restore the previous layout. Removed widgets can be re-added at any time from the Widget Picker.
Grid Layout Implementation
// DashboardWidgetGrid.tsx — simplified
import GridLayout from 'react-grid-layout';
function DashboardWidgetGrid({ widgets, isEditing, onLayoutChange }) {
const layout = widgets.map(w => ({
i: w.id,
x: w.col, y: w.row,
w: w.w, h: w.h,
static: !isEditing, // drag disabled when not in edit mode
}));
return (
<GridLayout
className="dashboard-grid"
layout={layout}
cols={3}
rowHeight={200}
isDraggable={isEditing}
isResizable={isEditing}
onLayoutChange={(newLayout) => {
// Map back to widget objects
const updated = widgets.map(w => {
const l = newLayout.find(nl => nl.i === w.id);
return l ? { ...w, col: l.x, row: l.y, w: l.w, h: l.h } : w;
});
onLayoutChange(updated);
}}
>
{widgets.map(w => (
<div key={w.id}>
<WidgetRenderer widget={w} isEditing={isEditing} />
</div>
))}
</GridLayout>
);
}
When you click "Save Layout", the updated grid positions are immediately POSTed to PUT /api/workdesk/dashboard. The save is optimistic — the layout appears saved in the UI before the API confirms. If the API fails, the previous layout is restored with an error notification.