Files
klz-cables.com/.pnpm-store/v10/files/bc/89e0e0f2fe0568c2ea9765ae6351da154ca48b6ef8c6429b9009eefef3fdf1e47f57b18f7022cf0aa606c53832f6ab1ea3cbd4acf7d3bd9aee000722adda6a
Marc Mintel 5397309103
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
fix(products): fix breadcrumbs and product filtering (backport from main)
2026-02-24 16:04:21 +01:00

165 lines
5.2 KiB
Plaintext

import { arrayMove } from '@dnd-kit/sortable';
import { ConfirmationModal, toast, useConfig, useModal, usePreferences, useServerFunctions } from '@payloadcms/ui';
import { PREFERENCE_KEYS } from 'payload/shared';
import React, { useCallback, useEffect, useState } from 'react';
import { RenderWidget } from './renderWidget/RenderWidget.js';
export function useDashboardLayout(initialLayout) {
const setLayoutPreference = useSetLayoutPreference();
const [isEditing, setIsEditing] = useState(false);
const {
widgets = []
} = useConfig().config.admin.dashboard ?? {};
const [currentLayout, setCurrentLayout] = useState(initialLayout);
const {
openModal
} = useModal();
const cancelModalSlug = 'cancel-dashboard-changes';
const {
serverFunction
} = useServerFunctions();
// Sync state when initialLayout prop changes (e.g., when query params change and server component re-renders)
useEffect(() => {
if (!isEditing) {
setCurrentLayout(initialLayout);
}
}, [initialLayout, isEditing]);
const saveLayout = useCallback(async () => {
try {
const layoutData = currentLayout.map(item => item.item);
setIsEditing(false);
await setLayoutPreference(layoutData);
} catch {
setIsEditing(true);
toast.error('Failed to save layout');
}
}, [setLayoutPreference, currentLayout]);
const resetLayout = useCallback(async () => {
try {
await setLayoutPreference(null);
const result = await serverFunction({
name: 'get-default-layout',
args: {}
});
setCurrentLayout(result.layout);
setIsEditing(false);
} catch {
toast.error('Failed to reset layout');
}
}, [setLayoutPreference, serverFunction]);
const performCancel = useCallback(() => {
setCurrentLayout(initialLayout);
setIsEditing(false);
}, [initialLayout]);
const cancel = useCallback(() => {
// Check if layout has changed
const hasChanges = currentLayout.length !== initialLayout.length || currentLayout.some((widget, index) => {
const initialWidget = initialLayout[index];
return !initialWidget || widget.item.id !== initialWidget.item.id || widget.item.width !== initialWidget.item.width;
});
// If there are changes, show confirmation modal
if (hasChanges) {
openModal(cancelModalSlug);
} else {
performCancel();
}
}, [currentLayout, initialLayout, openModal, cancelModalSlug, performCancel]);
const moveWidget = useCallback(({
moveFromIndex,
moveToIndex
}) => {
if (moveFromIndex === moveToIndex || moveFromIndex < 0 || moveToIndex < 0) {
return;
}
setCurrentLayout(prev => {
return arrayMove(prev, moveFromIndex, moveToIndex);
});
}, []);
const addWidget = useCallback(widgetSlug => {
if (!isEditing) {
return;
}
const widgetId = `${widgetSlug}-${Date.now()}`;
const widget = widgets.find(widget => widget.slug === widgetSlug);
// Create a new widget instance using RenderWidget
const newWidgetInstance = {
component: React.createElement(RenderWidget, {
widgetId
}),
item: {
id: widgetId,
maxWidth: widget?.maxWidth ?? 'full',
minWidth: widget?.minWidth ?? 'x-small',
width: widget?.minWidth ?? 'x-small'
}
};
setCurrentLayout(prev => [...prev, newWidgetInstance]);
// Scroll to the newly added widget after it's rendered and highlight it
setTimeout(() => {
const element = document.getElementById(widgetId);
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
// Add highlight animation to the widget element
const widget = element.closest('.widget');
if (widget) {
widget.classList.add('widget--highlight');
// Remove the class after animation completes (1.5s fade out)
setTimeout(() => {
widget.classList.remove('widget--highlight');
}, 1500);
}
}
}, 100);
}, [isEditing, widgets]);
const deleteWidget = useCallback(widgetId => {
if (!isEditing) {
return;
}
setCurrentLayout(prev => prev.filter(item => item.item.id !== widgetId));
}, [isEditing]);
const resizeWidget = useCallback((widgetId, newWidth) => {
if (!isEditing) {
return;
}
setCurrentLayout(prev => prev.map(item => item.item.id === widgetId ? {
...item,
item: {
...item.item,
width: newWidth
}
} : item));
}, [isEditing]);
const cancelModal = React.createElement(ConfirmationModal, {
body: 'You have unsaved changes to your dashboard layout. Are you sure you want to discard them?',
confirmLabel: 'Discard',
heading: 'Discard changes?',
modalSlug: cancelModalSlug,
onConfirm: performCancel
});
return {
addWidget,
cancel,
cancelModal,
currentLayout,
deleteWidget,
isEditing,
moveWidget,
resetLayout,
resizeWidget,
saveLayout,
setIsEditing
};
}
function useSetLayoutPreference() {
const {
setPreference
} = usePreferences();
return useCallback(async layout => {
await setPreference(PREFERENCE_KEYS.DASHBOARD_LAYOUT, {
layouts: layout
}, false);
}, [setPreference]);
}
//# sourceMappingURL=useDashboardLayout.js.map