Files
ember-market-frontend/hooks/useWidgetLayout.ts
g a6e6cd0757
Some checks failed
Build Frontend / build (push) Failing after 7s
Remove widget resizing and edit mode from dashboard
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.
2026-01-12 11:13:25 +00:00

189 lines
6.4 KiB
TypeScript

"use client"
import { useState, useEffect, useCallback } from "react"
// Per-widget settings types
export interface RecentActivitySettings {
itemCount: number // 5, 10, 15
}
export interface TopProductsSettings {
itemCount: number // 3, 5, 10
showRevenue: boolean
}
export interface OverviewSettings {
showChange: boolean // Show % change from previous period
}
export interface RevenueChartSettings {
days: number // 7, 14, 30
showComparison: boolean
}
export interface LowStockSettings {
threshold: number // Show items with stock below this
itemCount: number
}
export interface RecentCustomersSettings {
itemCount: number
showSpent: boolean
}
export interface PendingChatsSettings {
showPreview: boolean
}
export type WidgetSettings =
| { type: "quick-actions" }
| { type: "overview"; settings: OverviewSettings }
| { type: "recent-activity"; settings: RecentActivitySettings }
| { type: "top-products"; settings: TopProductsSettings }
| { type: "revenue-chart"; settings: RevenueChartSettings }
| { type: "low-stock"; settings: LowStockSettings }
| { type: "recent-customers"; settings: RecentCustomersSettings }
| { type: "pending-chats"; settings: PendingChatsSettings }
export interface WidgetConfig {
id: string
title: string
visible: boolean
order: number
settings?: Record<string, any>
}
const DEFAULT_WIDGETS: WidgetConfig[] = [
{ id: "quick-actions", title: "Quick Actions", visible: true, order: 0 },
{ id: "overview", title: "Overview", visible: true, order: 1, settings: { showChange: false } },
{ id: "recent-activity", title: "Recent Activity", visible: true, order: 2, settings: { itemCount: 10 } },
{ 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, settings: { days: 7, showComparison: false } },
{ 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, settings: { itemCount: 5, showSpent: true } },
{ id: "pending-chats", title: "Pending Chats", visible: false, order: 7, settings: { showPreview: true } },
]
const STORAGE_KEY = "dashboard-widget-layout-v4"
/**
* useWidgetLayout - Persist and manage dashboard widget visibility, order, and settings
*/
export function useWidgetLayout() {
const [widgets, setWidgets] = useState<WidgetConfig[]>(DEFAULT_WIDGETS)
const [isLoaded, setIsLoaded] = useState(false)
// Load from localStorage on mount
useEffect(() => {
if (typeof window === "undefined") return
try {
const stored = localStorage.getItem(STORAGE_KEY)
if (stored) {
const parsed = JSON.parse(stored) as WidgetConfig[]
// Merge with defaults to handle new widgets added in future
const merged = DEFAULT_WIDGETS.map(defaultWidget => {
const savedWidget = parsed.find(w => w.id === defaultWidget.id)
return savedWidget
? { ...defaultWidget, ...savedWidget, settings: { ...defaultWidget.settings, ...savedWidget.settings } }
: defaultWidget
})
setWidgets(merged.sort((a, b) => a.order - b.order))
}
} catch (e) {
console.warn("Failed to load widget layout:", e)
}
setIsLoaded(true)
}, [])
// Save to localStorage whenever widgets change
useEffect(() => {
if (!isLoaded || typeof window === "undefined") return
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(widgets))
} catch (e) {
console.warn("Failed to save widget layout:", e)
}
}, [widgets, isLoaded])
const toggleWidget = useCallback((widgetId: string) => {
setWidgets(prev =>
prev.map(w => w.id === widgetId ? { ...w, visible: !w.visible } : w)
)
}, [])
const moveWidget = useCallback((widgetId: string, direction: "up" | "down") => {
setWidgets(prev => {
const index = prev.findIndex(w => w.id === widgetId)
if (index === -1) return prev
const newIndex = direction === "up" ? index - 1 : index + 1
if (newIndex < 0 || newIndex >= prev.length) return prev
const newWidgets = [...prev]
const [widget] = newWidgets.splice(index, 1)
newWidgets.splice(newIndex, 0, widget)
// Update order values
return newWidgets.map((w, i) => ({ ...w, order: i }))
})
}, [])
const updateWidgetSettings = useCallback((widgetId: string, newSettings: Record<string, any>) => {
setWidgets(prev =>
prev.map(w => w.id === widgetId
? { ...w, settings: { ...w.settings, ...newSettings } }
: w
)
)
}, [])
const getWidgetSettings = useCallback(<T extends Record<string, any>>(widgetId: string): T | undefined => {
return widgets.find(w => w.id === widgetId)?.settings as T | undefined
}, [widgets])
const resetLayout = useCallback(() => {
setWidgets(DEFAULT_WIDGETS)
}, [])
const getVisibleWidgets = useCallback(() => {
return widgets.filter(w => w.visible).sort((a, b) => a.order - b.order)
}, [widgets])
const isWidgetVisible = useCallback((widgetId: string) => {
return widgets.find(w => w.id === widgetId)?.visible ?? true
}, [widgets])
// Reorder widgets by moving activeId to overId's position
const reorderWidgets = useCallback((activeId: string, overId: string) => {
setWidgets(prev => {
const oldIndex = prev.findIndex(w => w.id === activeId)
const newIndex = prev.findIndex(w => w.id === overId)
if (oldIndex === -1 || newIndex === -1) return prev
const newWidgets = [...prev]
const [removed] = newWidgets.splice(oldIndex, 1)
newWidgets.splice(newIndex, 0, removed)
// Update order values
return newWidgets.map((w, i) => ({ ...w, order: i }))
})
}, [])
return {
widgets,
toggleWidget,
moveWidget,
reorderWidgets,
updateWidgetSettings,
getWidgetSettings,
resetLayout,
getVisibleWidgets,
isWidgetVisible,
isLoaded
}
}