diff --git a/app/src/assets/image/sampleDecal.png b/app/src/assets/image/sampleDecal.png
new file mode 100644
index 0000000..ed8c9fd
Binary files /dev/null and b/app/src/assets/image/sampleDecal.png differ
diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx
index 7cee77f..691f50f 100644
--- a/app/src/components/icons/ExportCommonIcons.tsx
+++ b/app/src/components/icons/ExportCommonIcons.tsx
@@ -1345,4 +1345,135 @@ export const SuccessIcon = () => {
)
+}
+
+
+export const AlertIcon = () => {
+ return (
+
+
+ )
+}
+export const NavigationIcon = () => {
+ return (
+
+
+ )
+}
+export const HangTagIcon = () => {
+ return (
+
+
+ )
+}
+export const DecalInfoIcon = () => {
+ return (
+
+
+ )
+}
+
+
+export const LayeringBottomIcon = () => {
+ return (
+
+ )
+}
+
+export const LayeringTopIcon = () => {
+ return (
+
+
+ )
+}
+
+export const ValueUpdateIcon = () => {
+ return (
+
+
+
+ )
+}
+
+
+export const ListTaskIcon = () => {
+ return (
+
+ )
+}
+
+export const LocationPinIcon = () => {
+ return (
+
+ )
+}
+
+export const ClockThreeIcon = () => {
+ return (
+
+
+ )
+}
+
+export const SlectedTickIcon = () => {
+ return (
+
+ )
+}
+
+export const HourGlassIcon = () => {
+ return (
+
+ )
+}
+
+export const TargetIcon = () => {
+ return (
+
+ )
}
\ No newline at end of file
diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx
index 9ea5f1a..bc3a445 100644
--- a/app/src/components/layout/sidebarLeft/Assets.tsx
+++ b/app/src/components/layout/sidebarLeft/Assets.tsx
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import Search from "../../ui/inputs/Search";
import { getCategoryAsset } from "../../../services/factoryBuilder/asset/assets/getCategoryAsset";
import { fetchAssets } from "../../../services/marketplace/fetchAssets";
-import { useSelectedItem } from "../../../store/builder/store";
+import { useDecalStore, useSelectedItem } from "../../../store/builder/store";
// images -------------------
import vehicle from "../../../assets/image/categories/vehicles.png";
@@ -15,6 +15,7 @@ import safety from "../../../assets/image/categories/safety.png";
import feneration from "../../../assets/image/categories/feneration.png";
import decal from "../../../assets/image/categories/decal.png";
import SkeletonUI from "../../templates/SkeletonUI";
+import { AlertIcon, DecalInfoIcon, HangTagIcon, NavigationIcon } from "../../icons/ExportCommonIcons";
// -------------------------------------
interface AssetProp {
@@ -113,6 +114,16 @@ const Assets: React.FC = () => {
}
};
+
+ const activeSubcategories = [
+ { name: "Safety", icon: },
+ { name: "Navigation", icon: },
+ { name: "Branding", icon: },
+ { name: "Informational", icon: }
+ ]
+
+
+ const { selectedSubCategory, setSelectedSubCategory } = useDecalStore();
return (
@@ -183,6 +194,19 @@ const Assets: React.FC = () => {
← Back
+
+ {selectedCategory === "Decals" &&
+ <>
+
+ {activeSubcategories.map((cat, index) => (
+
setSelectedSubCategory(cat.name)}>
+
{cat.icon}
+
{cat.name}
+
+ ))}
+
+ >
+ }
{categoryAssets?.map((asset: any, index: number) => (
{
@@ -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
("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 ;
case "visualization":
return ;
+ case "decals":
+ return ;
default:
return null;
}
@@ -245,6 +258,7 @@ const SideBarRight: React.FC = () => {
{renderComponent()}
+ {/* */}
)}
diff --git a/app/src/components/layout/sidebarRight/decals/DecalTransformation.tsx b/app/src/components/layout/sidebarRight/decals/DecalTransformation.tsx
new file mode 100644
index 0000000..39a30d7
--- /dev/null
+++ b/app/src/components/layout/sidebarRight/decals/DecalTransformation.tsx
@@ -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 (
+
+ {["position", "rotation", "scale"].map((transformation) => (
+
+ ))}
+
+
+
+
+
+
+

+
replace
+
+
+ );
+};
+
+export default DecalTransformation;
+
diff --git a/app/src/components/layout/sidebarRight/hrm/Hrm.tsx b/app/src/components/layout/sidebarRight/hrm/Hrm.tsx
new file mode 100644
index 0000000..2b4b44e
--- /dev/null
+++ b/app/src/components/layout/sidebarRight/hrm/Hrm.tsx
@@ -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 (
+
+
+
setSelectType("assetManagement")}
+ >
+ Asset Management
+
+
setSelectType("peopleOperation")}
+ >
+ People Operations
+
+
+
+
+
{ }} />
+
+ { }}
+ search={false}
+ />
+
+
+
+
+ {["All People", "Technician", "Operator", "Supervisor", "Safety Officer"].map((cat, index) => (
+
setSelectCatogary(cat)}
+ >
+ {cat}
+
+ ))}
+
+
+
+ {employee_details.map((employee, index) => (
+
+
+
+
+

+
+
+
+
{employee.employee.name}
+
{employee.employee.employee_id}
+
+
+
+ View more
+
+
+
+ {/*
+
+
+
+ Ongoing Task:
+
+
{employee.task.title}
+
+
+
+
+
+
+ Location:
+
+
+ Floor {employee.task.location.floor}. Zone {employee.task.location.zone}
+
+
+
*/}
+
+
+
+
+
+
+ Planned time:
+
+
+
{employee.task.planned_time_hours} hr
+
+ {/*
+
+
+
+ Total Tasks:
+
+
+
{employee.task.total_tasks}
+
+
+
+
+
+ Time Spent:
+
+
+
{employee.task.time_spent_hours} hr
+
*/}
+
+
+
+
+ Completed Tasks:
+
+
+
{employee.task.completed_tasks}
+
+
+
+
+
+
{employee.location}
+
+
+ {/*
+ */}
+
+
+
+
+
+
+ ))}
+
+
+ )
+}
+
+export default Hrm
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx
index 7afa982..a45569f 100644
--- a/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx
@@ -9,6 +9,7 @@ import { useParams } from "react-router-dom";
import { getUserData } from "../../../../../functions/getUserData";
import { useSceneContext } from "../../../sceneContext";
import { useVersionContext } from "../../../../builder/version/versionContext";
+import { handleAssetPositionSnap } from "./functions/handleAssetPositionSnap";
// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
@@ -35,11 +36,16 @@ const DuplicationControls3D = ({
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
+ const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
+ const [axisConstraint, setAxisConstraint] = useState<"x" | "z" | null>(null);
const [dragOffset, setDragOffset] = useState(null);
const [initialPositions, setInitialPositions] = useState>({});
const [isDuplicating, setIsDuplicating] = useState(false);
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
const { contextAction, setContextAction } = useContextActionStore()
+ const fineMoveBaseRef = useRef(null);
+ const lastPointerPositionRef = useRef(null);
+ const wasShiftHeldRef = useRef(false);
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
const pointPosition = new THREE.Vector3().copy(point.position);
@@ -85,11 +91,33 @@ const DuplicationControls3D = ({
removeAsset(obj.userData.modelUuid);
});
}
+ setKeyEvent("");
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
+ if (isDuplicating && duplicatedObjects.length > 0) {
+ if (event.key.toLowerCase() === 'x') {
+ setAxisConstraint(prev => prev === 'x' ? null : 'x');
+ event.preventDefault();
+ return;
+ }
+ if (event.key.toLowerCase() === 'z') {
+ setAxisConstraint(prev => prev === 'z' ? null : 'z');
+ event.preventDefault();
+ return;
+ }
+ }
+
+ if (keyCombination !== keyEvent) {
+ if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
+ setKeyEvent(keyCombination);
+ } else {
+ setKeyEvent("");
+ }
+ }
+
if (keyCombination === "Ctrl+D" && movedObjects.length === 0 && rotatedObjects.length === 0) {
duplicateSelection();
}
@@ -102,11 +130,34 @@ const DuplicationControls3D = ({
}
};
+ const onKeyUp = (event: KeyboardEvent) => {
+ const keyCombination = detectModifierKeys(event);
+
+ if (keyCombination === "") {
+ setKeyEvent("");
+ } else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
+ setKeyEvent(keyCombination);
+ }
+ if (duplicatedObjects[0] && keyEvent !== "" && keyCombination === '') {
+ const intersectionPoint = new THREE.Vector3();
+ raycaster.setFromCamera(pointer, camera);
+ const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
+ if (hit) {
+ const model = scene.getObjectByProperty("uuid", duplicatedObjects[0].userData.modelUuid);
+ if (model) {
+ const newOffset = calculateDragOffset(model, intersectionPoint);
+ setDragOffset(newOffset);
+ }
+ }
+ }
+ };
+
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
+ canvasElement.addEventListener("keyup", onKeyUp);
}
return () => {
@@ -114,8 +165,9 @@ const DuplicationControls3D = ({
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
+ canvasElement.addEventListener("keyup", onKeyUp);
};
- }, [assets, camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, rotatedObjects]);
+ }, [assets, camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, rotatedObjects, keyEvent]);
useFrame(() => {
if (!isDuplicating || duplicatedObjects.length === 0) return;
@@ -135,7 +187,9 @@ const DuplicationControls3D = ({
}
if (dragOffset) {
- const adjustedHit = new THREE.Vector3().addVectors(intersectionPoint, dragOffset);
+ const rawBasePosition = new THREE.Vector3().addVectors(intersectionPoint, dragOffset);
+ const model = scene.getObjectByProperty("uuid", duplicatedObjects[0].userData.modelUuid);
+ const baseNewPosition = handleAssetPositionSnap({ rawBasePosition, intersectionPoint, model, axisConstraint, keyEvent, fineMoveBaseRef, lastPointerPositionRef, wasShiftHeldRef });
duplicatedObjects.forEach((duplicatedObject: THREE.Object3D) => {
if (duplicatedObject.userData.modelUuid) {
@@ -149,7 +203,7 @@ const DuplicationControls3D = ({
);
const model = scene.getObjectByProperty("uuid", duplicatedObject.userData.modelUuid);
- const newPosition = new THREE.Vector3().addVectors(adjustedHit, relativeOffset);
+ const newPosition = new THREE.Vector3().addVectors(baseNewPosition, relativeOffset);
const positionArray: [number, number, number] = [newPosition.x, newPosition.y, newPosition.z];
if (model) {
@@ -162,6 +216,21 @@ const DuplicationControls3D = ({
}
});
+ useEffect(() => {
+ if (duplicatedObjects.length > 0) {
+ const intersectionPoint = new THREE.Vector3();
+ raycaster.setFromCamera(pointer, camera);
+ const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
+ if (hit) {
+ const model = scene.getObjectByProperty("uuid", duplicatedObjects[0].userData.modelUuid);
+ if (model) {
+ const newOffset = calculateDragOffset(model, intersectionPoint);
+ setDragOffset(newOffset);
+ }
+ }
+ }
+ }, [axisConstraint, camera, duplicatedObjects])
+
const duplicateSelection = useCallback(() => {
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
const positions: Record = {};
@@ -593,6 +662,7 @@ const DuplicationControls3D = ({
setSelectedAssets([]);
setIsDuplicating(false);
setDragOffset(null);
+ setAxisConstraint(null);
};
return null;
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/functions/handleAssetPositionSnap.ts b/app/src/modules/scene/controls/selectionControls/selection3D/functions/handleAssetPositionSnap.ts
new file mode 100644
index 0000000..00b6924
--- /dev/null
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/functions/handleAssetPositionSnap.ts
@@ -0,0 +1,75 @@
+import * as THREE from "three";
+
+export function handleAssetPositionSnap({
+ rawBasePosition,
+ intersectionPoint,
+ model,
+ axisConstraint,
+ keyEvent,
+ fineMoveBaseRef,
+ lastPointerPositionRef,
+ wasShiftHeldRef
+}: {
+ rawBasePosition: THREE.Vector3;
+ intersectionPoint: THREE.Vector3;
+ model: THREE.Object3D | undefined;
+ axisConstraint: "x" | "z" | null;
+ keyEvent: string;
+ fineMoveBaseRef: React.MutableRefObject;
+ lastPointerPositionRef: React.MutableRefObject;
+ wasShiftHeldRef: React.MutableRefObject;
+}): THREE.Vector3 {
+ const CTRL_DISTANCE = 0.5;
+ const SHIFT_DISTANCE = 0.05;
+ const CTRL_SHIFT_DISTANCE = 0.05;
+
+ const isShiftHeld = keyEvent.includes("Shift");
+
+ // Handle Shift toggle state
+ if (isShiftHeld !== wasShiftHeldRef.current && model) {
+ if (isShiftHeld) {
+ fineMoveBaseRef.current = model.position.clone();
+ lastPointerPositionRef.current = intersectionPoint.clone();
+ } else {
+ fineMoveBaseRef.current = null;
+ }
+ wasShiftHeldRef.current = isShiftHeld;
+ }
+
+ // Start from raw
+ let baseNewPosition = rawBasePosition.clone();
+
+ // Apply snapping / fine move
+ if (keyEvent === "Ctrl") {
+ baseNewPosition.set(
+ Math.round(baseNewPosition.x / CTRL_DISTANCE) * CTRL_DISTANCE,
+ baseNewPosition.y,
+ Math.round(baseNewPosition.z / CTRL_DISTANCE) * CTRL_DISTANCE
+ );
+ } else if (keyEvent === "Ctrl+Shift") {
+ if (isShiftHeld && fineMoveBaseRef.current && lastPointerPositionRef.current) {
+ const pointerDelta = new THREE.Vector3().subVectors(intersectionPoint, lastPointerPositionRef.current);
+ baseNewPosition = fineMoveBaseRef.current.clone().add(pointerDelta.multiplyScalar(SHIFT_DISTANCE));
+ }
+ baseNewPosition.set(
+ Math.round(baseNewPosition.x / CTRL_SHIFT_DISTANCE) * CTRL_SHIFT_DISTANCE,
+ baseNewPosition.y,
+ Math.round(baseNewPosition.z / CTRL_SHIFT_DISTANCE) * CTRL_SHIFT_DISTANCE
+ );
+ } else if (isShiftHeld && fineMoveBaseRef.current && lastPointerPositionRef.current) {
+ const pointerDelta = new THREE.Vector3().subVectors(intersectionPoint, lastPointerPositionRef.current);
+ baseNewPosition = fineMoveBaseRef.current.clone().add(pointerDelta.multiplyScalar(SHIFT_DISTANCE));
+ }
+
+ // Apply axis constraint last
+ if (axisConstraint && model) {
+ const currentBasePosition = model.position.clone();
+ if (axisConstraint === 'x') {
+ baseNewPosition.set(baseNewPosition.x, currentBasePosition.y, currentBasePosition.z);
+ } else if (axisConstraint === 'z') {
+ baseNewPosition.set(currentBasePosition.x, currentBasePosition.y, baseNewPosition.z);
+ }
+ }
+
+ return baseNewPosition;
+}
\ No newline at end of file
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/functions/handleAssetRotationSnap.ts b/app/src/modules/scene/controls/selectionControls/selection3D/functions/handleAssetRotationSnap.ts
new file mode 100644
index 0000000..ca16720
--- /dev/null
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/functions/handleAssetRotationSnap.ts
@@ -0,0 +1,74 @@
+import * as THREE from "three";
+
+export function handleAssetRotationSnap({
+ object,
+ pointerDeltaX,
+ keyEvent,
+ snapBaseRef,
+ prevRotationRef,
+ wasShiftHeldRef
+}: {
+ object: THREE.Object3D;
+ pointerDeltaX: number;
+ keyEvent: string;
+ snapBaseRef: React.MutableRefObject;
+ prevRotationRef: React.MutableRefObject;
+ wasShiftHeldRef: React.MutableRefObject;
+}): number {
+ const SHIFT_SPEED = 0.5; // Fine rotation speed
+ const NORMAL_SPEED = 5; // Normal rotation speed
+ const CTRL_SNAP_DEG = 15; // 15 degrees
+ const CTRL_SHIFT_SNAP_DEG = 5; // 5 degrees
+
+ const isShiftHeld = keyEvent.includes("Shift");
+ const isCtrlHeld = keyEvent.includes("Ctrl");
+
+ const speedFactor = isShiftHeld ? SHIFT_SPEED : NORMAL_SPEED;
+ let deltaAngle = pointerDeltaX * speedFactor;
+
+ // Track if modifier changed
+ const modifierChanged = isShiftHeld !== wasShiftHeldRef.current;
+ if (modifierChanged) {
+ wasShiftHeldRef.current = isShiftHeld;
+ if (isCtrlHeld) snapBaseRef.current = null;
+ }
+
+ if (isCtrlHeld) {
+ const snapDeg = isShiftHeld ? CTRL_SHIFT_SNAP_DEG : CTRL_SNAP_DEG;
+ const snapRad = THREE.MathUtils.degToRad(snapDeg);
+
+ // Get current Y rotation from object's quaternion
+ const euler = new THREE.Euler().setFromQuaternion(object.quaternion, 'YXZ');
+ let currentAngle = euler.y;
+
+ // Initialize snap base on first frame
+ if (snapBaseRef.current === null) {
+ snapBaseRef.current = currentAngle;
+ prevRotationRef.current = currentAngle;
+ }
+
+ // Accumulate the total rotation from the base
+ const totalRotation = snapBaseRef.current + deltaAngle;
+
+ // Snap to nearest increment
+ const snappedAngle = Math.round(totalRotation / snapRad) * snapRad;
+
+ // Calculate the delta needed to reach the snapped angle from current rotation
+ let targetDelta = snappedAngle - currentAngle;
+
+ // Handle wrapping around 360 degrees
+ if (Math.abs(targetDelta) > Math.PI) {
+ targetDelta = targetDelta - Math.sign(targetDelta) * 2 * Math.PI;
+ }
+
+ // Update snap base for next frame
+ snapBaseRef.current = totalRotation;
+
+ return targetDelta;
+ } else {
+ // Reset snapping when Ctrl is not held
+ snapBaseRef.current = null;
+ prevRotationRef.current = null;
+ return deltaAngle;
+ }
+}
\ No newline at end of file
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx
index 234c345..21ddd33 100644
--- a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx
@@ -5,7 +5,7 @@ import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView
import * as Types from "../../../../../types/world/worldTypes";
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
-import { snapControls } from "../../../../../utils/handleSnap";
+import { handleAssetPositionSnap } from "./functions/handleAssetPositionSnap";
import DistanceFindingControls from "./distanceFindingControls";
import { useParams } from "react-router-dom";
import { useProductContext } from "../../../../simulation/products/productContext";
@@ -34,7 +34,6 @@ function MoveControls3D({
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { socket } = useSocketStore();
- const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
const { userId, organization } = getUserData();
const { projectId } = useParams();
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
@@ -43,12 +42,17 @@ function MoveControls3D({
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
+ const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
+ const [axisConstraint, setAxisConstraint] = useState<"x" | "z" | null>(null);
const [dragOffset, setDragOffset] = useState(null);
const [initialPositions, setInitialPositions] = useState>({});
const [initialStates, setInitialStates] = useState>({});
const [isMoving, setIsMoving] = useState(false);
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
const { contextAction, setContextAction } = useContextActionStore()
+ const fineMoveBaseRef = useRef(null);
+ const lastPointerPositionRef = useRef(null);
+ const wasShiftHeldRef = useRef(false);
const updateBackend = (
productName: string,
@@ -85,10 +89,21 @@ function MoveControls3D({
};
const onKeyUp = (event: KeyboardEvent) => {
- const isModifierKey = (!event.shiftKey && !event.ctrlKey);
+ const keyCombination = detectModifierKeys(event);
- if (isModifierKey && keyEvent !== "") {
+ if (keyCombination === "") {
setKeyEvent("");
+ } else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
+ setKeyEvent(keyCombination);
+ }
+ if (movedObjects[0]) {
+ const intersectionPoint = new THREE.Vector3();
+ raycaster.setFromCamera(pointer, camera);
+ const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
+ if (hit) {
+ const newOffset = calculateDragOffset(movedObjects[0], intersectionPoint);
+ setDragOffset(newOffset);
+ }
}
};
@@ -113,7 +128,6 @@ function MoveControls3D({
clearSelection();
setMovedObjects([]);
}
- setKeyEvent("");
};
const onKeyDown = (event: KeyboardEvent) => {
@@ -122,6 +136,19 @@ function MoveControls3D({
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0)
return;
+ if (isMoving && movedObjects.length > 0) {
+ if (event.key.toLowerCase() === 'x') {
+ setAxisConstraint(prev => prev === 'x' ? null : 'x');
+ event.preventDefault();
+ return;
+ }
+ if (event.key.toLowerCase() === 'z') {
+ setAxisConstraint(prev => prev === 'z' ? null : 'z');
+ event.preventDefault();
+ return;
+ }
+ }
+
if (keyCombination !== keyEvent) {
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
setKeyEvent(keyCombination);
@@ -192,9 +219,22 @@ function MoveControls3D({
}
}
});
- }, 0)
+ setAxisConstraint(null);
+ }, 100)
}, [movedObjects, initialStates, updateAsset]);
+ useEffect(() => {
+ if (movedObjects.length > 0) {
+ const intersectionPoint = new THREE.Vector3();
+ raycaster.setFromCamera(pointer, camera);
+ const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
+ if (hit) {
+ const newOffset = calculateDragOffset(movedObjects[0], intersectionPoint);
+ setDragOffset(newOffset);
+ }
+ }
+ }, [axisConstraint, camera, movedObjects])
+
useFrame(() => {
if (!isMoving || movedObjects.length === 0) return;
@@ -213,20 +253,8 @@ function MoveControls3D({
if (dragOffset) {
const rawBasePosition = new THREE.Vector3().addVectors(intersectionPoint, dragOffset);
-
- let moveDistance = keyEvent.includes("Shift") ? 0.05 : 1;
-
- const initialBasePosition = initialPositions[movedObjects[0].uuid];
- const positionDifference = new THREE.Vector3().subVectors(rawBasePosition, initialBasePosition);
-
- let adjustedDifference = positionDifference.multiplyScalar(moveDistance);
-
- const baseNewPosition = new THREE.Vector3().addVectors(initialBasePosition, adjustedDifference);
-
- if (keyEvent.includes("Ctrl")) {
- baseNewPosition.x = snapControls(baseNewPosition.x, keyEvent);
- baseNewPosition.z = snapControls(baseNewPosition.z, keyEvent);
- }
+ const model = movedObjects[0];
+ const baseNewPosition = handleAssetPositionSnap({ rawBasePosition, intersectionPoint, model, axisConstraint, keyEvent, fineMoveBaseRef, lastPointerPositionRef, wasShiftHeldRef });
movedObjects.forEach((movedAsset: THREE.Object3D) => {
if (movedAsset.userData.modelUuid) {
@@ -430,6 +458,7 @@ function MoveControls3D({
echo.success("Object moved!");
setIsMoving(false);
clearSelection();
+ setAxisConstraint(null);
};
const clearSelection = () => {
diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx
index d3457e2..8ea8e64 100644
--- a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx
+++ b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx
@@ -9,6 +9,8 @@ import { useProductContext } from "../../../../simulation/products/productContex
import { getUserData } from "../../../../../functions/getUserData";
import { useSceneContext } from "../../../sceneContext";
import { useVersionContext } from "../../../../builder/version/versionContext";
+import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
+import { handleAssetRotationSnap } from "./functions/handleAssetRotationSnap";
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
@@ -23,7 +25,6 @@ function RotateControls3D({
setDuplicatedObjects
}: any) {
const { camera, gl, scene, pointer, raycaster } = useThree();
- const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
@@ -38,6 +39,7 @@ function RotateControls3D({
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
+ const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
const [initialRotations, setInitialRotations] = useState>({});
const [initialPositions, setInitialPositions] = useState>({});
const [isRotating, setIsRotating] = useState(false);
@@ -45,6 +47,9 @@ function RotateControls3D({
const rotationCenter = useRef(null);
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
const { contextAction, setContextAction } = useContextActionStore()
+ const snapBaseRef = useRef(null);
+ const prevRotationRef = useRef(null);
+ const wasShiftHeldRef = useRef(false);
const updateBackend = useCallback((
productName: string,
@@ -103,6 +108,8 @@ function RotateControls3D({
};
const onKeyDown = (event: KeyboardEvent) => {
+ const keyCombination = detectModifierKeys(event);
+
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return;
if (event.key.toLowerCase() === "r") {
@@ -110,6 +117,15 @@ function RotateControls3D({
rotateAssets();
}
}
+
+ if (keyCombination !== keyEvent) {
+ if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
+ setKeyEvent(keyCombination);
+ } else {
+ setKeyEvent("");
+ }
+ }
+
if (event.key.toLowerCase() === "escape") {
event.preventDefault();
resetToInitialRotations();
@@ -118,11 +134,24 @@ function RotateControls3D({
}
};
+ const onKeyUp = (event: KeyboardEvent) => {
+ const keyCombination = detectModifierKeys(event);
+
+ if (keyCombination === "") {
+ setKeyEvent("");
+ } else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
+ setKeyEvent(keyCombination);
+ }
+ const currentPointer = new THREE.Vector2(pointer.x, pointer.y);
+ prevPointerPosition.current = currentPointer.clone();
+ };
+
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
+ canvasElement?.addEventListener("keyup", onKeyUp);
}
return () => {
@@ -130,68 +159,72 @@ function RotateControls3D({
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
+ canvasElement?.removeEventListener("keyup", onKeyUp);
};
- }, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects]);
+ }, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent]);
const resetToInitialRotations = useCallback(() => {
- rotatedObjects.forEach((obj: THREE.Object3D) => {
- const uuid = obj.uuid;
- if (obj.userData.modelUuid) {
- const initialRotation = initialRotations[uuid];
- const initialPosition = initialPositions[uuid];
+ setTimeout(() => {
+ rotatedObjects.forEach((obj: THREE.Object3D) => {
+ const uuid = obj.uuid;
+ if (obj.userData.modelUuid) {
+ const initialRotation = initialRotations[uuid];
+ const initialPosition = initialPositions[uuid];
- if (initialRotation && initialPosition) {
- const rotationArray: [number, number, number] = [initialRotation.x, initialRotation.y, initialRotation.z,];
- const positionArray: [number, number, number] = [initialPosition.x, initialPosition.y, initialPosition.z,];
+ if (initialRotation && initialPosition) {
+ const rotationArray: [number, number, number] = [initialRotation.x, initialRotation.y, initialRotation.z,];
+ const positionArray: [number, number, number] = [initialPosition.x, initialPosition.y, initialPosition.z,];
- updateAsset(obj.userData.modelUuid, {
- rotation: rotationArray,
- position: positionArray,
- });
+ updateAsset(obj.userData.modelUuid, {
+ rotation: rotationArray,
+ position: positionArray,
+ });
- obj.rotation.copy(initialRotation);
- obj.position.copy(initialPosition);
+ obj.rotation.copy(initialRotation);
+ obj.position.copy(initialPosition);
+ }
}
- }
- });
+ });
+ }, 100)
}, [rotatedObjects, initialRotations, initialPositions, updateAsset]);
useFrame(() => {
if (!isRotating || rotatedObjects.length === 0) return;
- const intersectionPoint = new THREE.Vector3();
- raycaster.setFromCamera(pointer, camera);
- const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
+ const currentPointer = new THREE.Vector2(pointer.x, pointer.y);
if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) {
- if (point) {
- prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
- }
+ prevPointerPosition.current = currentPointer.clone();
return;
}
- if (point && prevPointerPosition.current && rotationCenter.current) {
+ if (prevPointerPosition.current && rotationCenter.current) {
const center = rotationCenter.current;
-
- const currentAngle = Math.atan2(point.z - center.z, point.x - center.x);
- const prevAngle = Math.atan2(
- prevPointerPosition.current.y - center.z,
- prevPointerPosition.current.x - center.x
- );
- const angleDelta = prevAngle - currentAngle;
-
- const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
+ const deltaX = currentPointer.x - prevPointerPosition.current.x;
rotatedObjects.forEach((obj: THREE.Object3D) => {
if (obj.userData.modelUuid) {
+ const angleDelta = handleAssetRotationSnap({
+ object: obj,
+ pointerDeltaX: deltaX,
+ keyEvent,
+ snapBaseRef,
+ prevRotationRef,
+ wasShiftHeldRef
+ });
+
const relativePosition = new THREE.Vector3().subVectors(obj.position, center);
+ const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
relativePosition.applyMatrix4(rotationMatrix);
obj.position.copy(center).add(relativePosition);
- obj.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), angleDelta);
+
+ const rotationQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), angleDelta);
+ obj.quaternion.multiply(rotationQuat);
+ obj.rotation.setFromQuaternion(obj.quaternion);
}
});
- prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
+ prevPointerPosition.current = currentPointer.clone();
}
});
@@ -213,17 +246,13 @@ function RotateControls3D({
setInitialRotations(rotations);
setInitialPositions(positions);
- const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
- const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
- if (point) {
- prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
- }
+ prevPointerPosition.current = new THREE.Vector2(pointer.x, pointer.y);
setRotatedObjects(selectedAssets);
setIsRotating(true);
- }, [selectedAssets, camera, pointer, raycaster, plane]);
+ }, [selectedAssets, camera, pointer, raycaster]);
const placeRotatedAssets = useCallback(() => {
if (rotatedObjects.length === 0) return;
diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts
index f48312a..18e8bfd 100644
--- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts
+++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts
@@ -6,7 +6,7 @@ import { useProductContext } from "../../../products/productContext";
import { useHumanEventManager } from "../../../human/eventManager/useHumanEventManager";
export function useRetrieveHandler() {
- const { materialStore, armBotStore, machineStore, vehicleStore, storageUnitStore, conveyorStore, craneStore, productStore, humanStore, assetStore } = useSceneContext();
+ const { materialStore, armBotStore, machineStore, vehicleStore, storageUnitStore, conveyorStore, craneStore, productStore, humanStore, assetStore, humanEventManagerRef } = useSceneContext();
const { selectedProductStore } = useProductContext();
const { addMaterial } = materialStore();
const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = productStore();
@@ -464,11 +464,20 @@ export function useRetrieveHandler() {
if (action && action.actionType === 'pickAndDrop' && !hasLock && !crane.isCarrying && !crane.isActive && crane.currentLoad < (action?.maxPickUpCount || 0)) {
const material = getLastMaterial(storageUnit.modelUuid);
if (material) {
- incrementCraneLoad(crane.modelUuid, 1);
- addCurrentActionToCrane(crane.modelUuid, action.actionUuid, material.materialType, material.materialId);
- addCurrentMaterialToCrane(crane.modelUuid, material.materialType, material.materialId);
-
- cranePickupLockRef.current.set(crane.modelUuid, true);
+ if (action.triggers[0].triggeredAsset?.triggeredModel.modelUuid && action.triggers[0].triggeredAsset.triggeredAction?.actionUuid) {
+ const human = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0].triggeredAsset.triggeredModel.modelUuid);
+ if (human && human.type === 'human') {
+ if (!monitoredHumansRef.current.has(human.modelUuid)) {
+ addHumanToMonitor(human.modelUuid, () => {
+ incrementCraneLoad(crane.modelUuid, 1);
+ addCurrentActionToCrane(crane.modelUuid, action.actionUuid, material.materialType, material.materialId);
+ addCurrentMaterialToCrane(crane.modelUuid, material.materialType, material.materialId);
+ cranePickupLockRef.current.set(crane.modelUuid, true);
+ }, action.triggers[0].triggeredAsset.triggeredAction?.actionUuid)
+ }
+ monitoredHumansRef.current.add(human.modelUuid);
+ }
+ }
}
} else if (crane.isCarrying && crane.currentPhase === 'pickup-drop' && hasLock) {
cranePickupLockRef.current.set(crane.modelUuid, false);
diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts
index 6c3c12e..1e00e68 100644
--- a/app/src/store/builder/store.ts
+++ b/app/src/store/builder/store.ts
@@ -638,7 +638,21 @@ export const useSelectedPath = create((set: any) => ({
selectedPath: "auto",
setSelectedPath: (x: any) => set({ selectedPath: x }),
}));
+
export const useContextActionStore = create((set: any) => ({
contextAction: null,
setContextAction: (x: any) => set({ contextAction: x }),
}));
+
+
+// Define the store's state and actions type
+interface DecalStore {
+ selectedSubCategory: string;
+ setSelectedSubCategory: (subCategory: string) => void;
+}
+
+// Create the Zustand store with types
+export const useDecalStore = create((set) => ({
+ selectedSubCategory: '',
+ setSelectedSubCategory: (subCategory: string) => set({ selectedSubCategory: subCategory }),
+}));
diff --git a/app/src/styles/layout/hrm.scss b/app/src/styles/layout/hrm.scss
new file mode 100644
index 0000000..b2f6c43
--- /dev/null
+++ b/app/src/styles/layout/hrm.scss
@@ -0,0 +1,251 @@
+@use "../abstracts/variables" as *;
+@use "../abstracts/mixins" as *;
+
+.hrm-container {
+
+
+ .navigation-wrapper {
+ @include flex-space-between;
+ justify-content: space-around;
+
+ .navigation {
+
+ padding: 4px 12px;
+ border-radius: 20px;
+ text-wrap: nowrap;
+ margin: 6px 0;
+ cursor: pointer;
+
+ &.active {
+ background: var(--background-color-button);
+ }
+ }
+
+ }
+
+ .search-container {
+ position: relative;
+ padding: 2px 2px;
+
+ .search-wrapper {
+ input {
+ padding-right: 85px;
+ }
+ }
+
+ .select-catagory {
+ position: absolute;
+ top: 50%;
+ right: 16px;
+ transform: translate(0, -50%);
+ z-index: 10;
+
+ .regularDropdown-container {
+ padding: 2px 8px;
+ }
+ }
+ }
+
+ .catagories-wrapper {
+ display: flex;
+ gap: 12px;
+ width: 100%;
+ overflow: auto;
+ padding: 8px 10px;
+
+ .catagory {
+ text-wrap: nowrap;
+ position: relative;
+ cursor: pointer;
+
+ &.active {
+ &::after {
+ content: "";
+ position: absolute;
+ bottom: -6px;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ border-radius: 100px;
+ background: var(--background-color-button);
+ }
+
+ }
+ }
+ }
+
+ .analysis-container {
+ max-height: calc(62vh - 12px);
+ overflow: auto;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ margin-top: 7px;
+
+ .analysis-wrapper {
+ border: 1px solid var(--Color-Hover, #CCACFF);
+ border-radius: 20px;
+ padding: 16px;
+
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+
+ header {
+ position: relative;
+ @include flex-space-between;
+ padding: 3px 0;
+
+ .user-details {
+ display: flex;
+ gap: 6px;
+
+ .user-image-wrapper {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background-color: #fff;
+ position: relative;
+
+ .status {
+ border-radius: 50%;
+ width: 6px;
+ height: 6px;
+ outline: 1px solid #2F2C32;
+
+ position: absolute;
+ bottom: 0;
+ right: 0;
+
+ &.Active {
+ background-color: #44E5C6;
+ }
+ }
+ }
+
+ .details {
+ .employee-id {
+ color: #B7B7C6;
+ font-size: $tiny;
+
+ }
+ }
+ }
+
+ .see-more {
+ color: var(--accent-color);
+ text-decoration: underline;
+ cursor: pointer;
+ }
+
+ &::after {
+ content: "";
+ position: absolute;
+ bottom: -7px;
+ left: 0;
+ width: 100%;
+ height: 1px;
+ background-color: #6F6F7A;
+ }
+ }
+
+ .content {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+
+ .task-info {
+ padding: 8px 0;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+
+ .task-wrapper {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .task-label {
+ display: flex;
+ align-items: center;
+ gap: 3px;
+
+ .label-text {
+ color: #B7B7C6;
+ }
+ }
+ }
+ }
+
+ .task-stats {
+ display: grid;
+ grid-template-columns: repeat(1, 1fr); // Two equal-width columns
+ gap: 4px;
+
+ .stat-item {
+ border-radius: 100px;
+ @include flex-space-between;
+ background: linear-gradient(162.53deg,
+ rgba(51, 51, 51, 0.7) 0%,
+ rgba(45, 36, 55, 0.7) 106.84%);
+ border: 1px solid #FFFFFF0D;
+ padding: 6px;
+
+ .stat-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ }
+
+ span,
+ .stat-value {
+ font-size: 10px;
+ display: flex;
+ }
+ }
+ }
+
+ .location-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 6px 0;
+
+ .location-header {
+ display: flex;
+ gap: 6px;
+
+ .icon {
+ display: flex;
+ }
+
+ .header {
+ font-size: 12px;
+ color: #B7B7C6;
+ }
+ }
+ }
+
+ .task-actions {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr); // Two equal-width columns
+ gap: 4px;
+ margin-top: 3px;
+
+ button {
+ line-height: 133%;
+ font-size: 11px;
+ border: 1px solid var(--Linear-Border, #564B69);
+ border-radius: 100px;
+ padding: 4px 0;
+
+ &:last-child {
+ background-color: #CC2C1E;
+ }
+ }
+ }
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss
index 9030be7..ef5aaee 100644
--- a/app/src/styles/layout/sidebar.scss
+++ b/app/src/styles/layout/sidebar.scss
@@ -468,6 +468,96 @@
position: relative;
width: 304px;
+ .decal-transformation-container {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+
+ .transformation-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 6px 12px;
+
+ .header {
+ flex: 1;
+ text-transform: capitalize;
+ }
+
+ .input-wrapppers {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ flex: 1.5;
+
+ svg {
+ stroke: #CCACFF;
+ }
+
+ .icon {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ input {
+ min-width: 43px;
+ }
+ }
+
+ .layers {
+ display: flex;
+ gap: 6px;
+ align-items: center;
+
+ .icon {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ outline: 1px solid var(--border-color);
+ padding: 4px 16px;
+ width: 50px;
+ border-radius: 100px;
+ }
+ }
+ }
+
+ .opacity {
+ input {
+ min-width: 190px !important;
+ }
+ }
+
+ .preview {
+ width: 100%;
+ height: 150px;
+ border-radius: 20px;
+ outline: 1px solid var(--border-color);
+ position: relative;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ }
+
+ .replace-btn {
+ background-color: #6F42C1;
+ border-radius: 100px;
+ color: #FFFFFF;
+ padding: 4px 16px;
+ width: fit-content;
+ cursor: pointer;
+ font-size: 12px;
+ text-transform: capitalize;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+ }
+ }
+
.version-history-container {
max-height: calc(62vh - 12px);
display: flex;
@@ -1978,6 +2068,43 @@
}
}
+ .catogory-asset-filter {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ border: 1px solid #564B69;
+ padding: 12px 10px;
+ border-radius: 15px;
+
+ .catogory-asset-filter-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+ border: 1px solid #564B69;
+ padding: 4px 8px;
+ border-radius: 100px;
+ cursor: pointer;
+
+ .sub-catagory {
+ display: flex;
+ }
+
+ &.active {
+ background-color: #6F42C1;
+
+ .sub-catagory {
+
+ color: #FFFFFF;
+ }
+
+ // svg {
+ // stroke: white;
+ // fill: white;
+ // }
+ }
+ }
+ }
+
.assets-container {
width: 100%;
display: flex;
diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss
index b73439f..1c3a1b8 100644
--- a/app/src/styles/main.scss
+++ b/app/src/styles/main.scss
@@ -37,6 +37,7 @@
@use "layout/skeleton";
@use "layout/compareLayoutPopUp";
@use "layout/compareLayout";
+@use "layout/hrm";
// pages
@use "pages/dashboard";
diff --git a/app/src/styles/pages/forgotPassword.scss b/app/src/styles/pages/forgotPassword.scss
index 4965029..7af55b4 100644
--- a/app/src/styles/pages/forgotPassword.scss
+++ b/app/src/styles/pages/forgotPassword.scss
@@ -95,6 +95,18 @@
}
}
}
+
+ .resend {
+ span {
+ color: var(--highlight-text-color);
+ }
+
+ &.disabled {
+ span {
+ color: var(--text-disabled);
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/styles/scene/scene.scss b/app/src/styles/scene/scene.scss
index 4db9e8b..6358c81 100644
--- a/app/src/styles/scene/scene.scss
+++ b/app/src/styles/scene/scene.scss
@@ -134,3 +134,11 @@
align-items: center;
}
}
+
+.stats{
+ top: auto !important;
+ bottom: 36px !important;
+ left: 12px !important;
+ border-radius: 6px;
+ overflow: hidden;
+}
diff --git a/app/src/utils/handleSnap.ts b/app/src/utils/handleSnap.ts
deleted file mode 100644
index bd6a74d..0000000
--- a/app/src/utils/handleSnap.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-export function snapControls(value: number, event: string): number {
- const CTRL_DISTANCE = 1; // Snap to whole numbers when Ctrl is pressed
- const SHIFT_DISTANCE = 0.01; // Snap to half-step increments when Shift is pressed
- const CTRL_SHIFT_DISTANCE = 0.1; // Snap to fine increments when both Ctrl and Shift are pressed
-
- switch (event) {
- case "Ctrl":
- return Math.round(value / CTRL_DISTANCE) * CTRL_DISTANCE;
-
- case "Shift":
- return Math.round(value / SHIFT_DISTANCE) * SHIFT_DISTANCE;
-
- case "Ctrl+Shift":
- const base = Math.floor(value / CTRL_DISTANCE) * CTRL_DISTANCE;
- const offset =
- Math.round((value - base) / CTRL_SHIFT_DISTANCE) * CTRL_SHIFT_DISTANCE;
- return base + offset;
-
- default:
- return value; // No snapping if no modifier key is pressed
- }
-}
diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts
index f9b8cb8..507e31b 100644
--- a/app/src/utils/shortcutkeys/handleShortcutKeys.ts
+++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts
@@ -84,7 +84,7 @@ const KeyPressListener: React.FC = () => {
H: "free-hand",
};
const tool = toolMap[key];
- if (tool) {
+ if (tool && selectedAssets.length === 0) {
setActiveTool(tool);
setActiveSubTool(tool);
}