feat: add duplication action for items (#926)
This commit is contained in:
@@ -45,6 +45,10 @@ interface CreateItem {
|
||||
kind: WidgetKind;
|
||||
}
|
||||
|
||||
interface DuplicateItem {
|
||||
itemId: string;
|
||||
}
|
||||
|
||||
export const useItemActions = () => {
|
||||
const { updateBoard } = useUpdateBoard();
|
||||
|
||||
@@ -87,6 +91,38 @@ export const useItemActions = () => {
|
||||
[updateBoard],
|
||||
);
|
||||
|
||||
const duplicateItem = useCallback(
|
||||
({ itemId }: DuplicateItem) => {
|
||||
updateBoard((previous) => {
|
||||
const itemToDuplicate = previous.sections
|
||||
.flatMap((section) => section.items)
|
||||
.find((item) => item.id === itemId);
|
||||
|
||||
if (!itemToDuplicate) return previous;
|
||||
|
||||
const newItem = {
|
||||
...itemToDuplicate,
|
||||
id: createId(),
|
||||
yOffset: undefined,
|
||||
xOffset: undefined,
|
||||
} satisfies Omit<Item, "yOffset" | "xOffset"> & { yOffset?: number; xOffset?: number };
|
||||
|
||||
return {
|
||||
...previous,
|
||||
sections: previous.sections.map((section) => {
|
||||
// Return same section if item is not in it
|
||||
if (!section.items.some((item) => item.id === itemId)) return section;
|
||||
return {
|
||||
...section,
|
||||
items: section.items.concat(newItem as unknown as Item),
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
},
|
||||
[updateBoard],
|
||||
);
|
||||
|
||||
const updateItemOptions = useCallback(
|
||||
({ itemId, newOptions }: UpdateItemOptions) => {
|
||||
updateBoard((previous) => {
|
||||
@@ -258,6 +294,7 @@ export const useItemActions = () => {
|
||||
updateItemOptions,
|
||||
updateItemAdvancedOptions,
|
||||
updateItemIntegrations,
|
||||
duplicateItem,
|
||||
createItem,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { RefObject } from "react";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { ActionIcon, Card, Menu } from "@mantine/core";
|
||||
import { useElementSize } from "@mantine/hooks";
|
||||
import { IconDotsVertical, IconLayoutKanban, IconPencil, IconTrash } from "@tabler/icons-react";
|
||||
import { IconCopy, IconDotsVertical, IconLayoutKanban, IconPencil, IconTrash } from "@tabler/icons-react";
|
||||
import { QueryErrorResetBoundary } from "@tanstack/react-query";
|
||||
import combineClasses from "clsx";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
@@ -147,7 +147,8 @@ const ItemMenu = ({
|
||||
const { openModal } = useModalAction(WidgetEditModal);
|
||||
const { openConfirmModal } = useConfirmModal();
|
||||
const [isEditMode] = useEditMode();
|
||||
const { updateItemOptions, updateItemAdvancedOptions, updateItemIntegrations, removeItem } = useItemActions();
|
||||
const { updateItemOptions, updateItemAdvancedOptions, updateItemIntegrations, duplicateItem, removeItem } =
|
||||
useItemActions();
|
||||
const { data: integrationData, isPending } = clientApi.integration.all.useQuery();
|
||||
const currentDefinition = useMemo(() => widgetImports[item.kind].definition, [item.kind]);
|
||||
|
||||
@@ -216,6 +217,9 @@ const ItemMenu = ({
|
||||
{tItem("action.edit")}
|
||||
</Menu.Item>
|
||||
<Menu.Item leftSection={<IconLayoutKanban size={16} />}>{tItem("action.move")}</Menu.Item>
|
||||
<Menu.Item leftSection={<IconCopy size={16} />} onClick={() => duplicateItem({ itemId: item.id })}>
|
||||
{tItem("action.duplicate")}
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Label c="red.6">{t("common.dangerZone")}</Menu.Label>
|
||||
<Menu.Item c="red.6" leftSection={<IconTrash size={16} />} onClick={openRemoveModal}>
|
||||
|
||||
@@ -662,6 +662,7 @@ export default {
|
||||
import: "Import item",
|
||||
edit: "Edit item",
|
||||
move: "Move item",
|
||||
duplicate: "Duplicate item",
|
||||
remove: "Remove item",
|
||||
},
|
||||
menu: {
|
||||
|
||||
Reference in New Issue
Block a user