feat: Enhance undo/redo functionality in MoveControls2D and SelectionControls2D; process deleted and updated aisles, walls, floors, and zones for better state management

This commit is contained in:
2025-07-31 09:53:49 +05:30
parent e405596d9e
commit d6f8a29b83
2 changed files with 212 additions and 49 deletions

View File

@@ -38,10 +38,10 @@ function MoveControls2D({
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext(); const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
const { push2D } = undoRedo2DStore(); const { push2D } = undoRedo2DStore();
const { setPosition: setAislePosition, getAislesByPointId } = aisleStore(); const { setPosition: setAislePosition, getAislesByPointId, getAisleById } = aisleStore();
const { setPosition: setWallPosition, getWallsByPointId } = wallStore(); const { setPosition: setWallPosition, getWallsByPointId, getWallById } = wallStore();
const { setPosition: setFloorPosition, getFloorsByPointId } = floorStore(); const { setPosition: setFloorPosition, getFloorsByPointId, getFloorById } = floorStore();
const { setPosition: setZonePosition, getZonesByPointId } = zoneStore(); const { setPosition: setZonePosition, getZonesByPointId, getZoneById } = zoneStore();
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null); const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({}); const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
const [initialStates, setInitialStates] = useState<Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }>>({}); const [initialStates, setInitialStates] = useState<Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }>>({});
@@ -225,6 +225,10 @@ function MoveControls2D({
if (movedObjects.length === 0) return; if (movedObjects.length === 0) return;
const undoPoints: UndoRedo2DDataTypeSchema[] = []; const undoPoints: UndoRedo2DDataTypeSchema[] = [];
const processedAisles: UndoRedo2DDataTypeSchema[] = [];
const processedWalls: UndoRedo2DDataTypeSchema[] = [];
const processedFloors: UndoRedo2DDataTypeSchema[] = [];
const processedZones: UndoRedo2DDataTypeSchema[] = [];
movedObjects.forEach((movedObject: THREE.Object3D) => { movedObjects.forEach((movedObject: THREE.Object3D) => {
if (movedObject.userData.pointUuid) { if (movedObject.userData.pointUuid) {
@@ -253,7 +257,7 @@ function MoveControls2D({
const old = initialStates[movedObject.uuid]; const old = initialStates[movedObject.uuid];
if (old) { if (old) {
undoPoints.push({ processedAisles.push({
type: 'Aisle', type: 'Aisle',
lineData: { lineData: {
...updatedAisle, ...updatedAisle,
@@ -293,7 +297,7 @@ function MoveControls2D({
const old = initialStates[movedObject.uuid]; const old = initialStates[movedObject.uuid];
if (old) { if (old) {
undoPoints.push({ processedWalls.push({
type: 'Wall', type: 'Wall',
lineData: { lineData: {
...updatedWall, ...updatedWall,
@@ -313,6 +317,7 @@ function MoveControls2D({
}); });
} }
} else if (point.pointType === 'Floor') { } else if (point.pointType === 'Floor') {
const Floors = getFloorsByPointId(point.pointUuid);
const updatedFloors = getFloorsByPointId(point.pointUuid); const updatedFloors = getFloorsByPointId(point.pointUuid);
if (updatedFloors?.length && projectId) { if (updatedFloors?.length && projectId) {
updatedFloors.forEach(updatedFloor => { updatedFloors.forEach(updatedFloor => {
@@ -331,25 +336,27 @@ function MoveControls2D({
organization organization
}); });
const old = initialStates[movedObject.uuid]; const updatedFloorsData = updatedFloors.map((floor) => {
if (old) { const originalFloor = Floors.find(f => f.floorUuid === floor.floorUuid) || floor;
undoPoints.push({
type: 'Floor', const updatedPoints = originalFloor.points.map((pt: Point) => {
lineData: { const init = initialStates[pt.pointUuid];
...updatedFloor, return init ? { ...pt, position: [init.position.x, init.position.y, init.position.z] } : pt;
points: updatedFloor.points.map(p => }) as [Point, Point];
p.pointUuid === point.pointUuid
? { ...p, position: [old.position.x, old.position.y, old.position.z] } return {
: p type: "Floor" as const,
), lineData: { ...originalFloor, points: updatedPoints },
}, newData: floor,
newData: updatedFloor,
timeStamp: new Date().toISOString(), timeStamp: new Date().toISOString(),
}); };
} });
processedFloors.push(...updatedFloorsData);
}); });
} }
} else if (point.pointType === 'Zone') { } else if (point.pointType === 'Zone') {
const Zones = getZonesByPointId(point.pointUuid);
const updatedZones = getZonesByPointId(point.pointUuid); const updatedZones = getZonesByPointId(point.pointUuid);
if (updatedZones?.length && projectId) { if (updatedZones?.length && projectId) {
updatedZones.forEach(updatedZone => { updatedZones.forEach(updatedZone => {
@@ -368,39 +375,150 @@ function MoveControls2D({
organization organization
}); });
const old = initialStates[movedObject.uuid]; const updatedZonesData = updatedZones.map((zone) => {
if (old) { const originalZone = Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone;
undoPoints.push({
type: 'Zone', const updatedPoints = originalZone.points.map((pt: Point) => {
lineData: { const init = initialStates[pt.pointUuid];
...updatedZone, return init ? { ...pt, position: [init.position.x, init.position.y, init.position.z] } : pt;
points: updatedZone.points.map(p => }) as [Point, Point];
p.pointUuid === point.pointUuid
? { ...p, position: [old.position.x, old.position.y, old.position.z] } return {
: p type: "Zone" as const,
), lineData: { ...originalZone, points: updatedPoints },
}, newData: zone,
newData: updatedZone,
timeStamp: new Date().toISOString(), timeStamp: new Date().toISOString(),
}); };
} });
processedZones.push(...updatedZonesData);
}); });
} }
} }
} }
}); });
if (undoPoints.length > 0) { setTimeout(() => {
push2D({ if (processedWalls.length > 0) {
type: 'Draw', const wallMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
actions: [
{ for (const wall of processedWalls) {
actionType: 'Lines-Update', if (wall.type !== 'Wall' || !wall.lineData.wallUuid) continue;
points: undoPoints const uuid = wall.lineData.wallUuid;
if (!wallMap.has(uuid)) wallMap.set(uuid, []);
wallMap.get(uuid)!.push(wall);
}
wallMap.forEach((actions, uuid) => {
const hasUpdate = actions.some(action => 'newData' in action);
if (hasUpdate) {
const wallData = getWallById(uuid);
if (wallData) {
undoPoints.push({
type: 'Wall',
lineData: actions[0].lineData as Wall,
newData: wallData as Wall,
timeStamp: new Date().toISOString()
});
}
} }
] });
}); }
}
if (processedAisles.length > 0) {
const aisleMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const aisle of processedAisles) {
if (aisle.type !== 'Aisle' || !aisle.lineData.aisleUuid) continue;
const uuid = aisle.lineData.aisleUuid;
if (!aisleMap.has(uuid)) aisleMap.set(uuid, []);
aisleMap.get(uuid)!.push(aisle);
}
aisleMap.forEach((actions, uuid) => {
const hasUpdate = actions.some(action => 'newData' in action);
if (hasUpdate) {
const aisleData = getAisleById(uuid);
if (aisleData) {
undoPoints.push({
type: 'Aisle',
lineData: actions[0].lineData as Aisle,
newData: aisleData as Aisle,
timeStamp: new Date().toISOString()
});
}
}
});
}
if (processedFloors.length > 0) {
const floorMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const floor of processedFloors) {
if (floor.type !== 'Floor' || !floor.lineData.floorUuid) continue;
const uuid = floor.lineData.floorUuid;
if (!floorMap.has(uuid)) {
floorMap.set(uuid, []);
}
floorMap.get(uuid)!.push(floor);
}
floorMap.forEach((actions, uuid) => {
const hasUpdate = actions.some(action => 'newData' in action);
if (hasUpdate) {
const floorData = getFloorById(uuid);
if (floorData) {
undoPoints.push({
type: 'Floor',
lineData: actions[0].lineData as Floor,
newData: floorData as Floor,
timeStamp: new Date().toISOString()
});
}
}
});
}
if (processedZones.length > 0) {
const zoneMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const zone of processedZones) {
if (zone.type !== 'Zone' || !zone.lineData.zoneUuid) continue;
const uuid = zone.lineData.zoneUuid;
if (!zoneMap.has(uuid)) {
zoneMap.set(uuid, []);
}
zoneMap.get(uuid)!.push(zone);
}
zoneMap.forEach((actions, uuid) => {
const hasUpdate = actions.some(action => 'newData' in action);
if (hasUpdate) {
const zoneData = getZoneById(uuid);
if (zoneData) {
undoPoints.push({
type: 'Zone',
lineData: actions[0].lineData as Zone,
newData: zoneData as Zone,
timeStamp: new Date().toISOString()
});
}
}
});
}
if (undoPoints.length > 0) {
push2D({
type: 'Draw',
actions: [
{
actionType: 'Lines-Update',
points: undoPoints
}
]
});
}
}, 0);
echo.success("Object moved!"); echo.success("Object moved!");
clearSelection(); clearSelection();

View File

@@ -226,7 +226,8 @@ const SelectionControls2D: React.FC = () => {
const deletedPoints: UndoRedo2DDataTypeSchema[] = []; const deletedPoints: UndoRedo2DDataTypeSchema[] = [];
const updatedPoints: UndoRedo2DDataTypeSchema[] = []; const updatedPoints: UndoRedo2DDataTypeSchema[] = [];
const processedAisles: UndoRedo2DDataTypeSchema[] = [];
const processedWalls: UndoRedo2DDataTypeSchema[] = [];
const processedFloors: UndoRedo2DDataTypeSchema[] = []; const processedFloors: UndoRedo2DDataTypeSchema[] = [];
const processedZones: UndoRedo2DDataTypeSchema[] = []; const processedZones: UndoRedo2DDataTypeSchema[] = [];
@@ -263,7 +264,7 @@ const SelectionControls2D: React.FC = () => {
timeStamp: new Date().toISOString(), timeStamp: new Date().toISOString(),
})); }));
deletedPoints.push(...removedAislesData); processedAisles.push(...removedAislesData);
} }
} }
if (point.pointType === 'Wall') { if (point.pointType === 'Wall') {
@@ -296,7 +297,7 @@ const SelectionControls2D: React.FC = () => {
timeStamp: new Date().toISOString(), timeStamp: new Date().toISOString(),
})); }));
deletedPoints.push(...removedWallsData); processedWalls.push(...removedWallsData);
} }
} }
if (point.pointType === 'Floor') { if (point.pointType === 'Floor') {
@@ -433,6 +434,50 @@ const SelectionControls2D: React.FC = () => {
}) })
setTimeout(() => { setTimeout(() => {
if (processedWalls.length > 0) {
const wallMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const wall of processedWalls) {
if (wall.type !== 'Wall' || !wall.lineData.wallUuid) continue;
const uuid = wall.lineData.wallUuid;
if (!wallMap.has(uuid)) wallMap.set(uuid, []);
wallMap.get(uuid)!.push(wall);
}
wallMap.forEach((actions) => {
const hasDelete = actions.some(action => !('newData' in action));
if (hasDelete) {
deletedPoints.push({
type: 'Wall',
lineData: actions[0].lineData as Wall,
timeStamp: new Date().toISOString()
});
}
});
}
if (processedAisles.length > 0) {
const aisleMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
for (const aisle of processedAisles) {
if (aisle.type !== 'Aisle' || !aisle.lineData.aisleUuid) continue;
const uuid = aisle.lineData.aisleUuid;
if (!aisleMap.has(uuid)) aisleMap.set(uuid, []);
aisleMap.get(uuid)!.push(aisle);
}
aisleMap.forEach((actions) => {
const hasDelete = actions.some(action => !('newData' in action));
if (hasDelete) {
deletedPoints.push({
type: 'Aisle',
lineData: actions[0].lineData as Aisle,
timeStamp: new Date().toISOString()
});
}
});
}
if (processedFloors.length > 0) { if (processedFloors.length > 0) {
const floorMap = new Map<string, UndoRedo2DDataTypeSchema[]>(); const floorMap = new Map<string, UndoRedo2DDataTypeSchema[]>();