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:
@@ -0,0 +1,76 @@
|
||||
'use client'
|
||||
|
||||
import { getWidgetsForRole, type WidgetDefinition } from '@/lib/widgets/registry'
|
||||
import type { Role } from '@/contexts/auth-context'
|
||||
import type { WidgetLayoutItem } from '@/lib/api/dashboard'
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
} from '@/components/ui/sheet'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { HugeiconsIcon } from '@hugeicons/react'
|
||||
import { Add01Icon } from '@hugeicons/core-free-icons'
|
||||
|
||||
interface WidgetPickerProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
role: Role
|
||||
currentLayout: WidgetLayoutItem[]
|
||||
onAdd: (widgetType: string) => void
|
||||
}
|
||||
|
||||
export function WidgetPicker({ open, onClose, role, currentLayout, onAdd }: WidgetPickerProps) {
|
||||
const available = getWidgetsForRole(role)
|
||||
const activeTypes = new Set(currentLayout.map(l => l.widgetType))
|
||||
|
||||
function handleAdd(def: WidgetDefinition) {
|
||||
onAdd(def.type)
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={v => { if (!v) onClose() }}>
|
||||
<SheetContent side="right" className="flex w-full flex-col gap-0 sm:max-w-sm">
|
||||
<SheetHeader className="px-4 pb-3 pt-4">
|
||||
<SheetTitle>Add Widget</SheetTitle>
|
||||
<SheetDescription>
|
||||
Choose a widget to add to your dashboard.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
<Separator />
|
||||
<div className="flex flex-1 flex-col gap-1 overflow-auto p-4">
|
||||
{available.map(def => {
|
||||
const alreadyAdded = activeTypes.has(def.type)
|
||||
return (
|
||||
<div
|
||||
key={def.type}
|
||||
className="flex items-start justify-between gap-3 rounded-xl border p-3"
|
||||
>
|
||||
<div className="flex min-w-0 flex-col gap-0.5">
|
||||
<p className="text-sm font-medium">{def.label}</p>
|
||||
<p className="text-xs text-muted-foreground">{def.description}</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
Default size: {def.defaultW}×{def.defaultH}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={alreadyAdded ? 'outline' : 'default'}
|
||||
className="mt-0.5 shrink-0"
|
||||
onClick={() => handleAdd(def)}
|
||||
>
|
||||
<HugeiconsIcon icon={Add01Icon} size={14} strokeWidth={2} />
|
||||
{alreadyAdded ? 'Add another' : 'Add'}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user