feat: Enhance duplication and movement controls with axis constraints and snapping functionality

- Added axis constraint toggling for duplication and movement controls (X and Z axes).
- Implemented snapping functionality for asset positioning based on modifier keys (Ctrl, Shift).
- Created utility functions for handling asset position and rotation snapping.
- Refactored moveControls3D and rotateControls3D to utilize new snapping functions.
- Introduced state management for key events and axis constraints.
- Updated styles for new UI components related to asset filtering and transformation.
- Removed deprecated snapControls utility function.
This commit is contained in:
Nalvazhuthi
2025-08-21 09:57:48 +05:30
20 changed files with 1242 additions and 97 deletions

View File

@@ -12,6 +12,7 @@ import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations";
import useVersionHistoryVisibleStore, {
useDecalStore,
useSaveVersion,
useSelectedFloorItem,
useToolMode,
@@ -31,6 +32,8 @@ import WallProperties from "./properties/WallProperties";
import FloorProperties from "./properties/FloorProperties";
import SelectedWallProperties from "./properties/SelectedWallProperties";
import SelectedFloorProperties from "./properties/SelectedFloorProperties";
import DecalTransformation from "./decals/DecalTransformation";
import Hrm from "./hrm/Hrm";
type DisplayComponent =
| "versionHistory"
@@ -46,6 +49,7 @@ type DisplayComponent =
| "mechanics"
| "analysis"
| "visualization"
| "decals"
| "none";
const SideBarRight: React.FC = () => {
@@ -59,6 +63,7 @@ const SideBarRight: React.FC = () => {
const { selectedEventSphere } = useSelectedEventSphere();
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { isVersionSaved } = useSaveVersion();
const { selectedSubCategory } = useDecalStore();
const [displayComponent, setDisplayComponent] = useState<DisplayComponent>("none");
@@ -119,7 +124,13 @@ const SideBarRight: React.FC = () => {
setDisplayComponent("versionHistory");
return;
}
if (!selectedFloorItem && !selectedFloor && !selectedWall) {
if (selectedSubCategory) {
setDisplayComponent("decals");
return;
}
if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedSubCategory) {
console.log('selectedSubCategory: ', selectedSubCategory);
if (toolMode === "Aisle") {
setDisplayComponent("aisleProperties");
return;
@@ -143,7 +154,7 @@ const SideBarRight: React.FC = () => {
}
setDisplayComponent("none");
}, [viewVersionHistory, activeModule, subModule, isVersionSaved, selectedFloorItem, selectedWall, selectedFloor, selectedAisle, toolMode,]);
}, [viewVersionHistory, activeModule, subModule, isVersionSaved, selectedFloorItem, selectedWall, selectedFloor, selectedAisle, toolMode, selectedSubCategory]);
const renderComponent = () => {
switch (displayComponent) {
@@ -173,6 +184,8 @@ const SideBarRight: React.FC = () => {
return <Analysis />;
case "visualization":
return <Visualization />;
case "decals":
return <DecalTransformation />;
default:
return null;
}
@@ -245,6 +258,7 @@ const SideBarRight: React.FC = () => {
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
{renderComponent()}
{/* <Hrm /> */}
</div>
</div>
)}

View File

@@ -0,0 +1,59 @@
import React, { useState } from 'react';
import RotationInput from '../customInput/RotationInput';
import PositionInput from '../customInput/PositionInputs';
import { LockIcon } from '../../../icons/RealTimeVisulationIcons';
import { LayeringBottomIcon, LayeringTopIcon, ValueUpdateIcon } from '../../../icons/ExportCommonIcons';
import decalImage from "../../../../assets/image/sampleDecal.png"
const DecalTransformation = () => {
return (
<div className='decal-transformation-container'>
{["position", "rotation", "scale"].map((transformation) => (
<div className="transformation-wrapper">
<div className="header">{transformation}</div>
<div className="input-wrapppers">
<input type="number" name="" id="" />
<div className="icon"><ValueUpdateIcon /></div>
<input type="number" name="" id="" />
<div className="icon"><ValueUpdateIcon /></div>
<input type="number" name="" id="" />
<div className="icon"><ValueUpdateIcon /></div>
<div className="icon"><LockIcon /></div>
</div>
</div>
))}
<div className="transformation-wrapper opacity">
<div className="header">opacity</div>
<div className="input-wrapppers">
<input type="number" name="" id="" />
<div className="icon"><ValueUpdateIcon /></div>
</div>
</div>
<div className="transformation-wrapper opacity">
<div className="header">Layering</div>
<div className="layers">
<div className="icon">
<LayeringBottomIcon />
</div>
<div className="icon">
<LayeringTopIcon />
</div>
</div>
</div>
<div className="preview">
<img src={decalImage} alt="" />
<div className="replace-btn">replace</div>
</div>
</div>
);
};
export default DecalTransformation;

View File

@@ -0,0 +1,240 @@
import React, { useState } from 'react'
import Search from '../../../ui/inputs/Search'
import RegularDropDown from '../../../ui/inputs/RegularDropDown'
import { ClockThreeIcon, HourGlassIcon, ListTaskIcon, LocationPinIcon, SlectedTickIcon, TargetIcon } from '../../../icons/ExportCommonIcons'
const Hrm = () => {
const [selectType, setSelectType] = useState("assetManagement")
const [selectcatogory, setSelectCatogary] = useState("All People")
const employee_details = [
{
"employee": {
image: "",
"name": "John Doe",
"employee_id": "HR-204",
"status": "Active",
},
"task": {
"status": "Ongoing",
"title": "Inspecting Machine X",
"location": {
"floor": 4,
"zone": "B"
},
"planned_time_hours": 6,
"time_spent_hours": 2,
"total_tasks": 12,
"completed_tasks": 3
},
"actions": [
"Assign Task",
"Reassign Task",
"Pause",
"Emergency Stop"
],
"location": "Floor 4 . Zone B"
},
{
"employee": {
image: "",
"name": "Alice Smith",
"employee_id": "HR-205",
"status": "Active",
},
"task": {
"status": "Ongoing",
"title": "Calibrating Sensor Y",
"location": {
"floor": 2,
"zone": "A"
},
"planned_time_hours": 4,
"time_spent_hours": 1.5,
"total_tasks": 10,
"completed_tasks": 2
},
"actions": [
"Assign Task",
"Reassign Task",
"Pause",
"Emergency Stop"
],
"location": "Floor 4 . Zone B"
},
{
"employee": {
image: "",
"name": "Michael Lee",
"employee_id": "HR-206",
"status": "Active",
},
"task": {
"status": "Ongoing",
"title": "Testing Conveyor Belt Z",
"location": {
"floor": 5,
"zone": "C"
},
"planned_time_hours": 5,
"time_spent_hours": 3,
"total_tasks": 8,
"completed_tasks": 5
},
"actions": [
"Assign Task",
"Reassign Task",
"Pause",
"Emergency Stop"
],
"location": "Floor 4 . Zone B"
}
]
return (
<div className='hrm-container'>
<div className="navigation-wrapper">
<div
className={`navigation ${selectType === "assetManagement" ? "active" : ""}`}
onClick={() => setSelectType("assetManagement")}
>
Asset Management
</div>
<div
className={`navigation ${selectType === "peopleOperation" ? "active" : ""}`}
onClick={() => setSelectType("peopleOperation")}
>
People Operations
</div>
</div>
<div className="search-container">
<Search onChange={() => { }} />
<div className="select-catagory">
<RegularDropDown
header={"floor"}
options={["floor"]} // Pass layout names as options
onSelect={() => { }}
search={false}
/>
</div>
</div>
<div className="catagories-wrapper">
{["All People", "Technician", "Operator", "Supervisor", "Safety Officer"].map((cat, index) => (
<div
key={cat}
className={`catagory ${selectcatogory === cat ? "active" : ''}`}
onClick={() => setSelectCatogary(cat)}
>
{cat}
</div>
))}
</div>
<div className="analysis-container">
{employee_details.map((employee, index) => (
<div className="analysis-wrapper">
<header>
<div className="user-details">
<div className="user-image-wrapper">
<img className='user-image' src={employee.employee.image} alt="" />
<div className={`status ${employee.employee.status}`}></div>
</div>
<div className="details">
<div className="employee-name">{employee.employee.name}</div>
<div className="employee-id">{employee.employee.employee_id}</div>
</div>
</div>
<div className="see-more">View more</div>
</header>
<div className="content">
{/* <div className="task-info">
<div className="task-wrapper">
<div className="task-label">
<span className='label-icon'><ListTaskIcon /></span>
<span className='label-text'>Ongoing Task:</span>
</div>
<div className="task-title">{employee.task.title}</div>
</div>
<div className="task-wrapper">
<div className="task-label">
<span className='label-icon'><LocationPinIcon /></span>
<span className='label-text'>Location:</span>
</div>
<div className="task-title">
Floor {employee.task.location.floor}. Zone {employee.task.location.zone}
</div>
</div>
</div> */}
<div className="task-stats">
<div className="stat-item">
<div className="stat-wrapper">
<span className="stat-icon"><ClockThreeIcon /></span>
<span>Planned time:</span>
</div>
<span className='stat-value'>{employee.task.planned_time_hours} hr</span>
</div>
{/* <div className="stat-item">
<div className="stat-wrapper">
<span className="stat-icon"><SlectedTickIcon /></span>
<span>Total Tasks:</span>
</div>
<span className='stat-value'>{employee.task.total_tasks}</span>
</div>
<div className="stat-item">
<div className="stat-wrapper">
<span className="stat-icon"><HourGlassIcon /></span>
<span>Time Spent:</span>
</div>
<span className='stat-value'>{employee.task.time_spent_hours} hr</span>
</div> */}
<div className="stat-item">
<div className="stat-wrapper">
<span className="stat-icon"><TargetIcon /></span>
<span>Completed Tasks:</span>
</div>
<span className='stat-value'>{employee.task.completed_tasks}</span>
</div>
</div>
<div className="location-wrapper">
<div className="location-header">
<div className="icon">
<LocationPinIcon />
</div>
<div className="header">Location:</div>
</div>
<div className="location-value">{employee.location}</div>
</div>
<div className="task-actions">
{/* <button className="btn btn-default">Assign Task</button>
<button className="btn btn-default">Reassign Task</button> */}
<button className="btn btn-default">Pause</button>
<button className="btn btn-danger">Emergency Stop</button>
</div>
</div>
</div>
))}
</div>
</div>
)
}
export default Hrm