diff --git a/mirror-2/components/entity-tree/entity-tree.tsx b/mirror-2/components/entity-tree/entity-tree.tsx index 50478c0c..34435669 100644 --- a/mirror-2/components/entity-tree/entity-tree.tsx +++ b/mirror-2/components/entity-tree/entity-tree.tsx @@ -14,52 +14,69 @@ import { TwoWayInput } from '@/components/two-way-input'; import { z } from 'zod'; import { PlusCircle } from 'lucide-react'; -type Entity = Database['public']['Tables']['entities']['Row']; -type EntityWithPopulatedChildren = Omit & { - key: string, - title: string, - icon?: any, - children: EntityWithPopulatedChildren[]; // children now contain an array of Entity objects -}; type TreeDataNodeWithEntityData = TreeDataNode & { name: string, id: string } -function transformDbEntityStructureWithPopulatedChildren(entities: Entity[]): TreeDataNodeWithEntityData[] { - const entityMap = new Map(); - const assignedChildIds = new Set(); // Track all child IDs to remove from the main array +function transformDbEntityStructureWithPopulatedChildren(entities) { + const entityMap = new Map(); + const assignedChildIds = new Set(); - // Create a map for easy lookup of entities by ID, and keep the original children as string IDs - entities.forEach((entity) => { - const entityWithChildren: EntityWithPopulatedChildren & { childIds: string[] } = { + // Step 1: Populate the map with entities and their child IDs + entities.forEach(entity => { + const entityWithChildren = { ...entity, - children: [], // Initialize as an empty array for the populated children + children: [], // Will hold actual child objects key: entity.id, title: entity.name, - childIds: entity.children ? (entity.children as string[]) : [] // Temporarily store child IDs + childIds: Array.isArray(entity.children) ? [...entity.children] : [] // Clone to avoid mutation }; entityMap.set(entity.id, entityWithChildren); }); - // Now replace childIds with the actual EntityWithPopulatedChildren objects - entityMap.forEach((entityWithChildren) => { - if (entityWithChildren.childIds.length > 0) { - entityWithChildren.children = entityWithChildren.childIds.map((childId) => { - const childEntity = entityMap.get(childId)!; - assignedChildIds.add(childId); // Mark this ID as assigned to a parent - return childEntity; // Get the actual child entity - }); + // Step 2: Assign children to their respective parents, preventing duplication + entityMap.forEach(entity => { + if (entity.childIds.length > 0) { + entity.children = entity.childIds + .filter(childId => { + if (assignedChildIds.has(childId)) { + console.warn(`Child with ID ${childId} is already assigned to another parent. Skipping assignment to parent ID ${entity.id}.`); + return false; // Exclude to prevent duplication + } + return true; // Include this child + }) + .map(childId => { + assignedChildIds.add(childId); // Mark as assigned + const childEntity = entityMap.get(childId); + if (!childEntity) { + console.warn(`Child entity with ID ${childId} not found.`); + return null; + } + return childEntity; + }) + .filter(child => child !== null); // Remove any nulls } }); - // Filter out entities that were assigned as children (remove duplicates) + // Step 3: Identify root entities (those not assigned as children and marked as root) const rootEntities = Array.from(entityMap.values()).filter( - (entity) => !assignedChildIds.has(entity.id) // Keep only the root-level entities + entity => !assignedChildIds.has(entity.id) && entity.is_root ); - // Return the filtered entities without the temporary `childIds` field - return rootEntities.map(({ childIds, ...entity }) => entity); + // Step 4: Clean up temporary fields recursively + function cleanEntity(entity) { + const { childIds, ...rest } = entity; + if (rest.children.length > 0) { + rest.children = rest.children.map(child => cleanEntity(child)); + } + return rest; + } + + const cleanedRootEntities = rootEntities.map(cleanEntity); + + return cleanedRootEntities; } + const EntityTree: React.FC = () => { const [treeData, setTreeData] = useState([]); @@ -84,6 +101,7 @@ const EntityTree: React.FC = () => { useEffect(() => { if (entities && entities.length > 0) { const data = transformDbEntityStructureWithPopulatedChildren(entities) + // debugger setTreeData(data) // updateState({ entities, type: 'set-tree', itemId: '' }); } @@ -100,10 +118,7 @@ const EntityTree: React.FC = () => { const dragKey = info.dragNode.key; const dropPos = info.node.pos.split('-'); const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); // the drop position relative to the drop node, inside 0, top -1, bottom 1 - - // for updating db - - + let dragParent: any = null; const loop = ( data: TreeDataNode[], @@ -119,17 +134,40 @@ const EntityTree: React.FC = () => { } } }; + + + // Function to find the parent of the drag node + const findParentNode = (data, childKey: React.Key) => { + for (let i = 0; i < data.length; i++) { + if (data && data[i] && data[i].children) { + const children = data[i].children + const hasChild = children?.some(child => child.key === childKey); + if (hasChild) { + dragParent = data[i]; // Found the parent node + return; // Exit once found + } + findParentNode(children, childKey); + } + } + }; + + // Find the parent of the dragged node + findParentNode(treeData, dragKey); // `treeData` is your current tree structure + + console.log('Parent of dragged node:', dragParent); + const data = [...treeData]; // Find dragObject let dragObj: TreeDataNode; loop(data, dragKey, (item, index, arr) => { arr.splice(index, 1); + dragParent.children = arr dragObj = item; }); if (!info.dropToGap) { - console.log('content drop', data) + console.log('content drop to new parent', data) // Drop on the content loop(data, dropKey, (item) => { item.children = item.children || []; @@ -140,13 +178,16 @@ const EntityTree: React.FC = () => { // update the node and dragnode in DB if (info.node && info.node['id'] && info.node.children) { const childIds = info.node.children.map(child => child['id']) + // debugger updateEntity({ id: info.node['id'], updateData: { children: childIds } }) } if (info.dragNode && info.dragNode['id'] && info.dragNode.children) { const childIds = info.dragNode.children.map(child => child['id']) - updateEntity({ id: info.dragNode['id'], updateData: { children: info.dragNode.children } }) + // debugger + updateEntity({ id: info.dragNode['id'], updateData: { children: childIds } }) } + } else { console.log('gap drop', data) @@ -163,7 +204,7 @@ const EntityTree: React.FC = () => { // Drop on the bottom of the drop node ar.splice(i! + 1, 0, dragObj!); } - debugger + // debugger // not yet // update the node and dragnode in DB if (info.node && info.node['id'] && info.node.children) { const childIds = info.node.children.map(child => child['id']) @@ -171,10 +212,14 @@ const EntityTree: React.FC = () => { } if (info.dragNode && info.dragNode['id'] && info.dragNode.children) { const childIds = info.dragNode.children.map(child => child['id']) - updateEntity({ id: info.dragNode['id'], updateData: { children: info.dragNode.children } }) + updateEntity({ id: info.dragNode['id'], updateData: { children: childIds } }) } } + // update the dragParent to remove the child from its children array + const childIds = dragParent.children.map(child => child['id']) + updateEntity({ id: dragParent.id, updateData: { children: childIds } }) + setTreeData(data); };