diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index f7810d3f..322c63df 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -113,7 +113,8 @@ "invalid_file_type": "Invalid file type. Please select a file in the supported format.", "select_category": "Select a flow", "logout_failed": "Something went wrong during logout", - "duplicate_labels_not_allowed": "Duplicate labels are not allowed" + "duplicate_labels_not_allowed": "Duplicate labels are not allowed", + "duplicate_block_error": "Something went wrong while duplicating block" }, "menu": { "terms": "Terms of Use", diff --git a/frontend/public/locales/fr/translation.json b/frontend/public/locales/fr/translation.json index e257e4fd..fa45a278 100644 --- a/frontend/public/locales/fr/translation.json +++ b/frontend/public/locales/fr/translation.json @@ -113,7 +113,8 @@ "invalid_file_type": "Type de fichier invalide. Veuillez choisir un fichier dans un format pris en charge.", "select_category": "Sélectionner une catégorie", "logout_failed": "Une erreur s'est produite lors de la déconnexion", - "duplicate_labels_not_allowed": "Les étiquettes en double ne sont pas autorisées" + "duplicate_labels_not_allowed": "Les étiquettes en double ne sont pas autorisées", + "duplicate_block_error": "Une erreur est survenue lors de la duplication du bloc" }, "menu": { "terms": "Conditions d'utilisation", diff --git a/frontend/src/components/visual-editor/v2/Diagrams.tsx b/frontend/src/components/visual-editor/v2/Diagrams.tsx index 70b22f6e..e0588e49 100644 --- a/frontend/src/components/visual-editor/v2/Diagrams.tsx +++ b/frontend/src/components/visual-editor/v2/Diagrams.tsx @@ -1,12 +1,12 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -import { Add, MoveUp } from "@mui/icons-material"; +import { Add, ContentCopyRounded, MoveUp } from "@mui/icons-material"; import DeleteIcon from "@mui/icons-material/Delete"; import EditIcon from "@mui/icons-material/Edit"; import FitScreenIcon from "@mui/icons-material/FitScreen"; @@ -37,6 +37,7 @@ import { ConfirmDialogBody } from "@/app-components/dialogs"; import { CategoryFormDialog } from "@/components/categories/CategoryFormDialog"; import { BlockMoveFormDialog } from "@/components/visual-editor/BlockMoveFormDialog"; import { isSameEntity } from "@/hooks/crud/helpers"; +import { useCreate } from "@/hooks/crud/useCreate"; import { useDeleteFromCache } from "@/hooks/crud/useDelete"; import { useDeleteMany } from "@/hooks/crud/useDeleteMany"; import { useFind } from "@/hooks/crud/useFind"; @@ -46,6 +47,7 @@ import { useUpdateMany } from "@/hooks/crud/useUpdateMany"; import useDebouncedUpdate from "@/hooks/useDebouncedUpdate"; import { useDialogs } from "@/hooks/useDialogs"; import { useSearch } from "@/hooks/useSearch"; +import { useToast } from "@/hooks/useToast"; import { useTranslate } from "@/hooks/useTranslate"; import { EntityType, Format, QueryType, RouterType } from "@/services/types"; import { IBlock } from "@/types/block.types"; @@ -80,6 +82,15 @@ const Diagrams = () => { const { searchPayload } = useSearch({ $eq: [{ category: selectedCategoryId }], }); + const { toast } = useToast(); + const { mutate: duplicateBlock, isLoading: isDuplicatingBlock } = useCreate( + EntityType.BLOCK, + { + onError: () => { + toast.error(t("message.duplicate_block_error")); + }, + }, + ); const { data: categories } = useFind( { entity: EntityType.CATEGORY }, { @@ -172,6 +183,32 @@ const Diagrams = () => { enabled: !!selectedCategoryId, }, ); + const handleDuplicateBlock = () => { + const block = getBlockFromCache(selectedEntities[0]); + + if (!block) { + return; + } + const { + attachedBlock: _attachedBlock, + nextBlocks: _nextBlocks, + previousBlocks: _previousBlocks, + id: _id, + createdAt: _createdAt, + updatedAt: _updatedAt, + position, + ...duplicateBlockDto + } = block; + + duplicateBlock({ + ...duplicateBlockDto, + name: `${block.name} (Copy)`, + position: { + x: position.x + 100, + y: position.y + 100, + }, + }); + }; useEffect(() => { // Case when categories are already cached @@ -513,6 +550,11 @@ const Diagrams = () => { ); } }; + const selectedEntities = getSelectedIds(); + const shouldDisableDuplicateButton = + selectedEntities.length !== 1 || + selectedEntities[0]?.length !== 24 || + isDuplicatingBlock; return (
{ > {t("button.move")} +