uniadaptiveLTI / uniadaptiveLTI-Front

GNU General Public License v3.0
0 stars 2 forks source link

Flow fixes #5

Closed Javiondox closed 1 year ago

Javiondox commented 1 year ago

Suggested edit:

diff --git a/components/Aside.js b/components/Aside.js
index 3ce1b39..f45448d 100644
--- a/components/Aside.js
+++ b/components/Aside.js
@@ -18,8 +18,9 @@ import {
    VersionJsonContext,
    SettingsContext,
    BlocksDataContext,
+   ReactFlowInstanceContext,
 } from "../pages/_app.js";
-import { getUpdatedBlocksData } from "./Utils.js";
+import { getUpdatedArrayById } from "./Utils.js";

 export default function Aside({ className, closeBtn, svgExists }) {
    const [expandedContent, setExpandedContent] = useState(true);
@@ -61,6 +62,9 @@ export default function Aside({ className, closeBtn, svgExists }) {
    const { versionJson, setVersionJson } = useContext(VersionJsonContext);
    const { currentBlocksData, setCurrentBlocksData } =
        useContext(BlocksDataContext);
+   const { reactFlowInstance, setReactFlowInstance } = useContext(
+       ReactFlowInstanceContext
+   );

    const { expandedAside, setExpandedAside } = useContext(ExpandedAsideContext);

@@ -263,7 +267,27 @@ export default function Aside({ className, closeBtn, svgExists }) {
     * Updates the selected block with the values from the specified DOM elements.
     */
    const updateBlock = () => {
+       console.log(blockSelected);
        setCurrentBlocksData(
+           getUpdatedArrayById(
+               {
+                   ...blockSelected,
+                   ...{
+                       id: blockSelected.id,
+                       type: typeDOM.current.value,
+                       data: {
+                           label: titleDOM.current.value,
+                           resource: optionsDOM.current.value,
+                           children: blockSelected.children,
+                           identation: blockSelected.identation,
+                       },
+                   },
+               },
+               currentBlocksData
+           )
+       );
+
+       /*setCurrentBlocksData(
            getUpdatedBlocksData(
                {
                    id: blockSelected.id,
@@ -277,7 +301,7 @@ export default function Aside({ className, closeBtn, svgExists }) {
                },
                currentBlocksData
            )
-       );
+       );*/
        if (autoHideAside) {
            setExpandedAside(false);
        }
@@ -553,9 +577,9 @@ export default function Aside({ className, closeBtn, svgExists }) {

                                            {blockSelected.children &&
                                                blockSelected.children.map((childId) => {
-                                                   const selectedChild = currentBlocksData.find(
-                                                       (child) => child.id === childId
-                                                   );
+                                                   const selectedChild = reactFlowInstance
+                                                       .getNodes()
+                                                       .find((child) => child.id === childId);

                                                    return (
                                                        <option
diff --git a/components/BlockCanvas.js b/components/BlockCanvas.js
index f0f1117..9bf649d 100644
--- a/components/BlockCanvas.js
+++ b/components/BlockCanvas.js
@@ -28,7 +28,7 @@ import ConditionModal from "./flow/conditions/ConditionModal.js";
 import BlockFlow from "./BlockFlow.js";
 import { toast } from "react-toastify";
 import { useHotkeys } from "react-hotkeys-hook";
-import { uniqueId, getBlockByNode } from "./Utils.js";
+import { uniqueId, getBlockByNodeDOM, getBlocksByNodesDOM } from "./Utils.js";

 export const BlockOriginContext = createContext();
 export const PaneContextMenuPositionContext = createContext();
@@ -45,7 +45,7 @@ function addEventListeners(element, events) {
    });
 }

-const reservedBlocksTypes = ["start", "end", "fragment"];
+const reservedBlocksTypes = ["start", "end"];

 /**
  * Checks if there are any reserved blocks in an array of DOM elements
@@ -115,7 +115,7 @@ export default function BlockCanvas() {

    //This will be given by the back

-   const currentBlocksDataRef = useRef(currentBlocksData);
+   //const currentBlocksDataRef = useRef(reactFlowInstance.getNodes());

    /** Client-side */

@@ -143,19 +143,21 @@ export default function BlockCanvas() {
    });

    useEffect(() => {
-       if (cMBlockData)
+       if (cMBlockData && reactFlowInstance)
            if (!Array.isArray(cMBlockData)) {
-               let newcurrentBlocksData = [...currentBlocksData];
+               let newcurrentBlocksData = [...reactFlowInstance.getNodes()];
                newcurrentBlocksData[
-                   currentBlocksData.findIndex((b) => b.id == cMBlockData.id)
+                   reactFlowInstance.getNodes().findIndex((b) => b.id == cMBlockData.id)
                ] = cMBlockData;
-               setCurrentBlocksData(newcurrentBlocksData);
+               reactFlowInstance.setNodes(newcurrentBlocksData);
            } else {
-               const newcurrentBlocksData = currentBlocksData.map((block) => {
-                   const newBlock = cMBlockData.find((b) => b.id === block.id);
-                   return newBlock ? { ...block, ...newBlock } : block;
-               });
-               setCurrentBlocksData(newcurrentBlocksData);
+               const newcurrentBlocksData = reactFlowInstance
+                   .getNodes()
+                   .map((block) => {
+                       const newBlock = cMBlockData.find((b) => b.id === block.id);
+                       return newBlock ? { ...block, ...newBlock } : block;
+                   });
+               reactFlowInstance.setNodes(newcurrentBlocksData);
            }
    }, [cMBlockData]);

@@ -163,7 +165,7 @@ export default function BlockCanvas() {

    useEffect(() => {
        if (deletedEdge.id) {
-           let updatedBlocksArray = currentBlocksData.slice();
+           let updatedBlocksArray = reactFlowInstance.getNodes().slice();

            const blockNodeDelete = updatedBlocksArray.find(
                (obj) => obj.id === deletedEdge.source
@@ -191,15 +193,15 @@ export default function BlockCanvas() {
                }
            }

-           setCurrentBlocksData(updatedBlocksArray);
+           reactFlowInstance.setNodes(updatedBlocksArray);
        }
    }, [deletedEdge]);

    const deleteBlocks = (blocks) => {
        if (!Array.isArray(blocks)) {
-           const deletedBlockArray = currentBlocksData.filter(
-               (b) => b.id !== blocks.id
-           );
+           const deletedBlockArray = reactFlowInstance
+               .getNodes()
+               .filter((b) => b.id !== blocks.id);
            const deletedRelatedChildrenArray = deleteRelatedChildrenById(
                blocks.id,
                deletedBlockArray
@@ -210,10 +212,10 @@ export default function BlockCanvas() {
                deletedRelatedChildrenArray
            );

-           setCurrentBlocksData(deleteRelatedConditionsArray);
+           reactFlowInstance.setNodes(deleteRelatedConditionsArray);
        } else {
            if (blocks.length > 0) {
-               let updatedBlocksArray = currentBlocksData.slice();
+               let updatedBlocksArray = reactFlowInstance.getNodes().slice();

                blocks.forEach((b) => {
                    const id = b.id;
@@ -229,7 +231,7 @@ export default function BlockCanvas() {
                    );
                });

-               setCurrentBlocksData(updatedBlocksArray);
+               reactFlowInstance.setNodes(updatedBlocksArray);
            }
        }
    };
@@ -320,10 +322,6 @@ export default function BlockCanvas() {
        ]);
    }, []);

-   useLayoutEffect(() => {
-       currentBlocksDataRef.current = currentBlocksData;
-   }, [expandedAside, settings, currentBlocksData]);
-
    /**
     * Handles the context menu position, positioning it.
     * @param {Event} e
@@ -344,9 +342,7 @@ export default function BlockCanvas() {
                        e.preventDefault();
                        setCMX(e.clientX - bounds.left);
                        setCMY(e.clientY - bounds.top);
-                       let block = currentBlocksDataRef.current.find(
-                           (e) => e.id == selectedBlock.id
-                       );
+                       let block = currentBlocksData.find((e) => e.id == selectedBlock.id);
                        setCMBlockData(block);
                        if (block.type == "start" || block.type == "end") {
                            setCMContainsReservedNodes(true);
@@ -367,13 +363,19 @@ export default function BlockCanvas() {
                        document.getElementsByClassName("selected").length > 1)
                ) {
                    e.preventDefault();
+                   const selectedNodes = [
+                       ...document.querySelectorAll(".react-flow__node.selected"),
+                   ];
                    setCMX(e.clientX - bounds.left);
                    setCMY(e.clientY - bounds.top);
                    setContextMenuOrigin("nodesselection");
+
+                   console.log(selectedNodes);
+                   setCMBlockData(
+                       getBlocksByNodesDOM(selectedNodes, reactFlowInstance.getNodes())
+                   );
                    setCMContainsReservedNodes(
-                       thereIsReservedBlocksInArray([
-                           ...document.querySelectorAll(".react-flow__node.selected"),
-                       ])
+                       thereIsReservedBlocksInArray(selectedNodes)
                    );
                    setShowContextualMenu(true);
                }
@@ -388,22 +390,23 @@ export default function BlockCanvas() {
    const handleBlockCopy = (blockData = []) => {
        setShowContextualMenu(false);

-       let blockDataSet = new Set(blockData);
+       let blockDataSet;
        if (blockData.length > 0) {
            blockDataSet = new Set(blockData);
        } else {
            blockDataSet = new Set();
        }

-       const selectedNodes = document.querySelectorAll(
-           ".react-flow__node.selected"
-       );
+       const selectedNodes = [
+           ...document.querySelectorAll(".react-flow__node.selected"),
+       ];

        for (const node of selectedNodes) {
            const id = node.dataset?.id;
+           console.log(id);
            if (id) {
                const blockDataMap = new Map(
-                   currentBlocksData.map((block) => [block.id, block])
+                   reactFlowInstance.getNodes().map((block) => [block.id, block])
                );
                const block = blockDataMap.get(id);
                if (block) {
@@ -469,7 +472,10 @@ export default function BlockCanvas() {
            handleDeleteBlockSelection();
        } else {
            if (selectedNodes.length == 1) {
-               blockData = getBlockByNode(selectedNodes[0], currentBlocksData);
+               blockData = getBlockByNodeDOM(
+                   selectedNodes[0],
+                   reactFlowInstance.getNodes()
+               );
            }
            handleDeleteBlock(blockData);
        }
@@ -492,7 +498,7 @@ export default function BlockCanvas() {
            y: preferredPosition.y - reactFlowBounds.top,
        });

-       const asideOffset = expanded
+       const asideOffset = expandedAside
            ? Math.floor(asideBounds.width / 125) * 125
            : 0;

@@ -536,9 +542,9 @@ export default function BlockCanvas() {
        setShowContextualMenu(false);

        if (Object.keys(newBlockCreated).length !== 0) {
-           let newcurrentBlocksData = [...currentBlocksData];
+           let newcurrentBlocksData = [...reactFlowInstance.getNodes()];
            newcurrentBlocksData.push(newBlockCreated);
-           setCurrentBlocksData(newcurrentBlocksData);
+           reactFlowInstance.setNodes(newcurrentBlocksData);
        }
    };

@@ -554,7 +560,7 @@ export default function BlockCanvas() {
            y: preferredPosition.y - reactFlowBounds.top,
        });

-       const asideOffset = expanded
+       const asideOffset = expandedAside
            ? Math.floor(asideBounds.width / 125) * 125
            : 0;

@@ -568,8 +574,8 @@ export default function BlockCanvas() {
        });
        setShowContextualMenu(false);

-       let newcurrentBlocksData = [...currentBlocksData, ...newBlocks];
-       setCurrentBlocksData(newcurrentBlocksData);
+       let newcurrentBlocksData = [...reactFlowInstance.getNodes(), ...newBlocks];
+       reactFlowInstance.setNodes(newcurrentBlocksData);
    };

    const handleDeleteBlock = (blockData) => {
@@ -585,7 +591,9 @@ export default function BlockCanvas() {
        );
        const blockDataArray = [];
        for (let node of selectedNodes) {
-           blockDataArray.push(getBlockByNode(node, currentBlocksData));
+           blockDataArray.push(
+               getBlockByNodeDOM(node, reactFlowInstance.getNodes())
+           );
        }
        deleteBlocks(blockDataArray);
    };
@@ -596,7 +604,10 @@ export default function BlockCanvas() {
            undefined;

        end =
-           end || currentBlocksData.find((block) => block.id === currentSelectionId);
+           end ||
+           reactFlowInstance
+               .getNodes()
+               .find((block) => block.id === currentSelectionId);

        setShowContextualMenu(false);

@@ -613,7 +624,7 @@ export default function BlockCanvas() {
                return;
            }

-           const newBlocksData = [...currentBlocksData];
+           const newBlocksData = [...reactFlowInstance.getNodes()];
            const bI = newBlocksData.findIndex((block) => block.id == origin.id);
            if (newBlocksData[bI].children) {
                const alreadyAChildren = newBlocksData[bI].children.includes(end.id);
@@ -636,11 +647,11 @@ export default function BlockCanvas() {
                newBlocksData[bI].children = [end.id];
            }
            setBlockOrigin();
-           setCurrentBlocksData(newBlocksData);
+           reactFlowInstance.setNodes(newBlocksData);
        } else {
-           const starterBlock = currentBlocksData.find(
-               (block) => block.id == currentSelectionId
-           );
+           const starterBlock = reactFlowInstance
+               .getNodes()
+               .find((block) => block.id == currentSelectionId);

            setBlockOrigin(starterBlock);

@@ -663,7 +674,10 @@ export default function BlockCanvas() {

        let newCMBlockData = undefined;
        if (selectedNodes.length == 1) {
-           newCMBlockData = getBlockByNode(selectedNodes[0], currentBlocksData);
+           newCMBlockData = getBlockByNodeDOM(
+               selectedNodes[0],
+               reactFlowInstance.getNodes()
+           );
        }

        if (newCMBlockData || cMBlockData) {
@@ -723,7 +737,7 @@ export default function BlockCanvas() {
                            <ConditionModal
                                blockData={cMBlockData}
                                setBlockData={setCMBlockData}
-                               blocksData={currentBlocksData}
+                               blocksData={reactFlowInstance.getNodes()}
                                showConditionsModal={showConditionsModal}
                                setShowConditionsModal={setShowConditionsModal}
                            />
diff --git a/components/BlockFlow.js b/components/BlockFlow.js
index 39e2a54..118bbb8 100644
--- a/components/BlockFlow.js
+++ b/components/BlockFlow.js
@@ -42,7 +42,7 @@ import {
 } from "@fortawesome/free-solid-svg-icons";
 import { PaneContextMenuPositionContext } from "./BlockCanvas.js";
 import { Button } from "react-bootstrap";
-import { getBlockById, getNodeById, getUpdatedBlocksData } from "./Utils.js";
+import { getBlockById, getNodeById, getUpdatedArrayById } from "./Utils.js";

 const minimapStyle = {
    height: 120,
@@ -286,10 +286,10 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => {

    const handleNodeDragStart = (event, node) => {
        setShowContextualMenu(false);
-       let inFragment = true;
-       getBlockById(node.id, currentBlocksData).parent
-           ? null
-           : (inFragment = false);
+       let inFragment = false;
+       if (node.parentNode != undefined) {
+           inFragment = true;
+       }
        setSnapToGrid(!inFragment);
        draggedNodePosition.current = node.position;
    };
@@ -305,25 +305,12 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => {
            (draggedNodesPosition.current.x !== nodes[0].position.x ||
                draggedNodesPosition.current.y !== nodes[0].position.y)
        ) {
+           //FIXME:LOOK IF NECESSARY
            const selectionBlock = nodes.map((b) => ({
-               id: b.id,
-               x: b.position.x,
-               y: b.position.y,
-               type: b.type,
-               title: b.data.label,
-               parent: b.parentNode,
-               style: b.data.style,
-               innerNodes: b.data.innerNodes,
-               expanded: b.data.expanded,
-               draggable: b.draggable,
-               children: b.data.children,
-               identation: b.data.identation,
-               conditions: b.data.conditions,
-               order: b.data.order,
-               unit: b.data.unit,
+               b,
            }));
-           setCurrentBlocksData(
-               getUpdatedBlocksData(selectionBlock, currentBlocksData)
+           reactFlowInstance.setNodes(
+               getUpdatedArrayById(selectionBlock, reactFlowInstance.getNodes())
            );
        }
    };
@@ -334,29 +321,9 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => {
                draggedNodePosition.current.x !== node.position.x ||
                draggedNodePosition.current.y !== node.position.y
            ) {
-               setCurrentBlocksData(
-                   getUpdatedBlocksData(
-                       {
-                           id: node.id,
-                           x: node.position.x,
-                           y: node.position.y,
-                           type: node.type,
-                           title: node.data.label,
-                           parent: node.parentNode,
-                           style: node.data.style,
-                           innerNodes: node.data.innerNodes,
-                           expanded: node.data.expanded,
-                           draggable: node.draggable,
-                           children: node.data.children,
-                           identation: node.data.identation,
-                           conditions: node.data.conditions,
-                           order: node.data.order,
-                           unit: node.data.unit,
-                       },
-                       currentBlocksData
-                   )
+               reactFlowInstance.setNodes(
+                   getUpdatedArrayById(node, reactFlowInstance.getNodes())
                );
-               console.log(node);
                setSnapToGrid(true);
            }
        }
@@ -384,8 +351,20 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => {
        }

        setCurrentBlocksData(
-           getUpdatedBlocksData({ ...sourceNode }, currentBlocksData)
+           getUpdatedArrayById({ ...sourceNode }, currentBlocksData)
        );
+
+       //FIXME:
+       setEdges([
+           ...edges,
+           {
+               id: sourceNodeId + "-" + targetNodeId,
+               source: sourceNodeId,
+               target: targetNodeId,
+           },
+       ]);
+
+       console.log(sourceNode);
    };

    useEffect(() => {
@@ -412,30 +391,16 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => {
    useEffect(() => {
        setNewInitialNodes(
            map?.map((block) => ({
-               id: block.id,
-               type: block.type,
-               parentNode: block.parent,
+               ...block,
                extent: block.parent ? "parent" : undefined,
                //draggable: block.parent ? false : true,
-               data: {
-                   label: block.title,
-                   identation: block.identation,
-                   children: block.children,
-                   conditions: block.conditions,
-                   style: block.style,
-                   innerNodes: block.innerNodes,
-                   expanded: block.expanded,
-                   order: block.order,
-                   unit: block.unit,
-               },
-               position: { x: block.x, y: block.y },
            }))
        );

        setNewInitialEdges(
            map?.flatMap((parent) => {
-               if (parent.children) {
-                   return parent.children.map((child) => {
+               if (parent.data.children) {
+                   return parent.data.children.map((child) => {
                        return {
                            id: `${parent.id}-${child}`,
                            source: parent.id,
@@ -456,22 +421,24 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => {

    // we are using a bit of a shortcut here to adjust the edge type
    // this could also be done with a custom edge for example
-   const edgesWithUpdatedTypes = edges.map((edge) => {
-       if (edge.sourceHandle) {
-           const edgeType = nodes.find((node) => node.type === "custom").data
-               .selects[edge.sourceHandle];
-           edge.type = edgeType;
-       }
+   const edgesWithUpdatedTypes = edges
+       ? edges.map((edge) => {
+               if (edge.sourceHandle) {
+                   const edgeType = nodes.find((node) => node.type === "custom").data
+                       .selects[edge.sourceHandle];
+                   edge.type = edgeType;
+               }

-       //FIXME: Does nothing
-       if (edge.conditions != undefined) {
-           for (let condition of cedge.conditions) {
-               edge.label = "" + condition.operand + condition.objective;
-           }
-       }
+               //FIXME: Does nothing
+               if (edge.conditions != undefined) {
+                   for (let condition of cedge.conditions) {
+                       edge.label = "" + condition.operand + condition.objective;
+                   }
+               }

-       return edge;
-   });
+               return edge;
+         })
+       : edges;

    return (
        <div ref={reactFlowWrapper} style={{ height: "100%", width: "100%" }}>
@@ -502,6 +469,7 @@ const OverviewFlow = ({ map, deleteBlocks, setShowContextualMenu }, ref) => {
                deleteKeyCode={["Backspace", "Delete", "d"]}
                multiSelectionKeyCode={["Shift"]}
                selectionKeyCode={["Shift"]}
+               zoomOnDoubleClick={false}
            >
                {minimap && (
                    <MiniMap
diff --git a/components/ContextualMenu.js b/components/ContextualMenu.js
index 77aad4d..0bf818d 100644
--- a/components/ContextualMenu.js
+++ b/components/ContextualMenu.js
@@ -1,10 +1,14 @@
 import styles from "@components/styles/BlockContextualMenu.module.css";
-import { forwardRef, useContext } from "react";
-import { ExpandedAsideContext } from "@components/pages/_app";
+import { forwardRef, useContext, useState, useLayoutEffect } from "react";
+import {
+   BlocksDataContext,
+   ExpandedAsideContext,
+} from "@components/pages/_app";
 import { BlockOriginContext } from "./BlockCanvas";
 import CMBlockMenu from "./flow/contextualmenu/CMBlockMenu";
 import CMPaneMenu from "./flow/contextualmenu/CMPaneMenu";
 import CMSelectionMenu from "./flow/contextualmenu/CMSelectionMenu";
+import { getBlocksByNodesDOM } from "./Utils";

 export default forwardRef(function ContextualMenu(
    {
@@ -13,7 +17,6 @@ export default forwardRef(function ContextualMenu(
        showContextualMenu,
        blockData,
        setShowContextualMenu,
-       setShowConditionsModal,
        contextMenuOrigin,
        containsReservedNodes,
        handleBlockCopy,
@@ -28,104 +31,103 @@ export default forwardRef(function ContextualMenu(
    ref
 ) {
    const { blockOrigin, setBlockOrigin } = useContext(BlockOriginContext);
-
+   const { currentBlocksData, setCurrentBlocksData } =
+       useContext(BlocksDataContext);
    const { expanded: expandedAside } = useContext(ExpandedAsideContext);

    const asideBounds = expandedAside
        ? document.getElementsByTagName("aside")[0]?.getBoundingClientRect()
        : 0;

+   const [enableEditPreconditions, setEnableEditPreconditions] = useState(true);
+   const [enableCreateRelation, setEnableCreateRelation] = useState(true);
+   const [enableCreateFragment, setEnableCreateFragment] = useState(true);
+   const [enableCut, setEnableCut] = useState(true);
+   const [enableCopy, setEnableCopy] = useState(true);
+   const [enablePaste, setEnablePaste] = useState(true);
+   const [enableCreate, setEnableCreate] = useState(true);
+   const [enableDelete, setEnableDelete] = useState(true);
+
+   useLayoutEffect(() => {
+       if (containsReservedNodes) {
+           setEnableEditPreconditions(false);
+           setEnableCreateFragment(false);
+           setEnableDelete(false);
+           setEnableCut(false);
+           setEnableCopy(false);
+       } else {
+           setEnableDelete(true);
+           setEnableCut(true);
+           setEnableCopy(true);
+
+           if (blockData) {
+               if (Array.isArray(blockData)) {
+                   const blocks = getBlocksByNodesDOM(
+                       blockData,
+                       reactFlowInstance.getNodes()
+                   );
+                   const fragment = blocks.find((block) => block.type == "fragment");
+                   if (fragment) {
+                       setEnableCreateFragment(false);
+                   } else {
+                       setEnableCreateFragment(true);
+                   }
+               }
+           }
+       }
+   }, [blockData]);
+
    return (
        <>
            {showContextualMenu && (
                <>
-                   {containsReservedNodes ? (
-                       <div
-                           ref={ref}
-                           style={{
-                               top: `${y}px`,
-                               left: `${x + (asideBounds && asideBounds.width)}px`,
-                           }}
-                           className={styles.cM + " "}
-                       >
-                           {contextMenuOrigin == "block" && (
-                               <CMBlockMenu
-                                   handleShow={handleShow}
-                                   blockOrigin={blockOrigin}
-                                   blockData={blockData}
-                                   setBlockOrigin={setBlockOrigin}
-                                   setShowContextualMenu={setShowContextualMenu}
-                                   handleDeleteBlock={handleDeleteBlock}
-                                   handleNewRelation={handleNewRelation}
-                                   handleBlockCopy={handleBlockCopy}
-                                   handleBlockCut={handleBlockCut}
-                                   EnableEditPreconditions={false}
-                                   EnableCreateRelation={true}
-                                   EnableCut={false}
-                                   EnableCopy={false}
-                                   EnableDelete={false}
-                               />
-                           )}
-                           {contextMenuOrigin == "nodesselection" && (
-                               <CMSelectionMenu
-                                   handleDeleteBlockSelection={handleDeleteBlockSelection}
-                                   handleBlockCut={handleBlockCut}
-                                   handleBlockCopy={handleBlockCopy}
-                                   EnableCreateFragment={false}
-                                   EnableCut={false}
-                                   EnableCopy={false}
-                                   EnableDelete={false}
-                               />
-                           )}
-                       </div>
-                   ) : (
-                       <div
-                           ref={ref}
-                           style={{
-                               top: `${y}px`,
-                               left: `${x + (asideBounds && asideBounds.width)}px`,
-                           }}
-                           className={styles.cM + " "}
-                       >
-                           {contextMenuOrigin == "pane" && (
-                               <CMPaneMenu
-                                   createBlock={createBlock}
-                                   handleBlockPaste={handleBlockPaste}
-                                   EnableCreate={true}
-                                   EnablePaste={true}
-                               />
-                           )}
-                           {contextMenuOrigin == "block" && (
-                               <CMBlockMenu
-                                   handleShow={handleShow}
-                                   blockOrigin={blockOrigin}
-                                   blockData={blockData}
-                                   setBlockOrigin={setBlockOrigin}
-                                   setShowContextualMenu={setShowContextualMenu}
-                                   handleDeleteBlock={handleDeleteBlock}
-                                   handleNewRelation={handleNewRelation}
-                                   handleBlockCopy={handleBlockCopy}
-                                   handleBlockCut={handleBlockCut}
-                                   EnableEditPreconditions={true}
-                                   EnableCreateRelation={true}
-                                   EnableCut={true}
-                                   EnableCopy={true}
-                                   EnableDelete={true}
-                               />
-                           )}
-                           {contextMenuOrigin == "nodesselection" && (
-                               <CMSelectionMenu
-                                   handleDeleteBlockSelection={handleDeleteBlockSelection}
-                                   handleBlockCut={handleBlockCut}
-                                   handleBlockCopy={handleBlockCopy}
-                                   EnableCreateFragment={true}
-                                   EnableCut={true}
-                                   EnableCopy={true}
-                                   EnableDelete={true}
-                               />
-                           )}
-                       </div>
-                   )}
+                   <div
+                       ref={ref}
+                       style={{
+                           top: `${y}px`,
+                           left: `${x + (asideBounds && asideBounds.width)}px`,
+                       }}
+                       className={styles.cM + " "}
+                   >
+                       {contextMenuOrigin == "pane" && (
+                           <CMPaneMenu
+                               createBlock={createBlock}
+                               handleBlockPaste={handleBlockPaste}
+                               EnableCreate={enableCreate}
+                               EnablePaste={enablePaste}
+                           />
+                       )}
+                       {contextMenuOrigin == "block" && (
+                           <CMBlockMenu
+                               handleShow={handleShow}
+                               blockOrigin={blockOrigin}
+                               blockData={blockData}
+                               setBlockOrigin={setBlockOrigin}
+                               setShowContextualMenu={setShowContextualMenu}
+                               handleDeleteBlock={handleDeleteBlock}
+                               handleNewRelation={handleNewRelation}
+                               handleBlockCopy={handleBlockCopy}
+                               handleBlockCut={handleBlockCut}
+                               EnableEditPreconditions={enableEditPreconditions}
+                               EnableCreateRelation={enableCreateRelation}
+                               EnableCut={enableCut}
+                               EnableCopy={enableCopy}
+                               EnableDelete={enableDelete}
+                           />
+                       )}
+                       {contextMenuOrigin == "nodesselection" && (
+                           <CMSelectionMenu
+                               handleDeleteBlockSelection={handleDeleteBlockSelection}
+                               handleBlockCut={handleBlockCut}
+                               handleBlockCopy={handleBlockCopy}
+                               blocksData={blockData}
+                               EnableCreateFragment={enableCreateFragment}
+                               EnableCut={enableCut}
+                               EnableCopy={enableCopy}
+                               EnableDelete={enableDelete}
+                           />
+                       )}
+                   </div>
                </>
            )}
        </>
diff --git a/components/Header.js b/components/Header.js
index 46efa89..732f2c4 100644
--- a/components/Header.js
+++ b/components/Header.js
@@ -36,6 +36,7 @@ import {
    MSGContext,
    SettingsContext,
    OnlineContext,
+   ReactFlowInstanceContext,
 } from "@components/pages/_app";
 import { toast } from "react-toastify";
 import { notImplemented } from "@components/pages/_app";
@@ -81,6 +82,9 @@ function Header({ closeBtn }, ref) {

    const { expandedAside, setExpandedAside } = useContext(ExpandedAsideContext);

+   const { reactFlowInstance, setReactFlowInstance } = useContext(
+       ReactFlowInstanceContext
+   );
    const { currentBlocksData, setCurrentBlocksData } =
        useContext(BlocksDataContext);
    const { isOffline } = useContext(OnlineContext);
@@ -147,6 +151,7 @@ function Header({ closeBtn }, ref) {
        if (selectedMap.versions) {
            setSelectedVersion(selectedMap.versions[0]);
            setCurrentBlocksData(selectedMap.versions[0].blocksData);
+           console.log(selectedMap.versions[0].blocksData);
        }
        if (selectedMap.id == -1) {
            setCurrentBlocksData();
@@ -304,6 +309,7 @@ function Header({ closeBtn }, ref) {
        setCurrentBlocksData(
            modifiedMap.versions[selectedMap.versions.length - 1].blocksData
        );
+
        toast(
            `Versión: "Nueva Versión ${modifiedMap.versions.length - 1}" creada`,
            defaultToastSuccess
@@ -392,7 +398,7 @@ function Header({ closeBtn }, ref) {
            newMaps[mapIndex] = modifiedMap;
            setMaps(newMaps);
            setVersions(modifiedMap.versions);
-           setCurrentBlocksData(firstVersion?.blocksData || versions[0]?.blocksData);
+           currentBlocksData(firstVersion?.blocksData || versions[0]?.blocksData);
            toast(`Versión eliminada con éxito.`, defaultToastSuccess);
        } else {
            toast(`No puedes eliminar esta versión.`, defaultToastError);
@@ -401,7 +407,7 @@ function Header({ closeBtn }, ref) {

    const handleBlockDataExport = () => {
        download(
-           encodeURIComponent(JSON.stringify(currentBlocksData)),
+           encodeURIComponent(JSON.stringify(reactFlowInstance.getNodes())),
            `${mapSelected.name}-${selectedVersion.name}-${new Date()
                .toLocaleDateString()
                .replaceAll("/", "-")}.json`,
@@ -456,14 +462,14 @@ function Header({ closeBtn }, ref) {
    }, [mapSelected]);

    useEffect(() => {
-       if (selectedVersion) {
+       if (selectedVersion && reactFlowInstance) {
            if (selectedVersion.id != versionJson.id) {
                resetEdit();
                setCurrentBlocksData(selectedVersion.blocksData);
                resetMapSesion();
            }
        }
-   }, [selectedVersion]);
+   }, [selectedVersion, reactFlowInstance]);

    useEffect(() => {
        if (versions) {
@@ -844,7 +850,7 @@ function Header({ closeBtn }, ref) {
                                                fontFamily: "monospace",
                                            }}
                                        >
-                                           {JSON.stringify(currentBlocksData, null, "\t")}
+                                           {JSON.stringify(reactFlowInstance.getNodes(), null, "\t")}
                                        </code>
                                    </details>
                                </div>
diff --git a/components/Utils.js b/components/Utils.js
index 249e712..1dd2057 100644
--- a/components/Utils.js
+++ b/components/Utils.js
@@ -10,14 +10,14 @@ export const getBlockById = (id, blocksData) => {
    return blocksData.find((block) => block.id == id);
 };

-export const getBlockByNode = (node, blocksData) => {
+export const getBlockByNodeDOM = (node, blocksData) => {
    return blocksData.find((block) => block.id == node.dataset.id);
 };

-export const getBlocksByNodes = (nodes, blocksData) => {
+export const getBlocksByNodesDOM = (nodes, blocksData) => {
    const blockArray = [];
    nodes.forEach((node) => {
-       const block = getBlockByNode(node, blocksData);
+       const block = getBlockByNodeDOM(node, blocksData);
        if (block) {
            blockArray.push(block);
        }
@@ -25,17 +25,8 @@ export const getBlocksByNodes = (nodes, blocksData) => {
    return blockArray;
 };

-export const getUpdatedBlocksData = (newBlock, blocksData) => {
-   const newBlocks = Array.isArray(newBlock) ? newBlock : [newBlock];
-
-   return blocksData.map((oldBlock) => {
-       const newBlock = newBlocks.find((block) => block.id === oldBlock.id);
-       return newBlock ? { ...oldBlock, ...newBlock } : oldBlock;
-   });
-};
-
 export const getUpdatedBlocksDataFromFlow = (blocksData, reactflowInstance) => {
-   return getUpdatedBlocksData(reactflowInstance.getNodes(), blocksData);
+   return getUpdatedArrayById(reactflowInstance.getNodes(), blocksData);
 };
 //Nodes

@@ -50,7 +41,7 @@ export const getNodeByBlock = (block, reactflowInstance) => {
 export const getNodesByBlocks = (blocks, reactflowInstance) => {
    const nodeArray = [];
    blocks.forEach((block) => {
-       const node = getBlockByNode(block, reactflowInstance);
+       const node = getBlockByNodeDOM(block, reactflowInstance);
        if (node) {
            nodeArray.push(node);
        }
@@ -73,3 +64,20 @@ export const getNodesByProperty = (
 export const getEdgeBetweenNodeIds = (id1, id2, reactflowInstance) => {
    return reactflowInstance.getEdge(`${id1}-${id2}`);
 };
+
+//Generic
+
+/**
+ * Returns a new array with updated entries from the original array.
+ * @param {Object|Object[]} updatedEntry - The entry or entries to be updated in the original array. Each entry must have an id property.
+ * @param {Object[]} originalArray - The original array of entries. Each entry must have an id property.
+ * @returns {Object[]} A new array with the updated entries. If an entry in the original array does not have a matching id in the updatedEntry, it is returned unchanged.
+ */
+export const getUpdatedArrayById = (updatedEntry, originalArray) => {
+   const newBlocks = Array.isArray(updatedEntry) ? updatedEntry : [updatedEntry];
+
+   return originalArray.map((oldEntry) => {
+       const newBlock = newBlocks.find((entry) => entry.id === oldEntry.id);
+       return newBlock ? { ...oldEntry, ...newBlock } : oldEntry;
+   });
+};
diff --git a/components/flow/contextualmenu/CMBlockMenu.js b/components/flow/contextualmenu/CMBlockMenu.js
index 7457d2b..7a76900 100644
--- a/components/flow/contextualmenu/CMBlockMenu.js
+++ b/components/flow/contextualmenu/CMBlockMenu.js
@@ -34,21 +34,23 @@ const Menu = (
 ) => {
    return (
        <div ref={ref} className={styles.cM + " "}>
-           <li>
-               <Button
-                   variant="light"
-                   onClick={handleShow}
-                   disabled={!EnableEditPreconditions}
-               >
-                   <div>
-                       <FontAwesomeIcon icon={faEdit} />
+           {["fragment"].includes(blockData.type) == false && (
+               <li>
+                   <Button
+                       variant="light"
+                       onClick={handleShow}
+                       disabled={!EnableEditPreconditions}
+                   >
                        <div>
-                           Editar precondiciones
-                           <span>SHIFT+E</span>
+                           <FontAwesomeIcon icon={faEdit} />
+                           <div>
+                               Editar precondiciones
+                               <span>SHIFT+E</span>
+                           </div>
                        </div>
-                   </div>
-               </Button>
-           </li>
+                   </Button>
+               </li>
+           )}
            {blockOrigin ? (
                blockOrigin.id == blockData.id ? (
                    <li>
@@ -58,6 +60,7 @@ const Menu = (
                                setBlockOrigin();
                                setShowContextualMenu(false);
                            }}
+                           disabled={!EnableCreateRelation}
                        >
                            <div>
                                <FontAwesomeIcon icon={faDiagramNext} />
@@ -69,23 +72,27 @@ const Menu = (
                        </Button>
                    </li>
                ) : (
-                   <li>
-                       <Button
-                           variant="light"
-                           onClick={() => handleNewRelation(blockOrigin, blockData)}
-                       >
-                           <div>
-                               <FontAwesomeIcon icon={faDiagramNext} />
+                   ["fragment"].includes(blockData.type) == false && (
+                       <li>
+                           <Button
+                               variant="light"
+                               onClick={() => handleNewRelation(blockOrigin, blockData)}
+                               disabled={!EnableCreateRelation}
+                           >
                                <div>
-                                   Terminar relación
-                                   <span>Unir a "{blockOrigin.title}"</span>
+                                   <FontAwesomeIcon icon={faDiagramNext} />
+                                   <div>
+                                       Terminar relación
+                                       <span>Unir a "{blockOrigin.title}"</span>
+                                   </div>
                                </div>
-                           </div>
-                       </Button>
-                   </li>
+                           </Button>
+                       </li>
+                   )
                )
            ) : (
-               [...ActionBlocks, "end"].includes(blockData.type) == false && (
+               [...ActionBlocks, "end", "fragment"].includes(blockData.type) ==
+                   false && (
                    <li>
                        <Button
                            variant="light"
diff --git a/components/flow/contextualmenu/CMSelectionMenu.js b/components/flow/contextualmenu/CMSelectionMenu.js
index b079917..2783470 100644
--- a/components/flow/contextualmenu/CMSelectionMenu.js
+++ b/components/flow/contextualmenu/CMSelectionMenu.js
@@ -16,6 +16,7 @@ const Menu = (
        handleDeleteBlockSelection,
        handleBlockCopy,
        handleBlockCut,
+       blocksData,
        EnableCreateFragment,
        EnableCopy,
        EnableCut,
@@ -43,7 +44,7 @@ const Menu = (
            <li>
                <Button
                    variant="light"
-                   onClick={() => handleBlockCopy()}
+                   onClick={() => handleBlockCopy(blocksData)}
                    disabled={!EnableCopy}
                >
                    <div>
@@ -58,7 +59,7 @@ const Menu = (
            <li>
                <Button
                    variant="light"
-                   onClick={() => handleBlockCut()}
+                   onClick={() => handleBlockCut(blocksData)}
                    disabled={!EnableCut}
                >
                    <div>
@@ -73,7 +74,7 @@ const Menu = (
            <li>
                <Button
                    variant="light"
-                   onClick={handleDeleteBlockSelection}
+                   onClick={() => handleDeleteBlockSelection(blocksData)}
                    disabled={!EnableDelete}
                >
                    <div>
@@ -94,6 +95,7 @@ export default function CMSelectionMenu({
    handleDeleteBlockSelection,
    handleBlockCopy,
    handleBlockCut,
+   blocksData,
    EnableCreateFragment = false,
    EnableCopy = false,
    EnableCut = false,
@@ -114,6 +116,7 @@ export default function CMSelectionMenu({
                handleDeleteBlockSelection={handleDeleteBlockSelection}
                handleBlockCopy={handleBlockCopy}
                handleBlockCut={handleBlockCut}
+               blocksData={blocksData}
                EnableCreateFragment={EnableCreateFragment}
                EnableCut={EnableCut}
                EnableCopy={EnableCopy}
@@ -126,6 +129,7 @@ export default function CMSelectionMenu({
            handleDeleteBlockSelection={handleDeleteBlockSelection}
            handleBlockCopy={handleBlockCopy}
            handleBlockCut={handleBlockCut}
+           blocksData={blocksData}
            EnableCreateFragment={EnableCreateFragment}
            EnableCut={EnableCut}
            EnableCopy={EnableCopy}
diff --git a/components/flow/nodes/ActionNode.js b/components/flow/nodes/ActionNode.js
index 3cbcd03..ffbc3ef 100644
--- a/components/flow/nodes/ActionNode.js
+++ b/components/flow/nodes/ActionNode.js
@@ -96,7 +96,7 @@ const getAriaLabel = () => {
    return (
        getHumanDesc() +
        ", " +
-       blockData.title +
+       blockData.label +
        ", posición en el eje X: " +
        blockData.x +
        ", posición en el eje Y: " +
diff --git a/components/flow/nodes/ElementNode.js b/components/flow/nodes/ElementNode.js
index cd8ec1d..b2dcb5f 100644
--- a/components/flow/nodes/ElementNode.js
+++ b/components/flow/nodes/ElementNode.js
@@ -229,7 +229,7 @@ function ElementNode({ id, xPos, yPos, type, data, isConnectable }) {
        return (
            getHumanDesc() +
            ", " +
-           data.title +
+           data.label +
            ", posición en el eje X: " +
            xPos +
            ", posición en el eje Y: " +
diff --git a/components/flow/nodes/FragmentNode.js b/components/flow/nodes/FragmentNode.js
index 12bbdc7..f8ee16c 100644
--- a/components/flow/nodes/FragmentNode.js
+++ b/components/flow/nodes/FragmentNode.js
@@ -25,11 +25,11 @@ import {
 import { useEffect } from "react";
 import {
    getBlockById,
-   getBlockByNode,
+   getBlockByNodeDOM,
    getEdgeBetweenNodeIds,
    getNodeById,
    getNodesByProperty,
-   getUpdatedBlocksData,
+   getUpdatedArrayById,
    getUpdatedBlocksDataFromFlow,
 } from "@components/components/Utils";
 import FocusTrap from "focus-trap-react";
@@ -92,22 +92,28 @@ function FragmentNode({ id, xPos, yPos, type, data }) {

                if (!expanded) {
                    nodeDOM.classList.add("insideContractedFragment");
-                   const newBlock = getBlockByNode(nodeDOM, currentBlocksData);
+                   const newBlock = getBlockByNodeDOM(
+                       nodeDOM,
+                       reactFlowInstance.getNodes()
+                   );
                    newBlock.x = 0;
                    newBlock.y = 0;
-                   setCurrentBlocksData(
-                       getUpdatedBlocksData(newBlock, currentBlocksData)
+                   reactFlowInstance.setNodes(
+                       getUpdatedArrayById(newBlock, reactFlowInstance.getNodes())
                    );
                } else {
                    nodeDOM.classList.remove("insideContractedFragment");
-                   const newBlock = getBlockByNode(nodeDOM, currentBlocksData);
+                   const newBlock = getBlockByNodeDOM(
+                       nodeDOM,
+                       reactFlowInstance.getNodes()
+                   );
                    const matchingInnerNode = originalChildrenStatus.find(
                        (innerNode) => innerNode.id == newBlock.id
                    );
                    newBlock.x = matchingInnerNode.x;
                    newBlock.y = matchingInnerNode.y;
-                   setCurrentBlocksData(
-                       getUpdatedBlocksData(newBlock, currentBlocksData)
+                   reactFlowInstance.setNodes(
+                       getUpdatedArrayById(newBlock, reactFlowInstance.getNodes())
                    );
                }
            }
@@ -186,10 +192,10 @@ function FragmentNode({ id, xPos, yPos, type, data }) {

        setOriginalChildrenStatus(updatedChildrenBlockData);

-       setCurrentBlocksData(
-           getUpdatedBlocksData(
+       reactFlowInstance.setNodes(
+           getUpdatedArrayById(
                [updatedInfo, ...updatedChildrenBlockData],
-               currentBlocksData
+               reactFlowInstance.getNodes()
            )
        );
    };
@@ -202,7 +208,10 @@ function FragmentNode({ id, xPos, yPos, type, data }) {
                const nodeDOM = document.querySelector(
                    "[data-id=" + childNode.id + "]"
                );
-               const newBlock = getBlockByNode(nodeDOM, currentBlocksData);
+               const newBlock = getBlockByNodeDOM(
+                   nodeDOM,
+                   reactFlowInstance.getNodes()
+               );
                const matchingInnerNode = getNodesByProperty(
                    reactFlowInstance,
                    "parentNode",
@@ -234,7 +243,7 @@ function FragmentNode({ id, xPos, yPos, type, data }) {
        }
        return (
            "Fragmento, " +
-           data.title +
+           data.label +
            ", posición en el eje X: " +
            xPos +
            ", posición en el eje Y: " +
diff --git a/public/resources/dev.json b/public/resources/dev.json
index 7153e7a..bb85a6b 100644
--- a/public/resources/dev.json
+++ b/public/resources/dev.json
@@ -11,357 +11,439 @@
                "blocksData": [
                    {
                        "id": "dev-2A1",
-                       "x": 0,
-                       "y": 0,
+                       "position": {
+                           "x": 0,
+                           "y": 0
+                       },
                        "type": "start",
-                       "title": "Inicio",
-                       "children": ["dev0A1"],
-                       "identation": 1
+                       "data": {
+                           "label": "Inicio",
+                           "children": ["dev0A1"]
+                       }
                    },
                    {
                        "id": "dev-1A1",
-                       "x": 2000,
-                       "y": 0,
+                       "position": {
+                           "x": 2000,
+                           "y": 0
+                       },
                        "type": "end",
-                       "title": "Final",
-                       "identation": 1
+                       "data": {
+                           "label": "Final"
+                       }
                    },
                    {
                        "id": "dev0A1",
-                       "x": 125,
-                       "y": 0,
+                       "position": {
+                           "x": 125,
+                           "y": 0
+                       },
                        "type": "resource",
-                       "title": "Objetivos del curso",
-                       "res": "url/objetivos.pdf",
-                       "children": ["dev1A1"],
-                       "identation": 1,
-                       "order": 1,
-                       "unit": 1
+                       "data": {
+                           "label": "Objetivos del curso",
+                           "res": "url/objetivos.pdf",
+                           "children": ["dev1A1"],
+                           "identation": 1,
+                           "order": 1,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev1A1",
-                       "x": 250,
-                       "y": 0,
+                       "position": {
+                           "x": 250,
+                           "y": 0
+                       },
                        "type": "quiz",
-                       "title": "Aerodinámica",
-                       "children": ["dev2A1", "dev3A1", "dev9A1"],
-                       "identation": 2,
-                       "order": 2,
-                       "unit": 1,
-                       "conditions": {
-                           "type": "conditionsGroup",
-                           "id": "3123551512354",
-                           "op": "&",
-                           "conditions": [
-                               {
-                                   "type": "qualification",
-                                   "id": "1326689252709",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               },
-                               {
-                                   "type": "qualification",
-                                   "id": "7841278214890",
-                                   "op": "fullCourse",
-                                   "objective": "3"
-                               },
-                               {
-                                   "type": "qualification",
-                                   "id": "8795742484563",
-                                   "op": "fullCourse",
-                                   "objective": "10"
-                               }
-                           ]
+                       "data": {
+                           "label": "Aerodinámica",
+                           "children": ["dev2A1", "dev3A1", "dev9A1"],
+                           "identation": 2,
+                           "order": 2,
+                           "unit": 1,
+                           "conditions": {
+                               "type": "conditionsGroup",
+                               "id": "3123551512354",
+                               "op": "&",
+                               "conditions": [
+                                   {
+                                       "type": "qualification",
+                                       "id": "1326689252709",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   },
+                                   {
+                                       "type": "qualification",
+                                       "id": "7841278214890",
+                                       "op": "fullCourse",
+                                       "objective": "3"
+                                   },
+                                   {
+                                       "type": "qualification",
+                                       "id": "8795742484563",
+                                       "op": "fullCourse",
+                                       "objective": "10"
+                                   }
+                               ]
+                           }
                        }
                    },
                    {
                        "id": "dev2A1",
-                       "x": 375,
-                       "y": -175,
+                       "position": {
+                           "x": 375,
+                           "y": -175
+                       },
                        "type": "badge",
-                       "title": "Conocimiento"
+                       "data": {
+                           "label": "Conocimiento"
+                       }
                    },
                    {
                        "id": "dev3A1",
-                       "x": 500,
-                       "y": 0,
+                       "position": {
+                           "x": 500,
+                           "y": 0
+                       },
                        "type": "url",
-                       "title": "Web de Aerodinámica Avanzada",
-                       "children": ["dev4A1"],
-                       "identation": 2,
-                       "order": 3,
-                       "unit": 1
+                       "data": {
+                           "label": "Web de Aerodinámica Avanzada",
+                           "children": ["dev4A1"],
+                           "identation": 2,
+                           "order": 3,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev4A1",
-                       "x": 625,
-                       "y": 0,
+                       "position": {
+                           "x": 625,
+                           "y": 0
+                       },
                        "type": "assign",
-                       "title": "Ejercicios de la Web",
-                       "children": ["dev5A1", "dev6A1"],
-                       "identation": 2,
-                       "order": 4,
-                       "unit": 1,
-                       "conditions": {
-                           "type": "conditionsGroup",
-                           "id": "784378163420978",
-                           "op": "&",
-                           "conditions": [
-                               {
-                                   "type": "qualification",
-                                   "id": "89674351236523",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               },
-                               {
-                                   "type": "qualification",
-                                   "id": "1532657364123",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               }
-                           ]
+                       "data": {
+                           "label": "Ejercicios de la Web",
+                           "children": ["dev5A1", "dev6A1"],
+                           "identation": 2,
+                           "order": 4,
+                           "unit": 1,
+                           "conditions": {
+                               "type": "conditionsGroup",
+                               "id": "784378163420978",
+                               "op": "&",
+                               "conditions": [
+                                   {
+                                       "type": "qualification",
+                                       "id": "89674351236523",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   },
+                                   {
+                                       "type": "qualification",
+                                       "id": "1532657364123",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   }
+                               ]
+                           }
                        }
                    },
                    {
                        "id": "dev5A1",
-                       "x": 1000,
-                       "y": 0,
+                       "position": {
+                           "x": 1000,
+                           "y": 0
+                       },
                        "type": "quiz",
-                       "title": "Física de fluidos",
-                       "children": ["dev7A1", "dev8A1", "dev13A1"],
-                       "identation": 3,
-                       "order": 8,
-                       "unit": 1,
-                       "conditions": {
-                           "type": "conditionsGroup",
-                           "id": "100236670156",
-                           "op": "|",
-                           "conditions": [
-                               {
-                                   "type": "qualification",
-                                   "id": "1326689252709",
-                                   "op": "Ejercicios de la Web",
-                                   "objective2": "5"
-                               },
-                               {
-                                   "type": "conditionsGroup",
-                                   "id": "100236670128",
-                                   "op": "&",
-                                   "conditions": [
-                                       {
-                                           "type": "qualification",
-                                           "id": "1321231241559",
-                                           "op": "fullCourse",
-                                           "objective": "10"
-                                       }
-                                   ]
-                               },
-                               {
-                                   "type": "conditionsGroup",
-                                   "id": "324236670128",
-                                   "op": "&",
-                                   "conditions": [
-                                       {
-                                           "type": "qualification",
-                                           "id": "131234681559",
-                                           "op": "fullCourse",
-                                           "objective": "2"
-                                       }
-                                   ]
-                               }
-                           ]
+                       "data": {
+                           "label": "Física de fluidos",
+                           "children": ["dev7A1", "dev8A1", "dev13A1"],
+                           "identation": 3,
+                           "order": 8,
+                           "unit": 1,
+                           "conditions": {
+                               "type": "conditionsGroup",
+                               "id": "100236670156",
+                               "op": "|",
+                               "conditions": [
+                                   {
+                                       "type": "qualification",
+                                       "id": "1326689252709",
+                                       "op": "Ejercicios de la Web",
+                                       "objective2": "5"
+                                   },
+                                   {
+                                       "type": "conditionsGroup",
+                                       "id": "100236670128",
+                                       "op": "&",
+                                       "conditions": [
+                                           {
+                                               "type": "qualification",
+                                               "id": "1321231241559",
+                                               "op": "fullCourse",
+                                               "objective": "10"
+                                           }
+                                       ]
+                                   },
+                                   {
+                                       "type": "conditionsGroup",
+                                       "id": "324236670128",
+                                       "op": "&",
+                                       "conditions": [
+                                           {
+                                               "type": "qualification",
+                                               "id": "131234681559",
+                                               "op": "fullCourse",
+                                               "objective": "2"
+                                           }
+                                       ]
+                                   }
+                               ]
+                           }
                        }
                    },
                    {
                        "id": "dev6A1",
-                       "x": 875,
-                       "y": 175,
+                       "position": {
+                           "x": 875,
+                           "y": 175
+                       },
                        "type": "assign",
-                       "title": "Ejercicios de ampliación",
-                       "children": ["dev5A1"],
-                       "identation": 3,
-                       "order": 8,
-                       "unit": 1
+                       "data": {
+                           "label": "Ejercicios de ampliación",
+                           "children": ["dev5A1"],
+                           "identation": 3,
+                           "order": 8,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev7A1",
-                       "x": 1750,
-                       "y": -175,
+                       "position": {
+                           "x": 1750,
+                           "y": -175
+                       },
                        "type": "badge",
-                       "title": "Mecánica de fluidos",
-                       "identation": 1
+                       "data": {
+                           "label": "Mecánica de fluidos"
+                       }
                    },
                    {
                        "id": "dev8A1",
-                       "x": 1875,
-                       "y": 0,
+                       "position": {
+                           "x": 1875,
+                           "y": 0
+                       },
                        "type": "page",
-                       "title": "Física cuantica",
-                       "children": ["dev-1A1"],
-                       "identation": 1,
-                       "order": 14,
-                       "unit": 3
+                       "data": {
+                           "label": "Física cuantica",
+                           "children": ["dev-1A1"],
+                           "identation": 1,
+                           "order": 14,
+                           "unit": 3
+                       }
                    },
                    {
                        "id": "dev9A1",
-                       "x": 375,
-                       "y": 350,
+                       "position": {
+                           "x": 375,
+                           "y": 350
+                       },
                        "type": "folder",
-                       "title": "Aerodínamica, refuerzo",
-                       "children": ["dev10A1"],
-                       "identation": 2,
-                       "order": 5,
-                       "unit": 1
+                       "data": {
+                           "label": "Aerodínamica, refuerzo",
+                           "children": ["dev10A1"],
+                           "identation": 2,
+                           "order": 5,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev10A1",
-                       "x": 500,
-                       "y": 350,
+                       "position": {
+                           "x": 500,
+                           "y": 350
+                       },
                        "type": "quiz",
-                       "title": "Aerodínamica, refuerzo",
-                       "children": ["dev11A1", "dev12A1", "dev5A1"],
-                       "identation": 2,
-                       "order": 6,
-                       "unit": 1,
-                       "conditions": {
-                           "type": "conditionsGroup",
-                           "id": "98437432194690",
-                           "op": "&",
-                           "conditions": [
-                               {
-                                   "type": "qualification",
-                                   "id": "64819193482345",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               },
-                               {
-                                   "type": "qualification",
-                                   "id": "1245689934223",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               }
-                           ]
+                       "data": {
+                           "label": "Aerodínamica, refuerzo",
+                           "children": ["dev11A1", "dev12A1", "dev5A1"],
+                           "identation": 2,
+                           "order": 6,
+                           "unit": 1,
+                           "conditions": {
+                               "type": "conditionsGroup",
+                               "id": "98437432194690",
+                               "op": "&",
+                               "conditions": [
+                                   {
+                                       "type": "qualification",
+                                       "id": "64819193482345",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   },
+                                   {
+                                       "type": "qualification",
+                                       "id": "1245689934223",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   }
+                               ]
+                           }
                        }
                    },
                    {
                        "id": "dev11A1",
-                       "x": 625,
-                       "y": 175,
+                       "position": {
+                           "x": 625,
+                           "y": 175
+                       },
                        "type": "badge",
-                       "title": "Recuperación",
-                       "identation": 3
+                       "data": {
+                           "label": "Recuperación"
+                       }
                    },
                    {
                        "id": "dev12A1",
-                       "x": 625,
-                       "y": 525,
+                       "position": {
+                           "x": 625,
+                           "y": 525
+                       },
                        "type": "assign",
-                       "title": "Trabajo de recuperación",
-                       "children": ["dev6A1"],
-                       "identation": 3,
-                       "order": 7,
-                       "unit": 1
+                       "data": {
+                           "label": "Trabajo de recuperación",
+                           "children": ["dev6A1"],
+                           "identation": 3,
+                           "order": 7,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "devFragment",
-                       "x": 1125,
-                       "y": 175,
+                       "position": {
+                           "x": 1125,
+                           "y": 175
+                       },
                        "type": "fragment",
-                       "title": "Fragmento",
-                       "style": {
-                           "width": 318,
-                           "height": 68
-                       },
-                       "expanded": false,
-                       "innerNodes": [
-                           {
-                               "id": "dev13A1",
-                               "x": 32,
-                               "y": 64
+                       "data": {
+                           "label": "Fragmento",
+                           "style": {
+                               "width": 318,
+                               "height": 68
                            },
-                           {
-                               "id": "dev14A1",
-                               "x": 157,
-                               "y": 64
-                           },
-                           {
-                               "id": "dev15A1",
-                               "x": 282,
-                               "y": 64
-                           }
-                       ]
+                           "expanded": false,
+                           "innerNodes": [
+                               {
+                                   "id": "dev13A1",
+                                   "position": {
+                                       "x": 32,
+                                       "y": 64
+                                   }
+                               },
+                               {
+                                   "id": "dev14A1",
+                                   "position": {
+                                       "x": 157,
+                                       "y": 64
+                                   }
+                               },
+                               {
+                                   "id": "dev15A1",
+                                   "position": {
+                                       "x": 282,
+                                       "y": 64
+                                   }
+                               }
+                           ]
+                       }
                    },
                    {
                        "id": "dev13A1",
-                       "x": 32,
-                       "y": 64,
+                       "position": {
+                           "x": 32,
+                           "y": 64
+                       },
                        "type": "page",
-                       "title": "Ayuda física de fluidos",
-                       "parent": "devFragment",
-                       "children": ["dev14A1"],
-                       "identation": 3,
-                       "order": 10,
-                       "unit": 2
+                       "parentNode": "devFragment",
+                       "data": {
+                           "label": "Ayuda física de fluidos",
+                           "children": ["dev14A1"],
+                           "identation": 3,
+                           "order": 10,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev14A1",
-                       "x": 157,
-                       "y": 64,
+                       "position": {
+                           "x": 157,
+                           "y": 64
+                       },
                        "type": "forum",
-                       "title": "Preguntas fluidos",
-                       "parent": "devFragment",
-                       "children": ["dev15A1"],
-                       "identation": 3,
-                       "order": 11,
-                       "unit": 2
+                       "parentNode": "devFragment",
+                       "data": {
+                           "label": "Preguntas fluidos",
+                           "children": ["dev15A1"],
+                           "identation": 3,
+                           "order": 11,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev15A1",
-                       "x": 282,
-                       "y": 64,
+                       "position": {
+                           "x": 282,
+                           "y": 64
+                       },
                        "type": "quiz",
-                       "title": "Recuperación fluidos",
-                       "parent": "devFragment",
-                       "children": ["dev7A1", "dev8A1", "dev16A1"],
-                       "identation": 3,
-                       "order": 12,
-                       "unit": 2,
-                       "conditions": {
-                           "type": "conditionsGroup",
-                           "id": "66789554263476",
-                           "op": "&",
-                           "conditions": [
-                               {
-                                   "type": "22374819276399",
-                                   "id": "33211882379017",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               },
-                               {
-                                   "type": "qualification",
-                                   "id": "45566778891238",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               },
-                               {
-                                   "type": "qualification",
-                                   "id": "92172368816234",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               }
-                           ]
+                       "parentNode": "devFragment",
+                       "data": {
+                           "label": "Recuperación fluidos",
+                           "children": ["dev7A1", "dev8A1", "dev16A1"],
+                           "identation": 3,
+                           "order": 12,
+                           "unit": 2,
+                           "conditions": {
+                               "type": "conditionsGroup",
+                               "id": "66789554263476",
+                               "op": "&",
+                               "conditions": [
+                                   {
+                                       "type": "22374819276399",
+                                       "id": "33211882379017",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   },
+                                   {
+                                       "type": "qualification",
+                                       "id": "45566778891238",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   },
+                                   {
+                                       "type": "qualification",
+                                       "id": "92172368816234",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   }
+                               ]
+                           }
                        }
                    },
                    {
                        "id": "dev16A1",
-                       "x": 1500,
-                       "y": 350,
+                       "position": {
+                           "x": 1500,
+                           "y": 350
+                       },
                        "type": "assign",
-                       "title": "Trabajo de recuperación",
-                       "children": ["dev7A1", "dev8A1"],
-                       "identation": 4,
-                       "order": 13,
-                       "unit": 2
+                       "data": {
+                           "label": "Trabajo de recuperación",
+                           "children": ["dev7A1", "dev8A1"],
+                           "identation": 4,
+                           "order": 13,
+                           "unit": 2
+                       }
                    }
                ]
            },
@@ -373,19 +455,25 @@
                "blocksData": [
                    {
                        "id": "dev-2A2",
-                       "x": 0,
-                       "y": 0,
+                       "position": {
+                           "x": 0,
+                           "y": 0
+                       },
                        "type": "start",
-                       "title": "Inicio",
-                       "identation": 1
+                       "data": {
+                           "label": "Inicio"
+                       }
                    },
                    {
                        "id": "dev-1A2",
-                       "x": 125,
-                       "y": 0,
+                       "position": {
+                           "x": 125,
+                           "y": 0
+                       },
                        "type": "end",
-                       "title": "Final",
-                       "identation": 1
+                       "data": {
+                           "label": "Final"
+                       }
                    }
                ]
            }
@@ -403,145 +491,190 @@
                "blocksData": [
                    {
                        "id": "dev-2B1",
-                       "x": 0,
-                       "y": 175,
+                       "position": {
+                           "x": 0,
+                           "y": 175
+                       },
                        "type": "start",
-                       "title": "Inicio",
-                       "children": ["dev0B1"],
-                       "identation": 1
+                       "data": {
+                           "label": "Inicio",
+                           "children": ["dev0B1"]
+                       }
                    },
                    {
                        "id": "dev-1B1",
-                       "x": 1000,
-                       "y": 525,
+                       "position": {
+                           "x": 1000,
+                           "y": 525
+                       },
                        "type": "end",
-                       "title": "Final",
-                       "identation": 1
+                       "data": {
+                           "label": "Final"
+                       }
                    },
                    {
                        "id": "dev0B1",
-                       "x": 125,
-                       "y": 175,
+                       "position": {
+                           "x": 125,
+                           "y": 175
+                       },
                        "type": "resource",
-                       "title": "Ecuaciones",
-                       "children": ["dev1B1"],
-                       "identation": 1,
-                       "order": 1,
-                       "unit": 1
+                       "data": {
+                           "label": "Ecuaciones",
+                           "children": ["dev1B1"],
+                           "identation": 1,
+                           "order": 1,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev1B1",
-                       "x": 250,
-                       "y": 175,
+                       "position": {
+                           "x": 250,
+                           "y": 175
+                       },
                        "type": "quiz",
-                       "title": "Examen Tema 1",
-                       "children": ["dev2B1", "dev4B1"],
-                       "identation": 2,
-                       "order": 2,
-                       "unit": 1,
-                       "conditions": {
-                           "type": "conditionsGroup",
-                           "id": "781267123670427",
-                           "op": "&",
-                           "conditions": [
-                               {
-                                   "type": "qualification",
-                                   "id": "984325782581123",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               },
-                               {
-                                   "type": "qualification",
-                                   "id": "123658623618641",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               }
-                           ]
+                       "data": {
+                           "label": "Examen Tema 1",
+                           "children": ["dev2B1", "dev4B1"],
+                           "identation": 2,
+                           "order": 2,
+                           "unit": 1,
+                           "conditions": {
+                               "type": "conditionsGroup",
+                               "id": "781267123670427",
+                               "op": "&",
+                               "conditions": [
+                                   {
+                                       "type": "qualification",
+                                       "id": "984325782581123",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   },
+                                   {
+                                       "type": "qualification",
+                                       "id": "123658623618641",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   }
+                               ]
+                           }
                        }
                    },
                    {
                        "id": "dev2B1",
-                       "x": 375,
-                       "y": 0,
+                       "position": {
+                           "x": 375,
+                           "y": 0
+                       },
                        "type": "folder",
-                       "title": "Ecuaciones",
-                       "children": ["dev3B1"],
-                       "identation": 2,
-                       "order": 3,
-                       "unit": 1
+                       "data": {
+                           "label": "Ecuaciones",
+                           "children": ["dev3B1"],
+                           "identation": 2,
+                           "order": 3,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev3B1",
-                       "x": 500,
-                       "y": 0,
+                       "position": {
+                           "x": 500,
+                           "y": 0
+                       },
                        "type": "badge",
-                       "title": "Insignia Ecuaciones",
-                       "identation": 2
+                       "data": {
+                           "label": "Insignia Ecuaciones"
+                       }
                    },
                    {
                        "id": "dev4B1",
-                       "x": 375,
-                       "y": 350,
+                       "position": {
+                           "x": 375,
+                           "y": 350
+                       },
                        "type": "url",
-                       "title": "Web raices cuadradas",
-                       "children": ["dev5B1"],
-                       "identation": 1,
-                       "order": 4,
-                       "unit": 1
+                       "data": {
+                           "label": "Web raices cuadradas",
+                           "children": ["dev5B1"],
+                           "identation": 1,
+                           "order": 4,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev5B1",
-                       "x": 500,
-                       "y": 350,
+                       "position": {
+                           "x": 500,
+                           "y": 350
+                       },
                        "type": "forum",
-                       "title": "Foro de discusión",
-                       "children": ["dev6B1"],
-                       "identation": 2,
-                       "order": 5,
-                       "unit": 1
+                       "data": {
+                           "label": "Foro de discusión",
+                           "children": ["dev6B1"],
+                           "identation": 2,
+                           "order": 5,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev6B1",
-                       "x": 625,
-                       "y": 350,
+                       "position": {
+                           "x": 625,
+                           "y": 350
+                       },
                        "type": "quiz",
-                       "title": "Cuestionario de raices",
-                       "children": ["dev7B1", "dev8B1"],
-                       "identation": 1,
-                       "order": 6,
-                       "unit": 2
+                       "data": {
+                           "label": "Cuestionario de raices",
+                           "children": ["dev7B1", "dev8B1"],
+                           "identation": 1,
+                           "order": 6,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev7B1",
-                       "x": 750,
-                       "y": 175,
+                       "position": {
+                           "x": 750,
+                           "y": 175
+                       },
                        "type": "assign",
-                       "title": "Ejercicio de raices",
-                       "identation": 1,
-                       "order": 7,
-                       "unit": 2
+                       "data": {
+                           "label": "Ejercicio de raices",
+                           "identation": 1,
+                           "order": 7,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev8B1",
-                       "x": 750,
-                       "y": 525,
+                       "position": {
+                           "x": 750,
+                           "y": 525
+                       },
                        "type": "choice",
-                       "title": "Preguntas sobre raices",
-                       "children": ["dev9B1"],
-                       "identation": 1,
-                       "order": 8,
-                       "unit": 2
+                       "data": {
+                           "label": "Preguntas sobre raices",
+                           "children": ["dev9B1"],
+                           "identation": 1,
+                           "order": 8,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev9B1",
-                       "x": 875,
-                       "y": 525,
+                       "position": {
+                           "x": 875,
+                           "y": 525
+                       },
                        "type": "page",
-                       "title": "Web informativa",
-                       "children": ["dev-1B1"],
-                       "identation": 2,
-                       "order": 9,
-                       "unit": 3
+                       "data": {
+                           "label": "Web informativa",
+                           "children": ["dev-1B1"],
+                           "identation": 2,
+                           "order": 9,
+                           "unit": 3
+                       }
                    }
                ]
            },
@@ -553,166 +686,222 @@
                "blocksData": [
                    {
                        "id": "dev-2B2",
-                       "x": 0,
-                       "y": 700,
+                       "position": {
+                           "x": 0,
+                           "y": 700
+                       },
                        "type": "start",
-                       "title": "Inicio",
-                       "children": ["dev0B2"],
-                       "identation": 1
+                       "data": {
+                           "label": "Inicio",
+                           "children": ["dev0B2"]
+                       }
                    },
                    {
                        "id": "dev-1B2",
-                       "x": 1000,
-                       "y": 700,
+                       "position": {
+                           "x": 1000,
+                           "y": 700
+                       },
                        "type": "end",
-                       "title": "Final",
-                       "identation": 1
+                       "data": {
+                           "label": "Final"
+                       }
                    },
                    {
                        "id": "dev0B2",
-                       "x": 125,
-                       "y": 700,
+                       "position": {
+                           "x": 125,
+                           "y": 700
+                       },
                        "type": "resource",
-                       "title": "Ecuaciones",
-                       "children": ["dev1B2"],
-                       "identation": 1,
-                       "order": 1,
-                       "unit": 1
+                       "data": {
+                           "label": "Ecuaciones",
+                           "children": ["dev1B2"],
+                           "identation": 1,
+                           "order": 1,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev1B2",
-                       "x": 250,
-                       "y": 700,
+                       "position": {
+                           "x": 250,
+                           "y": 700
+                       },
                        "type": "quiz",
-                       "title": "Examen Tema 1",
-                       "children": ["dev2B2", "dev5B2"],
-                       "identation": 2,
-                       "order": 2,
-                       "unit": 1,
-                       "conditions": {
-                           "type": "conditionsGroup",
-                           "id": "77665544331122",
-                           "op": "&",
-                           "conditions": [
-                               {
-                                   "type": "qualification",
-                                   "id": "22885507753719",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               },
-                               {
-                                   "type": "qualification",
-                                   "id": "228855077531234",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               }
-                           ]
+                       "data": {
+                           "label": "Examen Tema 1",
+                           "children": ["dev2B2", "dev5B2"],
+                           "identation": 2,
+                           "order": 2,
+                           "unit": 1,
+                           "conditions": {
+                               "type": "conditionsGroup",
+                               "id": "77665544331122",
+                               "op": "&",
+                               "conditions": [
+                                   {
+                                       "type": "qualification",
+                                       "id": "22885507753719",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   },
+                                   {
+                                       "type": "qualification",
+                                       "id": "228855077531234",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   }
+                               ]
+                           }
                        }
                    },
                    {
                        "id": "dev2B2",
-                       "x": 375,
-                       "y": 525,
+                       "position": {
+                           "x": 375,
+                           "y": 525
+                       },
                        "type": "folder",
-                       "title": "Carpeta Ecuaciones",
-                       "children": ["dev10B2", "dev11B2"],
-                       "identation": 2,
-                       "order": 3,
-                       "unit": 1
+                       "data": {
+                           "label": "Carpeta Ecuaciones",
+                           "children": ["dev10B2", "dev11B2"],
+                           "identation": 2,
+                           "order": 3,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev5B2",
-                       "x": 375,
-                       "y": 1050,
+                       "position": {
+                           "x": 375,
+                           "y": 1050
+                       },
                        "type": "quiz",
-                       "title": "Cuestionario de raices",
-                       "children": ["dev6B2", "dev7B2"],
-                       "identation": 1,
-                       "order": 8,
-                       "unit": 2
+                       "data": {
+                           "label": "Cuestionario de raices",
+                           "children": ["dev6B2", "dev7B2"],
+                           "identation": 1,
+                           "order": 8,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev6B2",
-                       "x": 625,
-                       "y": 875,
+                       "position": {
+                           "x": 625,
+                           "y": 875
+                       },
                        "type": "assign",
-                       "title": "Ejercicio de raices",
-                       "children": ["dev-1B2"],
-                       "identation": 1,
-                       "order": 9,
-                       "unit": 2
+                       "data": {
+                           "label": "Ejercicio de raices",
+                           "children": ["dev-1B2"],
+                           "identation": 1,
+                           "order": 9,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev7B2",
                        "x": 625,
                        "y": 1225,
+                       "position": {
+                           "x": 625,
+                           "y": 1225
+                       },
                        "type": "choice",
-                       "title": "Preguntas y respuestas",
-                       "children": ["dev40B2", "dev41B2"],
-                       "identation": 1,
-                       "order": 10,
-                       "unit": 2
+                       "data": {
+                           "label": "Preguntas y respuestas",
+                           "children": ["dev40B2", "dev41B2"],
+                           "identation": 1,
+                           "order": 10,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev10B2",
-                       "x": 500,
-                       "y": 350,
+                       "position": {
+                           "x": 500,
+                           "y": 350
+                       },
                        "type": "folder",
-                       "title": "Carpeta Ecuaciones 2",
-                       "children": ["dev12B2", "dev13B2"],
-                       "identation": 2,
-                       "order": 4,
-                       "unit": 1
+                       "data": {
+                           "label": "Carpeta Ecuaciones 2",
+                           "children": ["dev12B2", "dev13B2"],
+                           "identation": 2,
+                           "order": 4,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev11B2",
-                       "x": 500,
-                       "y": 700,
+                       "position": {
+                           "x": 500,
+                           "y": 700
+                       },
                        "type": "folder",
-                       "title": "Carpeta Ecuaciones 3",
-                       "identation": 2,
-                       "order": 5,
-                       "unit": 1
+                       "data": {
+                           "label": "Carpeta Ecuaciones 3",
+                           "identation": 2,
+                           "order": 5,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev12B2",
-                       "x": 625,
-                       "y": 175,
+                       "position": {
+                           "x": 625,
+                           "y": 175
+                       },
                        "type": "folder",
-                       "title": "Insignia Ecuaciones 4",
-                       "identation": 2,
-                       "order": 6,
-                       "unit": 1
+                       "data": {
+                           "label": "Insignia Ecuaciones 4",
+                           "identation": 2,
+                           "order": 6,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev13B2",
-                       "x": 625,
-                       "y": 525,
+                       "position": {
+                           "x": 625,
+                           "y": 525
+                       },
                        "type": "folder",
-                       "title": "Carpeta Ecuaciones 5",
-                       "identation": 2,
-                       "order": 7,
-                       "unit": 1
+                       "data": {
+                           "label": "Carpeta Ecuaciones 5",
+                           "identation": 2,
+                           "order": 7,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev40B2",
-                       "x": 750,
-                       "y": 1050,
+                       "position": {
+                           "x": 750,
+                           "y": 1050
+                       },
                        "type": "page",
-                       "title": "Web informativa 2",
-                       "identation": 2,
-                       "order": 11,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 2",
+                           "identation": 2,
+                           "order": 11,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev41B2",
-                       "x": 750,
-                       "y": 1400,
+                       "position": {
+                           "x": 750,
+                           "y": 1400
+                       },
                        "type": "page",
-                       "title": "Web informativa 3",
-                       "identation": 2,
-                       "order": 12,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 3",
+                           "identation": 2,
+                           "order": 12,
+                           "unit": 2
+                       }
                    }
                ]
            },
@@ -724,220 +913,294 @@
                "blocksData": [
                    {
                        "id": "dev-2B3",
-                       "x": 0,
-                       "y": 175,
+                       "position": {
+                           "x": 0,
+                           "y": 175
+                       },
                        "type": "start",
-                       "title": "Inicio",
-                       "children": ["dev0B3"],
-                       "identation": 1
+                       "data": {
+                           "label": "Inicio",
+                           "children": ["dev0B3"]
+                       }
                    },
                    {
                        "id": "dev-1B3",
-                       "x": 1500,
-                       "y": 525,
+                       "position": {
+                           "x": 1500,
+                           "y": 525
+                       },
                        "type": "end",
-                       "title": "Final",
-                       "identation": 1
+                       "data": {
+                           "label": "Final"
+                       }
                    },
                    {
                        "id": "dev0B3",
-                       "x": 125,
-                       "y": 175,
+                       "position": {
+                           "x": 125,
+                           "y": 175
+                       },
                        "type": "resource",
-                       "title": "Ecuaciones",
-                       "children": ["dev1B3"],
-                       "identation": 1,
-                       "order": 1,
-                       "unit": 1
+                       "data": {
+                           "label": "Ecuaciones",
+                           "children": ["dev1B3"],
+                           "identation": 1,
+                           "order": 1,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev1B3",
-                       "x": 250,
-                       "y": 175,
+                       "position": {
+                           "x": 250,
+                           "y": 175
+                       },
                        "type": "quiz",
-                       "title": "Examen Tema 1",
-                       "children": ["dev2B3", "dev3B3"],
-                       "identation": 2,
-                       "order": 2,
-                       "unit": 1,
-                       "conditions": {
-                           "type": "conditionsGroup",
-                           "id": "44885577221100",
-                           "op": "&",
-                           "conditions": [
-                               {
-                                   "type": "qualification",
-                                   "id": "0987886579865",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               },
-                               {
-                                   "type": "qualification",
-                                   "id": "3678543556977",
-                                   "op": "fullCourse",
-                                   "objective": "2"
-                               }
-                           ]
+                       "data": {
+                           "label": "Examen Tema 1",
+                           "children": ["dev2B3", "dev3B3"],
+                           "identation": 2,
+                           "order": 2,
+                           "unit": 1,
+                           "conditions": {
+                               "type": "conditionsGroup",
+                               "id": "44885577221100",
+                               "op": "&",
+                               "conditions": [
+                                   {
+                                       "type": "qualification",
+                                       "id": "0987886579865",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   },
+                                   {
+                                       "type": "qualification",
+                                       "id": "3678543556977",
+                                       "op": "fullCourse",
+                                       "objective": "2"
+                                   }
+                               ]
+                           }
                        }
                    },
                    {
                        "id": "dev2B3",
-                       "x": 375,
-                       "y": 0,
+                       "position": {
+                           "x": 375,
+                           "y": 0
+                       },
                        "type": "folder",
-                       "title": "Carpeta Ecuaciones",
-                       "identation": 2,
-                       "order": 3,
-                       "unit": 1
+                       "data": {
+                           "label": "Carpeta Ecuaciones",
+                           "identation": 2,
+                           "order": 3,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev3B3",
-                       "x": 375,
-                       "y": 350,
+                       "position": {
+                           "x": 375,
+                           "y": 350
+                       },
                        "type": "url",
-                       "title": "Web raices cuadradas",
-                       "children": ["dev4B3"],
-                       "identation": 1,
-                       "order": 4,
-                       "unit": 1
+                       "data": {
+                           "label": "Web raices cuadradas",
+                           "children": ["dev4B3"],
+                           "identation": 1,
+                           "order": 4,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev4B3",
-                       "x": 500,
-                       "y": 350,
+                       "position": {
+                           "x": 500,
+                           "y": 350
+                       },
                        "type": "forum",
-                       "title": "Foro de discusión",
-                       "children": ["dev5B3"],
-                       "identation": 2,
-                       "order": 5,
-                       "unit": 1
+                       "data": {
+                           "label": "Foro de discusión",
+                           "children": ["dev5B3"],
+                           "identation": 2,
+                           "order": 5,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev5B3",
-                       "x": 625,
-                       "y": 350,
+                       "position": {
+                           "x": 625,
+                           "y": 350
+                       },
                        "type": "quiz",
-                       "title": "Cuestionario de raices",
-                       "children": ["dev6B3", "dev7B3"],
-                       "identation": 1,
-                       "order": 6,
-                       "unit": 1
+                       "data": {
+                           "label": "Cuestionario de raices",
+                           "children": ["dev6B3", "dev7B3"],
+                           "identation": 1,
+                           "order": 6,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev6B3",
-                       "x": 750,
-                       "y": 0,
+                       "position": {
+                           "x": 750,
+                           "y": 0
+                       },
                        "type": "assign",
-                       "title": "Ejercicio de raices",
-                       "identation": 1,
-                       "order": 7,
-                       "unit": 1
+                       "data": {
+                           "label": "Ejercicio de raices",
+                           "identation": 1,
+                           "order": 7,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev7B3",
-                       "x": 750,
-                       "y": 350,
+                       "position": {
+                           "x": 750,
+                           "y": 350
+                       },
                        "type": "choice",
-                       "title": "Preguntas de Matemáticas",
-                       "children": ["dev8B3"],
-                       "identation": 1,
-                       "order": 8,
-                       "unit": 1
+                       "data": {
+                           "label": "Preguntas de Matemáticas",
+                           "children": ["dev8B3"],
+                           "identation": 1,
+                           "order": 8,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev8B3",
-                       "x": 875,
-                       "y": 525,
+                       "position": {
+                           "x": 875,
+                           "y": 525
+                       },
                        "type": "page",
-                       "title": "Web informativa",
-                       "children": ["dev40B3", "dev41B3"],
-                       "identation": 2,
-                       "order": 9,
-                       "unit": 1
+                       "data": {
+                           "label": "Web informativa",
+                           "children": ["dev40B3", "dev41B3"],
+                           "identation": 2,
+                           "order": 9,
+                           "unit": 1
+                       }
                    },
                    {
                        "id": "dev40B3",
-                       "x": 1000,
-                       "y": 875,
+                       "position": {
+                           "x": 1000,
+                           "y": 875
+                       },
                        "type": "page",
-                       "title": "Web informativa 2",
-                       "identation": 2,
-                       "order": 10,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 2",
+                           "identation": 2,
+                           "order": 10,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev41B3",
-                       "x": 1000,
-                       "y": 525,
+                       "position": {
+                           "x": 1000,
+                           "y": 525
+                       },
                        "type": "page",
-                       "title": "Web informativa 3",
-                       "children": ["dev44B3", "dev45B3"],
-                       "identation": 2,
-                       "order": 11,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 3",
+                           "children": ["dev44B3", "dev45B3"],
+                           "identation": 2,
+                           "order": 11,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev44B3",
-                       "x": 1125,
-                       "y": 525,
+                       "position": {
+                           "x": 1125,
+                           "y": 525
+                       },
                        "type": "page",
-                       "title": "Web informativa 6",
-                       "children": ["dev46B3", "dev47B3"],
-                       "identation": 2,
-                       "order": 12,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 6",
+                           "children": ["dev46B3", "dev47B3"],
+                           "identation": 2,
+                           "order": 12,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev45B3",
-                       "x": 1125,
-                       "y": 875,
+                       "position": {
+                           "x": 1125,
+                           "y": 875
+                       },
                        "type": "page",
-                       "title": "Web informativa 7",
-                       "identation": 2,
-                       "order": 13,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 7",
+                           "identation": 2,
+                           "order": 13,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev46B3",
-                       "x": 1250,
-                       "y": 525,
+                       "position": {
+                           "x": 1250,
+                           "y": 525
+                       },
                        "type": "page",
-                       "title": "Web informativa 8",
-                       "children": ["dev48B3", "dev49B3"],
-                       "identation": 2,
-                       "order": 14,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 8",
+                           "children": ["dev48B3", "dev49B3"],
+                           "identation": 2,
+                           "order": 14,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev47B3",
-                       "x": 1250,
-                       "y": 875,
+                       "position": {
+                           "x": 1250,
+                           "y": 875
+                       },
                        "type": "page",
-                       "title": "Web informativa 9",
-                       "identation": 2,
-                       "order": 15,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 9",
+                           "identation": 2,
+                           "order": 15,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev48B3",
-                       "x": 1375,
-                       "y": 525,
+                       "position": {
+                           "x": 1375,
+                           "y": 525
+                       },
                        "type": "page",
-                       "title": "Web informativa 10",
-                       "children": ["dev-1B3"],
-                       "identation": 2,
-                       "order": 16,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 10",
+                           "children": ["dev-1B3"],
+                           "identation": 2,
+                           "order": 16,
+                           "unit": 2
+                       }
                    },
                    {
                        "id": "dev49B3",
-                       "x": 1375,
-                       "y": 875,
+                       "position": {
+                           "x": 1375,
+                           "y": 875
+                       },
                        "type": "page",
-                       "title": "Web informativa 11",
-                       "identation": 2,
-                       "order": 17,
-                       "unit": 2
+                       "data": {
+                           "label": "Web informativa 11",
+                           "identation": 2,
+                           "order": 17,
+                           "unit": 2
+                       }
                    }
                ]
            }
@@ -955,19 +1218,25 @@
                "blocksData": [
                    {
                        "id": "dev-2C1",
-                       "x": 0,
-                       "y": 0,
+                       "position": {
+                           "x": 0,
+                           "y": 0
+                       },
                        "type": "start",
-                       "title": "Inicio",
-                       "identation": 1
+                       "data": {
+                           "label": "Inicio"
+                       }
                    },
                    {
                        "id": "dev-1C1",
-                       "x": 125,
-                       "y": 0,
+                       "position": {
+                           "x": 125,
+                           "y": 0
+                       },
                        "type": "end",
-                       "title": "Final",
-                       "identation": 1
+                       "data": {
+                           "label": "Final"
+                       }
                    }
                ]
            },
@@ -981,17 +1250,25 @@
                        "id": "dev-2C2",
                        "x": 0,
                        "y": 0,
+                       "position": {
+                           "x": 0,
+                           "y": 0
+                       },
                        "type": "start",
-                       "title": "Inicio",
-                       "identation": 1
+                       "data": {
+                           "label": "Inicio"
+                       }
                    },
                    {
                        "id": "dev-1C2",
-                       "x": 125,
-                       "y": 0,
+                       "position": {
+                           "x": 125,
+                           "y": 0
+                       },
                        "type": "end",
-                       "title": "Final",
-                       "identation": 1
+                       "data": {
+                           "label": "Final"
+                       }
                    }
                ]
            }