Compare commits
11 Commits
ui
...
dev-resour
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d2a42b7bd | |||
| 6fa4d5323d | |||
| e813f194c7 | |||
| 2125c3dc73 | |||
| cd86846a37 | |||
| 6026c3b82b | |||
| 358ce22767 | |||
| ff22152d8c | |||
| 6aeef163d2 | |||
| 246236c15f | |||
| affffe09c8 |
@@ -78,8 +78,8 @@ const AssetProperties: React.FC = () => {
|
||||
|
||||
<section>
|
||||
<div className="header">User Data</div>
|
||||
{userData.map((data) => (
|
||||
<div className="input-container">
|
||||
{userData.map((data, i) => (
|
||||
<div className="input-container" key={i}>
|
||||
<InputWithDropDown
|
||||
key={data.id}
|
||||
label={data.label}
|
||||
@@ -103,9 +103,16 @@ const AssetProperties: React.FC = () => {
|
||||
</section>
|
||||
<div className="header">Animations</div>
|
||||
<section className="animations-lists">
|
||||
{assets.map((asset) => {
|
||||
{assets.map((asset, i) => {
|
||||
if (asset.modelUuid !== selectedFloorItem.uuid || !asset.animations)
|
||||
return null;
|
||||
return (
|
||||
i === 0 && (
|
||||
<div className="no-animation">
|
||||
Looks like there are no preset animations yet. Stay tuned for
|
||||
future additions!
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
return asset.animations.map((animation, index) => (
|
||||
<div key={index} className="animations-list-wrapper">
|
||||
|
||||
@@ -1,190 +1,200 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ClockThreeIcon, LocationPinIcon, TargetIcon } from '../../../../icons/ExportCommonIcons'
|
||||
import { useSceneContext } from '../../../../../modules/scene/sceneContext';
|
||||
import { useProductContext } from '../../../../../modules/simulation/products/productContext';
|
||||
import RenameInput from '../../../../ui/inputs/RenameInput';
|
||||
import { useResourceManagementId } from '../../../../../store/builder/store';
|
||||
import { getAssetThumbnail } from '../../../../../services/factoryBuilder/asset/assets/getAssetThumbnail';
|
||||
// import NavigateCatagory from '../NavigateCatagory'
|
||||
|
||||
const Hrm = () => {
|
||||
const [selectedCard, setSelectedCard] = useState(0);
|
||||
const [workers, setWorkers] = useState<any[]>([]);
|
||||
const { setResourceManagementId } = useResourceManagementId();
|
||||
const { assetStore } = useSceneContext();
|
||||
const { assets: allAssets } = assetStore();
|
||||
|
||||
const { productStore } = useSceneContext();
|
||||
const { products, getProductById } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProduct) {
|
||||
const productDetails = getProductById(selectedProduct.productUuid);
|
||||
const workerDetails = productDetails?.eventDatas || [];
|
||||
|
||||
const formattedWorkers = workerDetails
|
||||
.filter((worker: any) => worker.type === "human")
|
||||
.map((worker: any, index: number) => ({
|
||||
employee: {
|
||||
image: "",
|
||||
name: worker.modelName,
|
||||
employee_id: `HR-${204 + index}`,
|
||||
status: "Active",
|
||||
},
|
||||
task: {
|
||||
status: "Ongoing",
|
||||
title: worker.taskTitle || "No Task Assigned",
|
||||
location: {
|
||||
floor: worker.floor || 0,
|
||||
zone: worker.zone || "N/A"
|
||||
},
|
||||
planned_time_hours: worker.plannedTime || 0,
|
||||
time_spent_hours: worker.timeSpent || 0,
|
||||
total_tasks: worker.totalTasks || 0,
|
||||
completed_tasks: worker.completedTasks || 0
|
||||
},
|
||||
actions: [
|
||||
"Assign Task",
|
||||
"Reassign Task",
|
||||
"Pause",
|
||||
"Emergency Stop"
|
||||
],
|
||||
location: `Floor ${worker.floor || "-"} . Zone ${worker.zone || "-"}`
|
||||
}));
|
||||
|
||||
setWorkers(formattedWorkers);
|
||||
async function getAsset(assetId: string) {
|
||||
let thumbnail = await getAssetThumbnail(assetId)
|
||||
if (thumbnail.thumbnail) {
|
||||
let assetImage = thumbnail.thumbnail
|
||||
return assetImage;
|
||||
}
|
||||
}, [selectedProduct, getProductById]);
|
||||
|
||||
useEffect(() => {
|
||||
// console.log("Workers data updated:", workers);
|
||||
}, [workers]);
|
||||
|
||||
|
||||
|
||||
|
||||
// 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"
|
||||
// },
|
||||
// ]
|
||||
function handleRenameWorker(newName: string) {
|
||||
// console.log('newName: ', newName);
|
||||
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (allAssets.length > 0) {
|
||||
const fetchWorkers = async () => {
|
||||
const humans = allAssets.filter((worker: any) => worker.eventData.type === "Human");
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <NavigateCatagory
|
||||
const formattedWorkers = await Promise.all(
|
||||
humans.map(async (worker: any, index: number) => {
|
||||
const assetImage = await getAsset(worker.assetId);
|
||||
|
||||
return {
|
||||
employee: {
|
||||
image: assetImage,
|
||||
name: worker.modelName,
|
||||
modelId: worker.modelUuid,
|
||||
employee_id: `HR-${204 + index}`,
|
||||
status: "Active",
|
||||
},
|
||||
task: {
|
||||
status: "Ongoing",
|
||||
title: worker.taskTitle ?? "No Task Assigned",
|
||||
location: {
|
||||
floor: worker.floor ?? 0,
|
||||
zone: worker.zone ?? "N/A",
|
||||
},
|
||||
planned_time_hours: worker.plannedTime ?? 0,
|
||||
time_spent_hours: worker.timeSpent ?? 0,
|
||||
total_tasks: worker.totalTasks ?? 0,
|
||||
completed_tasks: worker.completedTasks ?? 0,
|
||||
},
|
||||
actions: ["Assign Task", "Reassign Task", "Pause", "Emergency Stop"],
|
||||
location: `Floor ${worker.floor || "-"} . Zone ${worker.zone || "-"}`,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
setWorkers(formattedWorkers);
|
||||
};
|
||||
|
||||
fetchWorkers();
|
||||
}
|
||||
}, [allAssets]);
|
||||
|
||||
|
||||
|
||||
// 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"
|
||||
// },
|
||||
// ]
|
||||
function handleRenameWorker(newName: string) {
|
||||
|
||||
|
||||
}
|
||||
function handleHumanClick(employee: any) {
|
||||
if (employee.modelId) {
|
||||
setResourceManagementId(employee.modelId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <NavigateCatagory
|
||||
category={["All People", "Technician", "Operator", "Supervisor", "Safety Officer"]}
|
||||
selectedCategory={selectedCategory}
|
||||
setSelectedCategory={setSelectedCategory}
|
||||
/> */}
|
||||
|
||||
<div className='hrm-container assetManagement-wrapper'>
|
||||
{workers.map((employee, index) => (
|
||||
<div
|
||||
className={`analysis-wrapper ${selectedCard === index ? "active" : ""}`}
|
||||
onClick={() => setSelectedCard(index)}
|
||||
key={index}
|
||||
>
|
||||
<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> */}
|
||||
<RenameInput value={employee.employee.name} onRename={handleRenameWorker} />
|
||||
<div className="employee-id">{employee.employee.employee_id}</div>
|
||||
</div>
|
||||
<div className='hrm-container assetManagement-wrapper'>
|
||||
{workers.map((employee, index) => (
|
||||
<div
|
||||
className={`analysis-wrapper ${selectedCard === index ? "active" : ""}`}
|
||||
onClick={() => setSelectedCard(index)}
|
||||
key={index}
|
||||
>
|
||||
<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> */}
|
||||
<RenameInput value={employee.employee.name} onRename={handleRenameWorker} />
|
||||
<div className="employee-id">{employee.employee.employee_id}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="see-more">View more</div>
|
||||
</header>
|
||||
<div className="see-more" onClick={() => { handleHumanClick(employee.employee) }}>View in Scene</div>
|
||||
</header>
|
||||
|
||||
<div className="content">
|
||||
{/* <div className="task-info">
|
||||
<div className="content">
|
||||
{/* <div className="task-info">
|
||||
<div className="task-wrapper">
|
||||
<div className="task-label">
|
||||
<span className='label-icon'><ListTaskIcon /></span>
|
||||
@@ -205,17 +215,17 @@ const Hrm = () => {
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className="task-stats">
|
||||
<div className="stat-item">
|
||||
<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 className="stat-wrapper">
|
||||
<span className="stat-icon"><ClockThreeIcon /></span>
|
||||
<span>Planned time:</span>
|
||||
</div>
|
||||
{/* <div className="stat-item">
|
||||
|
||||
<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>
|
||||
@@ -233,39 +243,39 @@ const Hrm = () => {
|
||||
|
||||
<span className='stat-value'>{employee.task.time_spent_hours} hr</span>
|
||||
</div> */}
|
||||
<div className="stat-item">
|
||||
<div className="stat-item">
|
||||
|
||||
<div className="stat-wrapper">
|
||||
<span className="stat-icon"><TargetIcon /></span>
|
||||
<span>Cost per hr:</span>
|
||||
</div>
|
||||
|
||||
<span className='stat-value'>{employee.task.completed_tasks}</span>
|
||||
<div className="stat-wrapper">
|
||||
<span className="stat-icon"><TargetIcon /></span>
|
||||
<span>Cost per hr:</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>
|
||||
<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
|
||||
|
||||
@@ -3,58 +3,65 @@ import { useEffect, useState } from 'react'
|
||||
import { EyeIcon, ForkLiftIcon, KebabIcon, LocationPinIcon, RightHalfFillCircleIcon } from '../../../../../icons/ExportCommonIcons';
|
||||
import assetImage from "../../../../../../assets/image/asset-image.png"
|
||||
import { useSceneContext } from '../../../../../../modules/scene/sceneContext';
|
||||
import { useProductContext } from '../../../../../../modules/simulation/products/productContext';
|
||||
import RenameInput from '../../../../../ui/inputs/RenameInput';
|
||||
import { useResourceManagementId } from '../../../../../../store/builder/store';
|
||||
import { getAssetThumbnail } from '../../../../../../services/factoryBuilder/asset/assets/getAssetThumbnail';
|
||||
|
||||
const AssetManagement = () => {
|
||||
// const [selectedCategory, setSelectedCategory] = useState("All Assets");
|
||||
const [expandedAssetId, setExpandedAssetId] = useState<string | null>(null);
|
||||
const [assets, setAssets] = useState<any[]>([]);
|
||||
const { setResourceManagementId } = useResourceManagementId();
|
||||
const { assetStore } = useSceneContext();
|
||||
const { assets: allAssets } = assetStore();
|
||||
|
||||
const { productStore } = useSceneContext();
|
||||
const { products, getProductById } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
|
||||
|
||||
async function getAsset(assetId: string) {
|
||||
let thumbnail = await getAssetThumbnail(assetId)
|
||||
if (thumbnail.thumbnail) {
|
||||
let assetImage = thumbnail.thumbnail
|
||||
return assetImage;
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProduct) {
|
||||
const productDetails = getProductById(selectedProduct.productUuid);
|
||||
const productAssets = productDetails?.eventDatas || [];
|
||||
const grouped: Record<string, any> = {};
|
||||
productAssets.forEach((asset: any) => {
|
||||
if (asset.type === "storageUnit" || asset.type === "human") return;
|
||||
if (!grouped[asset.modelName]) {
|
||||
grouped[asset.modelName] = {
|
||||
id: asset.assetId,
|
||||
name: asset.modelName,
|
||||
model: asset.modelCode || "N/A",
|
||||
status: asset.status || "Online",
|
||||
usageRate: asset.usageRate || 15,
|
||||
level: asset.level || "Level 1",
|
||||
image: assetImage,
|
||||
description: asset.description || "No description",
|
||||
cost: asset.cost || 0,
|
||||
count: 1,
|
||||
};
|
||||
} else {
|
||||
grouped[asset.modelName].count += 1;
|
||||
}
|
||||
});
|
||||
if (allAssets.length > 0) {
|
||||
const fetchAssets = async () => {
|
||||
const grouped: Record<string, any> = {};
|
||||
|
||||
setAssets(Object.values(grouped));
|
||||
// Use Promise.all to handle all async operations
|
||||
await Promise.all(allAssets.map(async (asset: any) => {
|
||||
|
||||
if (asset.eventData.type === "Storage" || asset.eventData.type === "Human") return;
|
||||
|
||||
const assetImage = await getAsset(asset.assetId);
|
||||
|
||||
if (!grouped[asset.assetId]) {
|
||||
grouped[asset.assetId] = {
|
||||
id: asset.modelUuid,
|
||||
assetId: asset.assetId,
|
||||
name: asset.modelName,
|
||||
model: asset.modelCode ?? "N/A",
|
||||
status: asset.status ?? "Online",
|
||||
usageRate: asset.usageRate ?? 15,
|
||||
level: asset.level ?? "Level 1",
|
||||
image: assetImage,
|
||||
description: asset.description ?? "No description",
|
||||
cost: asset.cost ?? 0,
|
||||
count: 1,
|
||||
};
|
||||
} else {
|
||||
grouped[asset.assetId].count += 1;
|
||||
}
|
||||
}));
|
||||
|
||||
setAssets(Object.values(grouped));
|
||||
}
|
||||
fetchAssets();
|
||||
}
|
||||
}, [selectedProduct]);
|
||||
}, [allAssets]);
|
||||
|
||||
function handleRenameAsset(newName: string) {
|
||||
// console.log('newName: ', newName);
|
||||
// if (expandedAssetId) {
|
||||
// setAssets(prevAssets =>
|
||||
// prevAssets.map(asset =>
|
||||
// asset.id === expandedAssetId ? { ...asset, name: newName } : asset
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -62,6 +69,10 @@ const AssetManagement = () => {
|
||||
|
||||
}, [assets]);
|
||||
|
||||
function handleAssetClick(id: string) {
|
||||
setResourceManagementId(id);
|
||||
}
|
||||
|
||||
// const dummyAssets = [
|
||||
// {
|
||||
// id: '1',
|
||||
@@ -105,10 +116,10 @@ const AssetManagement = () => {
|
||||
return (
|
||||
<>
|
||||
{/* <NavigateCatagory
|
||||
category={["All Assets", "Machines", "Workstation", "Vehicles"]}
|
||||
selectedCategory={selectedCategory}
|
||||
setSelectedCategory={setSelectedCategory}
|
||||
/> */}
|
||||
category={["All Assets", "Machines", "Workstation", "Vehicles"]}
|
||||
selectedCategory={selectedCategory}
|
||||
setSelectedCategory={setSelectedCategory}
|
||||
/> */}
|
||||
|
||||
<div className='assetManagement-container assetManagement-wrapper'>
|
||||
{assets.map((asset, index) => (
|
||||
@@ -125,15 +136,13 @@ const AssetManagement = () => {
|
||||
<div className="icon"><ForkLiftIcon /></div>
|
||||
}
|
||||
<div className="asset-details-container">
|
||||
<div className="asset-details">
|
||||
<div className="asset-details" >
|
||||
{/* <div className="asset-name">{asset.name}</div> */}
|
||||
<RenameInput value={asset.name} onRename={handleRenameAsset} />
|
||||
{asset.count !== 1 && <div>
|
||||
<span className="asset-id-label">x</span>
|
||||
<span className="asset-id">{asset.count}</span>
|
||||
</div>}
|
||||
|
||||
|
||||
<div className="asset-model">{asset.model}</div>
|
||||
</div>
|
||||
<div className="asset-status-wrapper">
|
||||
@@ -164,7 +173,7 @@ const AssetManagement = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="asset-estimate__view-button">
|
||||
<div className="asset-estimate__view-button" onClick={() => handleAssetClick(asset.id)}>
|
||||
<EyeIcon isClosed={false} />
|
||||
<div className="asset-estimate__view-text">View in Scene</div>
|
||||
</div>
|
||||
|
||||
@@ -57,6 +57,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||
const { zoneStore } = useSceneContext();
|
||||
const { zones, setZoneName } = zoneStore();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
useSelectedZoneStore.getState().setSelectedZone({
|
||||
zoneName: "",
|
||||
@@ -121,6 +122,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||
}
|
||||
|
||||
function handleAssetClick(asset: Asset) {
|
||||
|
||||
setZoneAssetId(asset);
|
||||
}
|
||||
|
||||
@@ -157,8 +159,9 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||
modelUuid: zoneAssetId.id,
|
||||
modelName: newName,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || ''
|
||||
|
||||
});
|
||||
// console.log("response: ", response);
|
||||
|
||||
setName(zoneAssetId.id, response.modelName);
|
||||
}
|
||||
@@ -255,9 +258,8 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`list-item ${
|
||||
selectedZone.zoneUuid === item.id ? "active" : ""
|
||||
}`}
|
||||
className={`list-item ${selectedZone.zoneUuid === item.id ? "active" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="zone-header">
|
||||
<button className="value" id="zone-name">
|
||||
@@ -301,9 +303,8 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||
{item.assets.map((asset) => (
|
||||
<li
|
||||
key={`asset-${asset.id}`}
|
||||
className={`list-container asset-item ${
|
||||
zoneAssetId?.id === asset.id ? "active" : ""
|
||||
}`}
|
||||
className={`list-container asset-item ${zoneAssetId?.id === asset.id ? "active" : ""
|
||||
}`}
|
||||
onClick={() => handleAssetClick(asset)}
|
||||
>
|
||||
<div className="list-item">
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CameraControls } from '@react-three/drei';
|
||||
import { ThreeEvent, useThree } from '@react-three/fiber';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import { useActiveTool, useDeletableFloorItem, useSelectedFloorItem, useToggleView, useZoneAssetId } from '../../../../../../store/builder/store';
|
||||
import { useActiveTool, useDeletableFloorItem, useResourceManagementId, useSelectedFloorItem, useToggleView, useZoneAssetId } from '../../../../../../store/builder/store';
|
||||
import useModuleStore, { useSubModuleStore } from '../../../../../../store/useModuleStore';
|
||||
import { useSocketStore } from '../../../../../../store/builder/store';
|
||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
||||
@@ -36,6 +36,7 @@ export function useModelEventHandlers({
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { getAssetById, removeAsset } = assetStore();
|
||||
const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
|
||||
const { resourceManagementId, setResourceManagementId } = useResourceManagementId();
|
||||
const { removeEvent, getEventByModelUuid } = eventStore();
|
||||
const { getIsEventInProduct, addPoint, deleteEvent } = productStore();
|
||||
const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
|
||||
@@ -74,7 +75,17 @@ export function useModelEventHandlers({
|
||||
if (zoneAssetId.id === asset.modelUuid) {
|
||||
handleDblClick(asset);
|
||||
}
|
||||
|
||||
}, [zoneAssetId])
|
||||
useEffect(() => {
|
||||
if (!resourceManagementId) return
|
||||
if (resourceManagementId === asset.modelUuid) {
|
||||
|
||||
|
||||
handleDblClick(asset);
|
||||
}
|
||||
|
||||
}, [resourceManagementId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedFloorItem) {
|
||||
@@ -83,10 +94,8 @@ export function useModelEventHandlers({
|
||||
}, [selectedFloorItem])
|
||||
|
||||
const handleDblClick = (asset: Asset) => {
|
||||
if (asset && activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') {
|
||||
|
||||
if (asset && activeTool === "cursor" && boundingBox && groupRef.current && (activeModule === 'builder' || (activeModule === 'simulation' && resourceManagementId))) {
|
||||
const frontView = false;
|
||||
|
||||
if (frontView) {
|
||||
const size = boundingBox.getSize(new THREE.Vector3());
|
||||
const center = boundingBox.getCenter(new THREE.Vector3());
|
||||
@@ -107,6 +116,7 @@ export function useModelEventHandlers({
|
||||
paddingBottom: 5,
|
||||
paddingRight: 5,
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
const collisionPos = new THREE.Vector3();
|
||||
@@ -127,6 +137,7 @@ export function useModelEventHandlers({
|
||||
}
|
||||
|
||||
setSelectedFloorItem(groupRef.current);
|
||||
setResourceManagementId("");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,222 +1,243 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { CameraControls, Html, ScreenSpace } from '@react-three/drei';
|
||||
import { useContextActionStore, useRenameModeStore, useSelectedAssets } from '../../../../store/builder/store';
|
||||
import ContextMenu from '../../../../components/ui/menu/contextMenu';
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { CameraControls, Html, ScreenSpace } from "@react-three/drei";
|
||||
import {
|
||||
useContextActionStore,
|
||||
useRenameModeStore,
|
||||
useSelectedAssets,
|
||||
} from "../../../../store/builder/store";
|
||||
import ContextMenu from "../../../../components/ui/menu/contextMenu";
|
||||
|
||||
function ContextControls() {
|
||||
const { gl, controls } = useThree();
|
||||
const [canRender, setCanRender] = useState(false);
|
||||
const [visibility, setVisibility] = useState({ rename: true, focus: true, flipX: true, flipZ: true, move: true, rotate: true, duplicate: true, copy: true, paste: true, modifier: false, group: false, array: false, delete: true, });
|
||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { setContextAction } = useContextActionStore();
|
||||
const { setIsRenameMode } = useRenameModeStore();
|
||||
const rightDrag = useRef(false);
|
||||
const isRightMouseDown = useRef(false);
|
||||
const { gl, controls } = useThree();
|
||||
const [canRender, setCanRender] = useState(false);
|
||||
const [visibility, setVisibility] = useState({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { setContextAction } = useContextActionStore();
|
||||
const { setIsRenameMode } = useRenameModeStore();
|
||||
const rightDrag = useRef(false);
|
||||
const isRightMouseDown = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 1) {
|
||||
setVisibility({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else if (selectedAssets.length > 1) {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: true,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: false,
|
||||
flipX: false,
|
||||
flipZ: false,
|
||||
move: false,
|
||||
rotate: false,
|
||||
duplicate: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: false,
|
||||
});
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 1) {
|
||||
setVisibility({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else if (selectedAssets.length > 1) {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: true,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: false,
|
||||
flipX: false,
|
||||
flipZ: false,
|
||||
move: false,
|
||||
rotate: false,
|
||||
duplicate: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: false,
|
||||
});
|
||||
}
|
||||
}, [selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onPointerDown = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = true;
|
||||
rightDrag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerMove = () => {
|
||||
if (isRightMouseDown.current) {
|
||||
rightDrag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerUp = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleContextClick = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if (rightDrag.current) return;
|
||||
if (selectedAssets.length > 0) {
|
||||
setMenuPosition({
|
||||
x: event.clientX - gl.domElement.width / 2,
|
||||
y: event.clientY - gl.domElement.height / 2,
|
||||
});
|
||||
setCanRender(true);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = false;
|
||||
}
|
||||
}, [selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onPointerDown = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = true;
|
||||
rightDrag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerMove = () => {
|
||||
if (isRightMouseDown.current) {
|
||||
rightDrag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerUp = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleContextClick = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if (rightDrag.current) return;
|
||||
if (selectedAssets.length > 0) {
|
||||
setMenuPosition({ x: event.clientX - gl.domElement.width / 2, y: event.clientY - gl.domElement.height / 2 });
|
||||
setCanRender(true);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = false;
|
||||
}
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (selectedAssets.length > 0) {
|
||||
canvasElement.addEventListener('pointerdown', onPointerDown);
|
||||
canvasElement.addEventListener('pointermove', onPointerMove);
|
||||
canvasElement.addEventListener('pointerup', onPointerUp);
|
||||
canvasElement.addEventListener('contextmenu', handleContextClick)
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setMenuPosition({ x: 0, y: 0 });
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener('pointerdown', onPointerDown);
|
||||
canvasElement.removeEventListener('pointermove', onPointerMove);
|
||||
canvasElement.removeEventListener('pointerup', onPointerUp);
|
||||
canvasElement.removeEventListener('contextmenu', handleContextClick);
|
||||
};
|
||||
}, [gl, selectedAssets]);
|
||||
|
||||
const handleAssetRename = () => {
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("renameAsset");
|
||||
setIsRenameMode(true);
|
||||
}
|
||||
const handleAssetFocus = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("focusAsset");
|
||||
}
|
||||
const handleAssetMove = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("moveAsset")
|
||||
}
|
||||
const handleAssetRotate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("rotateAsset")
|
||||
}
|
||||
const handleAssetCopy = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("copyAsset")
|
||||
}
|
||||
const handleAssetPaste = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("pasteAsset")
|
||||
}
|
||||
const handleAssetDelete = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("deleteAsset")
|
||||
}
|
||||
const handleAssetDuplicate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("duplicateAsset")
|
||||
}
|
||||
};
|
||||
|
||||
if (selectedAssets.length > 0) {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
canvasElement.addEventListener("contextmenu", handleContextClick);
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setMenuPosition({ x: 0, y: 0 });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{canRender && (
|
||||
<ScreenSpace depth={1} >
|
||||
<Html
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: menuPosition.y,
|
||||
left: menuPosition.x,
|
||||
zIndex: 1000
|
||||
}}
|
||||
>
|
||||
<ContextMenu
|
||||
visibility={visibility}
|
||||
onRename={() => handleAssetRename()}
|
||||
onFocus={() => handleAssetFocus()}
|
||||
onFlipX={() => console.log("Flip to X")}
|
||||
onFlipZ={() => console.log("Flip to Z")}
|
||||
onMove={() => handleAssetMove()}
|
||||
onRotate={() => handleAssetRotate()}
|
||||
onDuplicate={() => handleAssetDuplicate()}
|
||||
onCopy={() => handleAssetCopy()}
|
||||
onPaste={() => handleAssetPaste()}
|
||||
onGroup={() => console.log("Group")}
|
||||
onArray={() => console.log("Array")}
|
||||
onDelete={() => handleAssetDelete()}
|
||||
/>
|
||||
</Html>
|
||||
</ScreenSpace>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return () => {
|
||||
canvasElement.removeEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.removeEventListener("pointermove", onPointerMove);
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("contextmenu", handleContextClick);
|
||||
};
|
||||
}, [controls, gl, selectedAssets]);
|
||||
|
||||
const handleAssetRename = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("renameAsset");
|
||||
setIsRenameMode(true);
|
||||
};
|
||||
const handleAssetFocus = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("focusAsset");
|
||||
};
|
||||
const handleAssetMove = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("moveAsset");
|
||||
};
|
||||
const handleAssetRotate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("rotateAsset");
|
||||
};
|
||||
const handleAssetCopy = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("copyAsset");
|
||||
};
|
||||
const handleAssetPaste = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("pasteAsset");
|
||||
};
|
||||
const handleAssetDelete = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("deleteAsset");
|
||||
};
|
||||
const handleAssetDuplicate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("duplicateAsset");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{canRender && (
|
||||
<ScreenSpace depth={1}>
|
||||
<Html
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: menuPosition.y,
|
||||
left: menuPosition.x,
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<ContextMenu
|
||||
visibility={visibility}
|
||||
onRename={() => handleAssetRename()}
|
||||
onFocus={() => handleAssetFocus()}
|
||||
onFlipX={() => console.log("Flip to X")}
|
||||
onFlipZ={() => console.log("Flip to Z")}
|
||||
onMove={() => handleAssetMove()}
|
||||
onRotate={() => handleAssetRotate()}
|
||||
onDuplicate={() => handleAssetDuplicate()}
|
||||
onCopy={() => handleAssetCopy()}
|
||||
onPaste={() => handleAssetPaste()}
|
||||
onGroup={() => console.log("Group")}
|
||||
onArray={() => console.log("Array")}
|
||||
onDelete={() => handleAssetDelete()}
|
||||
/>
|
||||
</Html>
|
||||
</ScreenSpace>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ContextControls;
|
||||
|
||||
@@ -64,6 +64,7 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
||||
setContextAction(null);
|
||||
moveAssets()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [contextAction])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -176,6 +177,7 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]);
|
||||
|
||||
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
|
||||
@@ -223,6 +225,7 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
||||
setDragOffset(newOffset);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [axisConstraint, camera, movedObjects])
|
||||
|
||||
useFrame(() => {
|
||||
|
||||
@@ -67,6 +67,7 @@ const SelectionControls3D: React.FC = () => {
|
||||
setContextAction(null);
|
||||
deleteSelection()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [contextAction])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -222,12 +223,14 @@ const SelectionControls3D: React.FC = () => {
|
||||
helper.enabled = false;
|
||||
helper.dispose();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
||||
clearSelection();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [activeModule, toolMode, toggleView]);
|
||||
|
||||
const selectAssets = useCallback(() => {
|
||||
@@ -362,7 +365,7 @@ const SelectionControls3D: React.FC = () => {
|
||||
removeAsset(uuid);
|
||||
});
|
||||
|
||||
echo.success("Selected models removed!");
|
||||
echo.warn("Selected models removed!");
|
||||
clearSelection();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from "three";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useThree, useFrame } from "@react-three/fiber";
|
||||
import { useToolMode } from "../../../store/builder/store";
|
||||
import { Html, Line } from "@react-three/drei";
|
||||
@@ -10,7 +10,81 @@ const MeasurementTool = () => {
|
||||
|
||||
const [points, setPoints] = useState<THREE.Vector3[]>([]);
|
||||
const [linePoints, setLinePoints] = useState<THREE.Vector3[] | null>(null);
|
||||
const [axisLock, setAxisLock] = useState<"x" | "y" | "z" | null>(null);
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const keysPressed = useRef<Set<string>>(new Set());
|
||||
|
||||
// Axis lock key handling
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.altKey) {
|
||||
if (e.key.toLowerCase() === "x") keysPressed.current.add("x");
|
||||
else if (e.key.toLowerCase() === "y") keysPressed.current.add("y");
|
||||
else if (e.key.toLowerCase() === "z") keysPressed.current.add("z");
|
||||
|
||||
if (keysPressed.current.has("x")) setAxisLock("x");
|
||||
else if (keysPressed.current.has("y")) setAxisLock("y");
|
||||
else if (keysPressed.current.has("z")) setAxisLock("z");
|
||||
} else if (e.key === "Escape") {
|
||||
setPoints([]);
|
||||
setLinePoints(null);
|
||||
setAxisLock(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyUp = (e: KeyboardEvent) => {
|
||||
keysPressed.current.delete(e.key.toLowerCase());
|
||||
if (keysPressed.current.has("x")) setAxisLock("x");
|
||||
else if (keysPressed.current.has("y")) setAxisLock("y");
|
||||
else if (keysPressed.current.has("z")) setAxisLock("z");
|
||||
else setAxisLock(null);
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
window.addEventListener("keyup", handleKeyUp);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
window.removeEventListener("keyup", handleKeyUp);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const getLineColor = useCallback(() => {
|
||||
switch (axisLock) {
|
||||
case "x":
|
||||
return "#d94522"; // Red for X axis
|
||||
case "y":
|
||||
return "#22ab2e"; // Green for Y axis
|
||||
case "z":
|
||||
return "#227bd9"; // Blue for Z axis
|
||||
default:
|
||||
return "#b18ef1"; // Default purple
|
||||
}
|
||||
}, [axisLock]);
|
||||
|
||||
// Apply axis lock to a point
|
||||
const applyAxisLock = useCallback(
|
||||
(point: THREE.Vector3, referencePoint: THREE.Vector3) => {
|
||||
const lockedPoint = point.clone();
|
||||
|
||||
switch (axisLock) {
|
||||
case "x":
|
||||
lockedPoint.y = referencePoint.y;
|
||||
lockedPoint.z = referencePoint.z;
|
||||
break;
|
||||
case "y":
|
||||
lockedPoint.x = referencePoint.x;
|
||||
lockedPoint.z = referencePoint.z;
|
||||
break;
|
||||
case "z":
|
||||
lockedPoint.x = referencePoint.x;
|
||||
lockedPoint.y = referencePoint.y;
|
||||
break;
|
||||
}
|
||||
|
||||
return lockedPoint;
|
||||
},
|
||||
[axisLock]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
@@ -45,7 +119,13 @@ const MeasurementTool = () => {
|
||||
);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const intersectionPoint = intersects[0].point.clone();
|
||||
let intersectionPoint = intersects[0].point.clone();
|
||||
if (axisLock && points.length > 0) {
|
||||
intersectionPoint = applyAxisLock(
|
||||
intersectionPoint,
|
||||
points[points.length - 1]
|
||||
);
|
||||
}
|
||||
if (points.length < 2) {
|
||||
setPoints([...points, intersectionPoint]);
|
||||
} else {
|
||||
@@ -84,7 +164,7 @@ const MeasurementTool = () => {
|
||||
canvasElement.removeEventListener("contextmenu", onContextMenu);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [toolMode, camera, raycaster, pointer, scene, points]);
|
||||
}, [toolMode, camera, raycaster, pointer, scene, points, axisLock]);
|
||||
|
||||
useFrame(() => {
|
||||
if (points.length === 1) {
|
||||
@@ -107,7 +187,10 @@ const MeasurementTool = () => {
|
||||
);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const tempEnd = intersects[0].point.clone();
|
||||
let tempEnd = intersects[0].point.clone();
|
||||
if (axisLock) {
|
||||
tempEnd = applyAxisLock(tempEnd, points[0]);
|
||||
}
|
||||
updateMeasurement(points[0], tempEnd);
|
||||
}
|
||||
} else if (points.length === 2) {
|
||||
@@ -139,7 +222,7 @@ const MeasurementTool = () => {
|
||||
{/* Main line */}
|
||||
<Line
|
||||
points={linePoints}
|
||||
color="#b18ef1"
|
||||
color={getLineColor()}
|
||||
lineWidth={2} // actual line width
|
||||
depthTest={false}
|
||||
depthWrite={false}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
|
||||
export const getAssetThumbnail = async (assetId: String) => {
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/v2/getAssetThumbnail/${assetId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch assets");
|
||||
}
|
||||
|
||||
//
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
echo.error("Failed to get asset image");
|
||||
|
||||
}
|
||||
};
|
||||
@@ -436,6 +436,16 @@ export const useZoneAssetId = create<ZoneAssetState>((set) => ({
|
||||
setZoneAssetId: (asset) => set({ zoneAssetId: asset }),
|
||||
}));
|
||||
|
||||
interface ResourceManagementState {
|
||||
resourceManagementId: string;
|
||||
setResourceManagementId: (id: string) => void;
|
||||
}
|
||||
|
||||
export const useResourceManagementId = create<ResourceManagementState>((set) => ({
|
||||
resourceManagementId: "", // default value
|
||||
setResourceManagementId: (id: string) => set({ resourceManagementId: id }),
|
||||
}));
|
||||
|
||||
// version visible hidden
|
||||
interface VersionHistoryState {
|
||||
viewVersionHistory: boolean;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
border-radius: #{$border-radius-large};
|
||||
outline: 1px solid var(--border-color);
|
||||
z-index: 100;
|
||||
backdrop-filter: blur(4px);
|
||||
.header {
|
||||
@include flex-center;
|
||||
gap: 8px;
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
header {
|
||||
position: relative;
|
||||
@include flex-space-between;
|
||||
padding: 3px 0;
|
||||
padding-bottom: 6px;
|
||||
|
||||
.user-details {
|
||||
display: flex;
|
||||
@@ -117,6 +117,12 @@
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.user-image{
|
||||
height: 300%;
|
||||
width: 300%;
|
||||
transform: translate(-26px, -12px);
|
||||
}
|
||||
|
||||
.status {
|
||||
border-radius: 50%;
|
||||
@@ -136,6 +142,9 @@
|
||||
|
||||
.details {
|
||||
max-width: 144px;
|
||||
.input-value{
|
||||
max-width: 120px;
|
||||
}
|
||||
.employee-id {
|
||||
color: #b7b7c6;
|
||||
font-size: $tiny;
|
||||
|
||||
@@ -1606,7 +1606,10 @@
|
||||
.animations-lists {
|
||||
max-height: 210px;
|
||||
overflow: auto;
|
||||
|
||||
.no-animation {
|
||||
padding: 6px 8px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.animations-list-wrapper {
|
||||
padding: 0 4px;
|
||||
|
||||
@@ -2061,9 +2064,11 @@
|
||||
&:nth-child(2) {
|
||||
&::after {
|
||||
// @include gradient-by-child(4); // Second child uses the second color
|
||||
background: linear-gradient(144.19deg,
|
||||
rgba(197, 137, 26, 0.5) 16.62%,
|
||||
rgba(69, 48, 10, 0.5) 85.81%);
|
||||
background: linear-gradient(
|
||||
144.19deg,
|
||||
rgba(197, 137, 26, 0.5) 16.62%,
|
||||
rgba(69, 48, 10, 0.5) 85.81%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2200,9 +2205,11 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: var(--font-size-regular);
|
||||
background: linear-gradient(0deg,
|
||||
rgba(37, 24, 51, 0) 0%,
|
||||
rgba(52, 41, 61, 0.5) 100%);
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(37, 24, 51, 0) 0%,
|
||||
rgba(52, 41, 61, 0.5) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
backdrop-filter: blur(8px);
|
||||
opacity: 0;
|
||||
@@ -2458,4 +2465,4 @@
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user