29f181d5e1
- 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.
77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
'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>
|
|
)
|
|
}
|