Remove widget resizing and edit mode from dashboard
Some checks failed
Build Frontend / build (push) Failing after 7s
Some checks failed
Build Frontend / build (push) Failing after 7s
Eliminated the ability to resize dashboard widgets by removing colSpan from WidgetConfig, related UI, and logic. Removed edit mode functionality and the EditDashboardButton, simplifying the dashboard layout and widget management. Updated drag-and-drop strategy to vertical list and incremented the storage key version.
This commit is contained in:
@@ -6,7 +6,7 @@ import QuickActions from "./quick-actions"
|
|||||||
import RecentActivity from "./recent-activity"
|
import RecentActivity from "./recent-activity"
|
||||||
import { WidgetSettings } from "./widget-settings"
|
import { WidgetSettings } from "./widget-settings"
|
||||||
import { WidgetSettingsModal } from "./widget-settings-modal"
|
import { WidgetSettingsModal } from "./widget-settings-modal"
|
||||||
import { DashboardEditor, EditDashboardButton } from "./dashboard-editor"
|
import { DashboardEditor } from "./dashboard-editor"
|
||||||
import { DraggableWidget } from "./draggable-widget"
|
import { DraggableWidget } from "./draggable-widget"
|
||||||
import RevenueWidget from "./revenue-widget"
|
import RevenueWidget from "./revenue-widget"
|
||||||
import LowStockWidget from "./low-stock-widget"
|
import LowStockWidget from "./low-stock-widget"
|
||||||
@@ -46,9 +46,8 @@ export default function Content({ username, orderStats }: ContentProps) {
|
|||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { widgets, toggleWidget, moveWidget, reorderWidgets, resetLayout, isWidgetVisible, updateWidgetSettings, updateWidgetColSpan } = useWidgetLayout();
|
const { widgets, toggleWidget, moveWidget, reorderWidgets, resetLayout, isWidgetVisible, updateWidgetSettings } = useWidgetLayout();
|
||||||
const [configuredWidget, setConfiguredWidget] = useState<WidgetConfig | null>(null);
|
const [configuredWidget, setConfiguredWidget] = useState<WidgetConfig | null>(null);
|
||||||
const [isEditMode, setIsEditMode] = useState(false);
|
|
||||||
|
|
||||||
// Initialize with a default quote to match server-side rendering, then randomize on client
|
// Initialize with a default quote to match server-side rendering, then randomize on client
|
||||||
const [randomQuote, setRandomQuote] = useState({ text: "Loading wisdom...", author: "..." });
|
const [randomQuote, setRandomQuote] = useState({ text: "Loading wisdom...", author: "..." });
|
||||||
@@ -232,10 +231,6 @@ export default function Content({ username, orderStats }: ContentProps) {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<EditDashboardButton
|
|
||||||
isEditMode={isEditMode}
|
|
||||||
onToggle={() => setIsEditMode(!isEditMode)}
|
|
||||||
/>
|
|
||||||
<WidgetSettings
|
<WidgetSettings
|
||||||
widgets={widgets}
|
widgets={widgets}
|
||||||
onToggle={toggleWidget}
|
onToggle={toggleWidget}
|
||||||
@@ -248,30 +243,24 @@ export default function Content({ username, orderStats }: ContentProps) {
|
|||||||
|
|
||||||
<DashboardEditor
|
<DashboardEditor
|
||||||
widgets={widgets}
|
widgets={widgets}
|
||||||
isEditMode={isEditMode}
|
isEditMode={false}
|
||||||
onToggleEditMode={() => setIsEditMode(false)}
|
onToggleEditMode={() => { }}
|
||||||
onReorder={reorderWidgets}
|
onReorder={reorderWidgets}
|
||||||
onReset={resetLayout}
|
onReset={resetLayout}
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 auto-rows-min">
|
<div className="space-y-10">
|
||||||
{widgets.map((widget) => {
|
{widgets.map((widget) => {
|
||||||
if (!widget.visible && !isEditMode) return null;
|
if (!widget.visible) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggableWidget
|
<DraggableWidget
|
||||||
key={widget.id}
|
key={widget.id}
|
||||||
widget={widget}
|
widget={widget}
|
||||||
isEditMode={isEditMode}
|
isEditMode={false}
|
||||||
onConfigure={() => setConfiguredWidget(widget)}
|
onConfigure={() => setConfiguredWidget(widget)}
|
||||||
onToggleVisibility={() => toggleWidget(widget.id)}
|
onToggleVisibility={() => toggleWidget(widget.id)}
|
||||||
>
|
>
|
||||||
{!widget.visible && isEditMode ? (
|
{renderWidget(widget)}
|
||||||
<div className="opacity-40 grayscale pointer-events-none h-full">
|
|
||||||
{renderWidget(widget)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
renderWidget(widget)
|
|
||||||
)}
|
|
||||||
</DraggableWidget>
|
</DraggableWidget>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -283,9 +272,8 @@ export default function Content({ username, orderStats }: ContentProps) {
|
|||||||
widget={configuredWidget}
|
widget={configuredWidget}
|
||||||
open={!!configuredWidget}
|
open={!!configuredWidget}
|
||||||
onOpenChange={(open) => !open && setConfiguredWidget(null)}
|
onOpenChange={(open) => !open && setConfiguredWidget(null)}
|
||||||
onSave={(widgetId, settings, colSpan) => {
|
onSave={(widgetId, settings) => {
|
||||||
updateWidgetSettings(widgetId, settings);
|
updateWidgetSettings(widgetId, settings);
|
||||||
if (colSpan !== undefined) updateWidgetColSpan(widgetId, colSpan);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
SortableContext,
|
SortableContext,
|
||||||
sortableKeyboardCoordinates,
|
sortableKeyboardCoordinates,
|
||||||
rectSortingStrategy,
|
verticalListSortingStrategy,
|
||||||
arrayMove,
|
arrayMove,
|
||||||
} from "@dnd-kit/sortable"
|
} from "@dnd-kit/sortable"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
@@ -81,7 +81,7 @@ export function DashboardEditor({
|
|||||||
>
|
>
|
||||||
<SortableContext
|
<SortableContext
|
||||||
items={widgets.map(w => w.id)}
|
items={widgets.map(w => w.id)}
|
||||||
strategy={rectSortingStrategy}
|
strategy={verticalListSortingStrategy}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
@@ -126,32 +126,3 @@ export function DashboardEditor({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React from "react"
|
import React, { useState, useEffect, useRef } from "react"
|
||||||
import { useSortable } from "@dnd-kit/sortable"
|
import { useSortable } from "@dnd-kit/sortable"
|
||||||
import { CSS } from "@dnd-kit/utilities"
|
import { CSS } from "@dnd-kit/utilities"
|
||||||
import { GripVertical, Settings, X, Eye, EyeOff } from "lucide-react"
|
import { GripVertical, Settings, X, Eye, EyeOff } from "lucide-react"
|
||||||
@@ -41,21 +41,12 @@ export function DraggableWidget({
|
|||||||
zIndex: isDragging ? 1000 : 1,
|
zIndex: isDragging ? 1000 : 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
const colSpanClasses = {
|
|
||||||
1: "lg:col-span-1",
|
|
||||||
2: "lg:col-span-2",
|
|
||||||
3: "lg:col-span-3",
|
|
||||||
4: "lg:col-span-4",
|
|
||||||
}[widget.colSpan || 4] || "lg:col-span-4"
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
style={style}
|
style={style}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative group",
|
"relative group",
|
||||||
colSpanClasses,
|
|
||||||
"md:col-span-2", // Default to 2 columns on tablet
|
|
||||||
isEditMode && "ring-2 ring-primary ring-offset-2 ring-offset-background rounded-lg",
|
isEditMode && "ring-2 ring-primary ring-offset-2 ring-offset-background rounded-lg",
|
||||||
isDragging && "z-50 shadow-2xl"
|
isDragging && "z-50 shadow-2xl"
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -28,25 +28,23 @@ interface WidgetSettingsModalProps {
|
|||||||
widget: WidgetConfig | null
|
widget: WidgetConfig | null
|
||||||
open: boolean
|
open: boolean
|
||||||
onOpenChange: (open: boolean) => void
|
onOpenChange: (open: boolean) => void
|
||||||
onSave: (widgetId: string, settings: Record<string, any>, colSpan: number) => void
|
onSave: (widgetId: string, settings: Record<string, any>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WidgetSettingsModal({ widget, open, onOpenChange, onSave }: WidgetSettingsModalProps) {
|
export function WidgetSettingsModal({ widget, open, onOpenChange, onSave }: WidgetSettingsModalProps) {
|
||||||
const [localSettings, setLocalSettings] = useState<Record<string, any>>({})
|
const [localSettings, setLocalSettings] = useState<Record<string, any>>({})
|
||||||
const [localColSpan, setLocalColSpan] = useState<number>(4)
|
|
||||||
|
|
||||||
// Initialize local settings when widget changes
|
// Initialize local settings when widget changes
|
||||||
const handleOpenChange = (isOpen: boolean) => {
|
const handleOpenChange = (isOpen: boolean) => {
|
||||||
if (isOpen && widget) {
|
if (isOpen && widget) {
|
||||||
setLocalSettings({ ...widget.settings })
|
setLocalSettings({ ...widget.settings })
|
||||||
setLocalColSpan(widget.colSpan || 4)
|
|
||||||
}
|
}
|
||||||
onOpenChange(isOpen)
|
onOpenChange(isOpen)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (widget) {
|
if (widget) {
|
||||||
onSave(widget.id, localSettings, localColSpan)
|
onSave(widget.id, localSettings)
|
||||||
onOpenChange(false)
|
onOpenChange(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,27 +70,6 @@ export function WidgetSettingsModal({ widget, open, onOpenChange, onSave }: Widg
|
|||||||
|
|
||||||
<ScrollArea className="max-h-[60vh] -mr-4 pr-4">
|
<ScrollArea className="max-h-[60vh] -mr-4 pr-4">
|
||||||
<div className="space-y-6 py-4">
|
<div className="space-y-6 py-4">
|
||||||
{/* Resize Selection */}
|
|
||||||
<div className="space-y-3 pb-6 border-b border-border/40">
|
|
||||||
<Label className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">Widget Display</Label>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<Label htmlFor="colSpan" className="text-sm font-medium">Widget Width</Label>
|
|
||||||
<Select
|
|
||||||
value={String(localColSpan)}
|
|
||||||
onValueChange={(v) => setLocalColSpan(parseInt(v))}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-40">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="1">Small (1/4)</SelectItem>
|
|
||||||
<SelectItem value="2">Medium (1/2)</SelectItem>
|
|
||||||
<SelectItem value="3">Large (3/4)</SelectItem>
|
|
||||||
<SelectItem value="4">Full Width</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Recent Activity Settings */}
|
{/* Recent Activity Settings */}
|
||||||
|
|||||||
@@ -50,22 +50,21 @@ export interface WidgetConfig {
|
|||||||
title: string
|
title: string
|
||||||
visible: boolean
|
visible: boolean
|
||||||
order: number
|
order: number
|
||||||
colSpan: number // 1, 2, 3, 4 (full)
|
|
||||||
settings?: Record<string, any>
|
settings?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_WIDGETS: WidgetConfig[] = [
|
const DEFAULT_WIDGETS: WidgetConfig[] = [
|
||||||
{ id: "quick-actions", title: "Quick Actions", visible: true, order: 0, colSpan: 4 },
|
{ id: "quick-actions", title: "Quick Actions", visible: true, order: 0 },
|
||||||
{ id: "overview", title: "Overview", visible: true, order: 1, colSpan: 4, settings: { showChange: false } },
|
{ id: "overview", title: "Overview", visible: true, order: 1, settings: { showChange: false } },
|
||||||
{ id: "recent-activity", title: "Recent Activity", visible: true, order: 2, colSpan: 2, settings: { itemCount: 10 } },
|
{ id: "recent-activity", title: "Recent Activity", visible: true, order: 2, settings: { itemCount: 10 } },
|
||||||
{ id: "top-products", title: "Top Products", visible: true, order: 3, colSpan: 2, settings: { itemCount: 5, showRevenue: true } },
|
{ id: "top-products", title: "Top Products", visible: true, order: 3, settings: { itemCount: 5, showRevenue: true } },
|
||||||
{ id: "revenue-chart", title: "Revenue Chart", visible: false, order: 4, colSpan: 2, settings: { days: 7, showComparison: false } },
|
{ id: "revenue-chart", title: "Revenue Chart", visible: false, order: 4, settings: { days: 7, showComparison: false } },
|
||||||
{ id: "low-stock", title: "Low Stock Alerts", visible: false, order: 5, colSpan: 2, settings: { threshold: 5, itemCount: 5 } },
|
{ id: "low-stock", title: "Low Stock Alerts", visible: false, order: 5, settings: { threshold: 5, itemCount: 5 } },
|
||||||
{ id: "recent-customers", title: "Recent Customers", visible: false, order: 6, colSpan: 2, settings: { itemCount: 5, showSpent: true } },
|
{ id: "recent-customers", title: "Recent Customers", visible: false, order: 6, settings: { itemCount: 5, showSpent: true } },
|
||||||
{ id: "pending-chats", title: "Pending Chats", visible: false, order: 7, colSpan: 2, settings: { showPreview: true } },
|
{ id: "pending-chats", title: "Pending Chats", visible: false, order: 7, settings: { showPreview: true } },
|
||||||
]
|
]
|
||||||
|
|
||||||
const STORAGE_KEY = "dashboard-widget-layout-v3"
|
const STORAGE_KEY = "dashboard-widget-layout-v4"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* useWidgetLayout - Persist and manage dashboard widget visibility, order, and settings
|
* useWidgetLayout - Persist and manage dashboard widget visibility, order, and settings
|
||||||
@@ -140,11 +139,6 @@ export function useWidgetLayout() {
|
|||||||
)
|
)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const updateWidgetColSpan = useCallback((widgetId: string, colSpan: number) => {
|
|
||||||
setWidgets(prev =>
|
|
||||||
prev.map(w => w.id === widgetId ? { ...w, colSpan } : w)
|
|
||||||
)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const getWidgetSettings = useCallback(<T extends Record<string, any>>(widgetId: string): T | undefined => {
|
const getWidgetSettings = useCallback(<T extends Record<string, any>>(widgetId: string): T | undefined => {
|
||||||
return widgets.find(w => w.id === widgetId)?.settings as T | undefined
|
return widgets.find(w => w.id === widgetId)?.settings as T | undefined
|
||||||
@@ -185,7 +179,6 @@ export function useWidgetLayout() {
|
|||||||
moveWidget,
|
moveWidget,
|
||||||
reorderWidgets,
|
reorderWidgets,
|
||||||
updateWidgetSettings,
|
updateWidgetSettings,
|
||||||
updateWidgetColSpan,
|
|
||||||
getWidgetSettings,
|
getWidgetSettings,
|
||||||
resetLayout,
|
resetLayout,
|
||||||
getVisibleWidgets,
|
getVisibleWidgets,
|
||||||
|
|||||||
Reference in New Issue
Block a user