feat(dashboard): implement customizable dashboard with widget support

- Added `react-grid-layout` for drag-and-drop functionality.
- Created `DashboardCanvas` component to manage layout and widget interactions.
- Introduced `WidgetContainer` and `WidgetPicker` components for widget management.
- Implemented various widgets: Active Rides, Today Sales, Pending Purchase Orders, Stock Alerts, Recent Orders, and Loading Sheets.
- Integrated API calls for fetching and saving dashboard preferences.
- Defined default layouts for different user roles.
- Created a card component for consistent widget styling.
This commit is contained in:
Kalana B. Thilakarathna
2026-08-29 16:51:25 +05:30
parent dc7e7a70fc
commit 29f181d5e1
17 changed files with 1089 additions and 9 deletions
+22
View File
@@ -0,0 +1,22 @@
import type { WidgetLayoutItem } from '@/lib/api/dashboard'
export const ROLE_DEFAULT_LAYOUTS: Record<string, WidgetLayoutItem[]> = {
admin: [
{ i: 'today-sales-1', widgetType: 'today-sales', x: 0, y: 0, w: 4, h: 4 },
{ i: 'active-rides-1', widgetType: 'active-rides', x: 4, y: 0, w: 4, h: 4 },
{ i: 'stock-alerts-1', widgetType: 'stock-alerts', x: 8, y: 0, w: 4, h: 4 },
{ i: 'pending-purchase-orders-1', widgetType: 'pending-purchase-orders', x: 0, y: 4, w: 4, h: 4 },
{ i: 'loading-sheets-1', widgetType: 'loading-sheets', x: 4, y: 4, w: 4, h: 4 },
{ i: 'recent-orders-1', widgetType: 'recent-orders', x: 0, y: 8, w: 8, h: 4 },
],
warehouse: [
{ i: 'active-rides-1', widgetType: 'active-rides', x: 0, y: 0, w: 6, h: 4 },
{ i: 'stock-alerts-1', widgetType: 'stock-alerts', x: 6, y: 0, w: 6, h: 4 },
{ i: 'loading-sheets-1', widgetType: 'loading-sheets', x: 0, y: 4, w: 6, h: 4 },
{ i: 'pending-purchase-orders-1', widgetType: 'pending-purchase-orders', x: 6, y: 4, w: 6, h: 4 },
],
sales: [
{ i: 'today-sales-1', widgetType: 'today-sales', x: 0, y: 0, w: 4, h: 4 },
{ i: 'recent-orders-1', widgetType: 'recent-orders', x: 4, y: 0, w: 8, h: 4 },
],
}
+97
View File
@@ -0,0 +1,97 @@
import type { ComponentType } from 'react'
import type { Role } from '@/contexts/auth-context'
import { ActiveRidesWidget } from '@/components/dashboard/widgets/active-rides-widget'
import { TodaySalesWidget } from '@/components/dashboard/widgets/today-sales-widget'
import { PendingPOsWidget } from '@/components/dashboard/widgets/pending-pos-widget'
import { StockAlertsWidget } from '@/components/dashboard/widgets/stock-alerts-widget'
import { RecentOrdersWidget } from '@/components/dashboard/widgets/recent-orders-widget'
import { LoadingSheetsWidget } from '@/components/dashboard/widgets/loading-sheets-widget'
export interface WidgetDefinition {
type: string
label: string
description: string
defaultW: number
defaultH: number
minW: number
minH: number
maxW?: number
allowedRoles: Role[] | '*'
component: ComponentType
}
export const WIDGET_REGISTRY: Record<string, WidgetDefinition> = {
'active-rides': {
type: 'active-rides',
label: 'Active Rides',
description: 'Live view of all currently active rides with driver and route info.',
defaultW: 4,
defaultH: 4,
minW: 3,
minH: 3,
allowedRoles: ['admin', 'warehouse'],
component: ActiveRidesWidget,
},
'today-sales': {
type: 'today-sales',
label: "Today's Sales",
description: "Total revenue and sales counts for today.",
defaultW: 4,
defaultH: 4,
minW: 2,
minH: 3,
allowedRoles: ['admin', 'sales'],
component: TodaySalesWidget,
},
'pending-purchase-orders': {
type: 'pending-purchase-orders',
label: 'Pending Purchase Orders',
description: 'Purchase orders that have been issued but not yet fully received.',
defaultW: 4,
defaultH: 4,
minW: 3,
minH: 3,
allowedRoles: ['admin', 'warehouse'],
component: PendingPOsWidget,
},
'stock-alerts': {
type: 'stock-alerts',
label: 'Stock Alerts',
description: 'Items at zero or critically low stock levels across all locations.',
defaultW: 4,
defaultH: 4,
minW: 3,
minH: 3,
allowedRoles: ['admin', 'warehouse'],
component: StockAlertsWidget,
},
'recent-orders': {
type: 'recent-orders',
label: 'Recent Orders',
description: 'Latest customer orders with status and value.',
defaultW: 5,
defaultH: 4,
minW: 4,
minH: 3,
allowedRoles: ['admin', 'sales'],
component: RecentOrdersWidget,
},
'loading-sheets': {
type: 'loading-sheets',
label: 'Pending Loading Sheets',
description: 'Loading sheets awaiting dispatch confirmation.',
defaultW: 4,
defaultH: 4,
minW: 3,
minH: 3,
allowedRoles: ['admin', 'warehouse'],
component: LoadingSheetsWidget,
},
}
export function getWidgetsForRole(role: Role): WidgetDefinition[] {
return Object.values(WIDGET_REGISTRY).filter(def =>
def.allowedRoles === '*' || def.allowedRoles.includes(role)
)
}