Some checks failed
Build Frontend / build (push) Failing after 7s
Introduces a modular dashboard system with draggable, configurable widgets including revenue, low stock, recent customers, and pending chats. Adds a dashboard editor for layout customization, widget visibility, and settings. Refactors dashboard content to use the new widget system and improves UI consistency and interactivity.
158 lines
4.7 KiB
TypeScript
158 lines
4.7 KiB
TypeScript
"use client"
|
|
|
|
import React, { useState } from "react"
|
|
import {
|
|
DndContext,
|
|
closestCenter,
|
|
KeyboardSensor,
|
|
PointerSensor,
|
|
useSensor,
|
|
useSensors,
|
|
DragEndEvent,
|
|
DragOverlay,
|
|
DragStartEvent,
|
|
} from "@dnd-kit/core"
|
|
import {
|
|
SortableContext,
|
|
sortableKeyboardCoordinates,
|
|
rectSortingStrategy,
|
|
arrayMove,
|
|
} from "@dnd-kit/sortable"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Edit3, X, Check, RotateCcw } from "lucide-react"
|
|
import { WidgetConfig } from "@/hooks/useWidgetLayout"
|
|
import { motion, AnimatePresence } from "framer-motion"
|
|
|
|
interface DashboardEditorProps {
|
|
widgets: WidgetConfig[]
|
|
isEditMode: boolean
|
|
onToggleEditMode: () => void
|
|
onReorder: (activeId: string, overId: string) => void
|
|
onReset: () => void
|
|
children: React.ReactNode
|
|
}
|
|
|
|
export function DashboardEditor({
|
|
widgets,
|
|
isEditMode,
|
|
onToggleEditMode,
|
|
onReorder,
|
|
onReset,
|
|
children
|
|
}: DashboardEditorProps) {
|
|
const [activeId, setActiveId] = useState<string | null>(null)
|
|
|
|
const sensors = useSensors(
|
|
useSensor(PointerSensor, {
|
|
activationConstraint: {
|
|
distance: 8,
|
|
},
|
|
}),
|
|
useSensor(KeyboardSensor, {
|
|
coordinateGetter: sortableKeyboardCoordinates,
|
|
})
|
|
)
|
|
|
|
const handleDragStart = (event: DragStartEvent) => {
|
|
setActiveId(event.active.id as string)
|
|
}
|
|
|
|
const handleDragEnd = (event: DragEndEvent) => {
|
|
const { active, over } = event
|
|
|
|
if (over && active.id !== over.id) {
|
|
onReorder(active.id as string, over.id as string)
|
|
}
|
|
|
|
setActiveId(null)
|
|
}
|
|
|
|
const handleDragCancel = () => {
|
|
setActiveId(null)
|
|
}
|
|
|
|
return (
|
|
<DndContext
|
|
sensors={sensors}
|
|
collisionDetection={closestCenter}
|
|
onDragStart={handleDragStart}
|
|
onDragEnd={handleDragEnd}
|
|
onDragCancel={handleDragCancel}
|
|
>
|
|
<SortableContext
|
|
items={widgets.map(w => w.id)}
|
|
strategy={rectSortingStrategy}
|
|
>
|
|
{children}
|
|
</SortableContext>
|
|
|
|
{/* Edit Mode Banner */}
|
|
<AnimatePresence>
|
|
{isEditMode && (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 50 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: 50 }}
|
|
className="fixed bottom-6 left-1/2 -translate-x-1/2 z-50"
|
|
>
|
|
<div className="flex items-center gap-3 bg-primary text-primary-foreground px-4 py-2.5 rounded-full shadow-lg border border-primary-foreground/20">
|
|
<span className="text-sm font-medium">
|
|
Editing Dashboard • Drag widgets to reorder
|
|
</span>
|
|
<div className="h-4 w-px bg-primary-foreground/30" />
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="h-7 px-2 hover:bg-primary-foreground/20"
|
|
onClick={onReset}
|
|
>
|
|
<RotateCcw className="h-3.5 w-3.5 mr-1" />
|
|
Reset
|
|
</Button>
|
|
<Button
|
|
variant="secondary"
|
|
size="sm"
|
|
className="h-7 px-3"
|
|
onClick={onToggleEditMode}
|
|
>
|
|
<Check className="h-3.5 w-3.5 mr-1" />
|
|
Done
|
|
</Button>
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</DndContext>
|
|
)
|
|
}
|
|
|
|
// Edit button component to add to the header
|
|
export function EditDashboardButton({
|
|
isEditMode,
|
|
onToggle
|
|
}: {
|
|
isEditMode: boolean
|
|
onToggle: () => void
|
|
}) {
|
|
return (
|
|
<Button
|
|
variant={isEditMode ? "default" : "outline"}
|
|
size="sm"
|
|
className="h-8 gap-2"
|
|
onClick={onToggle}
|
|
>
|
|
{isEditMode ? (
|
|
<>
|
|
<X className="h-4 w-4" />
|
|
<span className="hidden sm:inline">Cancel</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Edit3 className="h-4 w-4" />
|
|
<span className="hidden sm:inline">Edit Layout</span>
|
|
</>
|
|
)}
|
|
</Button>
|
|
)
|
|
}
|