Merge remote-tracking branch 'origin/main-demo' into ui
This commit is contained in:
@@ -14,7 +14,6 @@ import lightThemeImage from "../../assets/image/lightThemeProject.png";
|
||||
import { SettingsIcon, TrashIcon } from "../icons/ExportCommonIcons";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import { useLoadingProgress, useSocketStore } from "../../store/builder/store";
|
||||
import { createProject } from "../../services/dashboard/createProject";
|
||||
|
||||
interface SidePannelProps {
|
||||
setActiveTab: React.Dispatch<React.SetStateAction<string>>;
|
||||
@@ -38,11 +37,13 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
|
||||
const handleCreateNewProject = async () => {
|
||||
const token = localStorage.getItem("token");
|
||||
const refreshToken = localStorage.getItem("refreshToken")
|
||||
console.log('refreshToken: ', refreshToken);
|
||||
const refreshToken = localStorage.getItem("refreshToken");
|
||||
console.log("refreshToken: ", refreshToken);
|
||||
try {
|
||||
const projectId = generateProjectId();
|
||||
useSocketStore.getState().initializeSocket(email, organization, token, refreshToken);
|
||||
useSocketStore
|
||||
.getState()
|
||||
.initializeSocket(email, organization, token, refreshToken);
|
||||
|
||||
//API for creating new Project
|
||||
// const project = await createProject(
|
||||
@@ -59,12 +60,12 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
projectUuid: projectId,
|
||||
};
|
||||
|
||||
console.log('projectSocket: ', projectSocket);
|
||||
console.log("projectSocket: ", projectSocket);
|
||||
if (projectSocket) {
|
||||
const handleResponse = (data: any) => {
|
||||
if (data.message === "Project created successfully") {
|
||||
setLoadingProgress(1)
|
||||
navigate(`/${data.data.projectId}`);
|
||||
navigate(`/projects/${data.data.projectId}`);
|
||||
}
|
||||
projectSocket.off("v1-project:response:add", handleResponse); // Clean up
|
||||
};
|
||||
@@ -88,7 +89,8 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
</div>
|
||||
<div className="user-name">
|
||||
{userName
|
||||
? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase()
|
||||
? userName.charAt(0).toUpperCase() +
|
||||
userName.slice(1).toLowerCase()
|
||||
: "Anonymous"}
|
||||
</div>
|
||||
</div>
|
||||
@@ -162,10 +164,14 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
<SettingsIcon />
|
||||
Settings
|
||||
</div>
|
||||
<div className="option-list" style={{ cursor: "pointer" }} onClick={() => {
|
||||
localStorage.clear();
|
||||
navigate("/");
|
||||
}}>
|
||||
<div
|
||||
className="option-list"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
localStorage.clear();
|
||||
navigate("/");
|
||||
}}
|
||||
>
|
||||
<LogoutIcon />
|
||||
Log out
|
||||
</div>
|
||||
@@ -179,4 +185,4 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SidePannel;
|
||||
export default SidePannel;
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
interface Props {
|
||||
email: string;
|
||||
setEmail: (value: string) => void;
|
||||
onSubmit: () => void;
|
||||
email: string;
|
||||
setEmail: (value: string) => void;
|
||||
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
||||
}
|
||||
|
||||
const EmailInput: React.FC<Props> = ({ email, setEmail, onSubmit }) => {
|
||||
return (
|
||||
<div className='request-container'>
|
||||
<h1 className='header'>Forgot password</h1>
|
||||
<p className='sub-header'>
|
||||
Enter your email for the verification process, we will send a 4-digit code to your email.
|
||||
</p>
|
||||
<form className='auth-form' onSubmit={(e) => { e.preventDefault(); onSubmit(); }}>
|
||||
<input
|
||||
type='email'
|
||||
placeholder='Email'
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button type='submit' className='continue-button'>Continue</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="request-container">
|
||||
<h1 className="header">Forgot password</h1>
|
||||
<p className="sub-header">
|
||||
Enter your email for the verification process, we will send a 4-digit
|
||||
code to your email.
|
||||
</p>
|
||||
<form
|
||||
className="auth-form"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
onSubmit(e);
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button type="submit" className="continue-button">
|
||||
Continue
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmailInput;
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
|
||||
const OTPInput: React.FC<{ length?: number; onComplete: (otp: string) => void }> = ({ length = 4, onComplete }) => {
|
||||
const [otpValues, setOtpValues] = useState<string[]>(Array(length).fill(''));
|
||||
const OTPInput: React.FC<{
|
||||
length?: number;
|
||||
onComplete: (otp: string) => void;
|
||||
code: string | number;
|
||||
}> = ({ length = 4, onComplete, code }) => {
|
||||
const [otpValues, setOtpValues] = useState<string[]>(Array(length).fill(""));
|
||||
const inputsRef = useRef<(HTMLInputElement | null)[]>([]);
|
||||
|
||||
// Auto focus first input on mount
|
||||
// ✅ Pre-fill inputs if code is passed
|
||||
useEffect(() => {
|
||||
if (code) {
|
||||
const codeString = String(code);
|
||||
const filled = codeString.split("").slice(0, length);
|
||||
const padded = filled.concat(Array(length - filled.length).fill(""));
|
||||
setOtpValues(padded);
|
||||
if (filled.length === length) {
|
||||
onComplete(filled.join(""));
|
||||
}
|
||||
}
|
||||
}, [code, length, onComplete]);
|
||||
|
||||
// ✅ Focus first input on mount
|
||||
useEffect(() => {
|
||||
inputsRef.current[0]?.focus();
|
||||
}, []);
|
||||
@@ -19,14 +36,18 @@ const OTPInput: React.FC<{ length?: number; onComplete: (otp: string) => void }>
|
||||
inputsRef.current[index + 1]?.focus();
|
||||
}
|
||||
|
||||
if (newOtp.every((digit) => digit !== '')) {
|
||||
onComplete(newOtp.join(''));
|
||||
// ✅ Only trigger onComplete when all digits are filled
|
||||
if (newOtp.every((digit) => digit !== "")) {
|
||||
onComplete(newOtp.join(""));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
|
||||
if (e.key === 'Backspace' && !otpValues[index] && index > 0) {
|
||||
const handleKeyDown = (
|
||||
e: React.KeyboardEvent<HTMLInputElement>,
|
||||
index: number
|
||||
) => {
|
||||
if (e.key === "Backspace" && !otpValues[index] && index > 0) {
|
||||
inputsRef.current[index - 1]?.focus();
|
||||
}
|
||||
};
|
||||
@@ -39,7 +60,7 @@ const OTPInput: React.FC<{ length?: number; onComplete: (otp: string) => void }>
|
||||
type="text"
|
||||
className="otp-input"
|
||||
maxLength={1}
|
||||
value={value}
|
||||
value={value ?? ""}
|
||||
onChange={(e) => handleChange(e.target.value, index)}
|
||||
onKeyDown={(e) => handleKeyDown(e, index)}
|
||||
ref={(el) => (inputsRef.current[index] = el)}
|
||||
|
||||
@@ -1,57 +1,77 @@
|
||||
import React, { useState } from 'react';
|
||||
import OTPInput from './OTPInput';
|
||||
import React, { FormEvent, useState } from "react";
|
||||
import OTPInput from "./OTPInput";
|
||||
|
||||
interface Props {
|
||||
email: string;
|
||||
timer: number;
|
||||
setCode: (value: string) => void;
|
||||
onSubmit: () => void;
|
||||
resendCode: () => void;
|
||||
email: string;
|
||||
code: string;
|
||||
timer: number;
|
||||
setCode: (value: string) => void;
|
||||
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
||||
resendCode: () => void;
|
||||
}
|
||||
|
||||
const OTPVerification: React.FC<Props> = ({ email, timer, setCode, onSubmit, resendCode }) => {
|
||||
const [otp, setOtp] = useState('');
|
||||
const OTPVerification: React.FC<Props> = ({
|
||||
email,
|
||||
timer,
|
||||
setCode,
|
||||
onSubmit,
|
||||
resendCode,
|
||||
code,
|
||||
}) => {
|
||||
const [otp, setOtp] = useState("");
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
console.log('otp.length: ', otp.length);
|
||||
if (otp.length === 4) {
|
||||
onSubmit();
|
||||
} else {
|
||||
alert('Please enter the 4-digit code');
|
||||
}
|
||||
};
|
||||
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
return (
|
||||
<div className='request-container'>
|
||||
<h1 className='header'>Verification</h1>
|
||||
<p className='sub-header'>
|
||||
Enter the 4-digit code sent to <strong>{email}</strong>.
|
||||
</p>
|
||||
<form className='auth-form' onSubmit={handleSubmit}>
|
||||
<OTPInput length={4} onComplete={(code) => { setOtp(code); setCode(code); }} />
|
||||
<div className="timing">
|
||||
{timer > 0
|
||||
? `${String(Math.floor(timer / 60)).padStart(2, '0')}:${String(timer % 60).padStart(2, '0')}`
|
||||
: ''}
|
||||
</div>
|
||||
<button
|
||||
type='submit'
|
||||
className='continue-button'
|
||||
disabled={otp.length < 4} // prevent clicking if not complete
|
||||
>
|
||||
Verify
|
||||
</button>
|
||||
</form>
|
||||
<div
|
||||
className={`resend ${timer > 0 ? 'disabled' : ''}`}
|
||||
onClick={timer === 0 ? resendCode : undefined}
|
||||
style={{ cursor: timer === 0 ? 'pointer' : 'not-allowed', opacity: timer === 0 ? 1 : 0.5 }}
|
||||
>
|
||||
If you didn’t receive a code, <span>Resend</span>
|
||||
</div>
|
||||
if (otp.length === 4) {
|
||||
onSubmit(e);
|
||||
} else {
|
||||
alert("Please enter the 4-digit code");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="request-container">
|
||||
<h1 className="header">Verification</h1>
|
||||
<p className="sub-header">
|
||||
Enter the 4-digit code sent to <strong>{email}</strong>.
|
||||
</p>
|
||||
<form className="auth-form" onSubmit={handleSubmit}>
|
||||
<OTPInput
|
||||
length={4}
|
||||
onComplete={(codes) => {
|
||||
setOtp(code);
|
||||
setCode(codes);
|
||||
}}
|
||||
code={code}
|
||||
/>
|
||||
<div className="timing">
|
||||
{timer > 0
|
||||
? `${String(Math.floor(timer / 60)).padStart(2, "0")}:${String(
|
||||
timer % 60
|
||||
).padStart(2, "0")}`
|
||||
: ""}
|
||||
</div>
|
||||
);
|
||||
<button
|
||||
type="submit"
|
||||
className="continue-button"
|
||||
// disabled={otp.length < 4} // prevent clicking if not complete
|
||||
>
|
||||
Verify
|
||||
</button>
|
||||
</form>
|
||||
<div
|
||||
className={`resend ${timer > 0 ? "disabled" : ""}`}
|
||||
onClick={timer === 0 ? resendCode : undefined}
|
||||
style={{
|
||||
cursor: timer === 0 ? "pointer" : "not-allowed",
|
||||
opacity: timer === 0 ? 1 : 0.5,
|
||||
}}
|
||||
>
|
||||
If you didn’t receive a code, <span>Resend</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OTPVerification;
|
||||
|
||||
@@ -1,77 +1,82 @@
|
||||
import React, { useState } from 'react';
|
||||
import { EyeIcon } from '../icons/ExportCommonIcons';
|
||||
import React, { useState } from "react";
|
||||
import { EyeIcon } from "../icons/ExportCommonIcons";
|
||||
|
||||
interface Props {
|
||||
newPassword: string;
|
||||
confirmPassword: string;
|
||||
setNewPassword: (value: string) => void;
|
||||
setConfirmPassword: (value: string) => void;
|
||||
onSubmit: () => void;
|
||||
newPassword: string;
|
||||
confirmPassword: string;
|
||||
setNewPassword: (value: string) => void;
|
||||
setConfirmPassword: (value: string) => void;
|
||||
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
||||
}
|
||||
|
||||
const PasswordSetup: React.FC<Props> = ({
|
||||
newPassword,
|
||||
confirmPassword,
|
||||
setNewPassword,
|
||||
setConfirmPassword,
|
||||
onSubmit
|
||||
newPassword,
|
||||
confirmPassword,
|
||||
setNewPassword,
|
||||
setConfirmPassword,
|
||||
onSubmit,
|
||||
}) => {
|
||||
const [showNewPassword, setShowNewPassword] = useState(false);
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
const [showNewPassword, setShowNewPassword] = useState(false);
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
|
||||
return (
|
||||
<div className='request-container'>
|
||||
<h1 className='header'>New Password</h1>
|
||||
<p className='sub-header'>Set the new password for your account so you can login and access all features.</p>
|
||||
<form
|
||||
className='auth-form'
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (newPassword !== confirmPassword) {
|
||||
alert('Passwords do not match');
|
||||
return;
|
||||
}
|
||||
onSubmit();
|
||||
}}
|
||||
>
|
||||
<div className="password-container">
|
||||
<input
|
||||
type={showNewPassword ? 'text' : 'password'}
|
||||
placeholder='Enter new password'
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="toggle-password"
|
||||
onClick={() => setShowNewPassword(prev => !prev)}
|
||||
>
|
||||
<EyeIcon isClosed={!showNewPassword} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="password-container">
|
||||
<input
|
||||
type={showConfirmPassword ? 'text' : 'password'}
|
||||
placeholder='Confirm password'
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="toggle-password"
|
||||
onClick={() => setShowConfirmPassword(prev => !prev)}
|
||||
>
|
||||
<EyeIcon isClosed={!showConfirmPassword} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button type='submit' className='continue-button'>Update password</button>
|
||||
</form>
|
||||
return (
|
||||
<div className="request-container">
|
||||
<h1 className="header">New Password</h1>
|
||||
<p className="sub-header">
|
||||
Set the new password for your account so you can login and access all
|
||||
features.
|
||||
</p>
|
||||
<form
|
||||
className="auth-form"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (newPassword !== confirmPassword) {
|
||||
alert("Passwords do not match");
|
||||
return;
|
||||
}
|
||||
onSubmit(e);
|
||||
}}
|
||||
>
|
||||
<div className="password-container">
|
||||
<input
|
||||
type={showNewPassword ? "text" : "password"}
|
||||
placeholder="Enter new password"
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="toggle-password"
|
||||
onClick={() => setShowNewPassword((prev) => !prev)}
|
||||
>
|
||||
<EyeIcon isClosed={!showNewPassword} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
<div className="password-container">
|
||||
<input
|
||||
type={showConfirmPassword ? "text" : "password"}
|
||||
placeholder="Confirm password"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="toggle-password"
|
||||
onClick={() => setShowConfirmPassword((prev) => !prev)}
|
||||
>
|
||||
<EyeIcon isClosed={!showConfirmPassword} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button type="submit" className="continue-button">
|
||||
Update password
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PasswordSetup;
|
||||
|
||||
@@ -1394,7 +1394,7 @@ export const AlertIcon = () => {
|
||||
<path
|
||||
d="M13.454 14.797H6.54597C6.1917 14.8122 5.83973 14.7329 5.5262 14.5673C5.21266 14.4017 4.94885 14.1556 4.76177 13.8544C4.5747 13.5531 4.47112 13.2075 4.46165 12.8531C4.45219 12.4986 4.53719 12.148 4.70792 11.8372L8.16189 5.83436C8.35337 5.51828 8.62315 5.25693 8.94513 5.07553C9.2671 4.89413 9.63038 4.79883 9.99993 4.79883C10.3695 4.79883 10.7328 4.89413 11.0548 5.07553C11.3768 5.25693 11.6466 5.51828 11.838 5.83436L15.292 11.8372C15.4627 12.148 15.5478 12.4986 15.5383 12.8531C15.5288 13.2075 15.4253 13.5531 15.2382 13.8544C15.0511 14.1556 14.7872 14.4017 14.4737 14.5673C14.1602 14.7329 13.8082 14.8122 13.454 14.797Z"
|
||||
stroke="var(--text-color)"
|
||||
stroke-width="0.832956"
|
||||
strokeWidth="0.832956"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
@@ -1402,7 +1402,7 @@ export const AlertIcon = () => {
|
||||
d="M10.0015 12.1777C10.0679 12.1791 10.1324 12.1997 10.187 12.2373C10.2417 12.275 10.2842 12.328 10.3091 12.3896C10.3339 12.4515 10.3402 12.5197 10.3267 12.585C10.3131 12.6501 10.2802 12.7099 10.2329 12.7568C10.1857 12.8035 10.1254 12.8357 10.0601 12.8486C9.99488 12.8615 9.92713 12.8544 9.86572 12.8291C9.83506 12.8165 9.80623 12.8 9.78076 12.7793L9.71436 12.7061L9.67139 12.6172C9.66175 12.5864 9.65628 12.5541 9.65576 12.5215C9.65579 12.4763 9.66481 12.4314 9.68213 12.3896C9.69961 12.3477 9.72603 12.3093 9.7583 12.2773C9.79045 12.2455 9.82857 12.2203 9.87061 12.2031C9.91219 12.1862 9.95664 12.1776 10.0015 12.1777ZM9.85596 10.998L9.77783 8.09961V8.08887L9.77686 8.07812V8.03125C9.77846 8.01596 9.78187 8.00098 9.78662 7.98633C9.7962 7.95676 9.81179 7.92933 9.83252 7.90625C9.84285 7.89476 9.85427 7.88407 9.8667 7.875L9.90674 7.85156C9.93523 7.83888 9.96642 7.83203 9.99756 7.83203C10.013 7.83203 10.0284 7.83375 10.0435 7.83691L10.0884 7.85156C10.1166 7.86421 10.1418 7.88315 10.1626 7.90625C10.1834 7.92943 10.199 7.95689 10.2085 7.98633C10.2181 8.01596 10.2215 8.04735 10.2183 8.07812L10.2173 8.08887V8.10059L10.145 10.999V11.0059C10.145 11.0441 10.1291 11.0804 10.1021 11.1074C10.0749 11.1345 10.0386 11.1504 10.0005 11.1504C9.96219 11.1504 9.92502 11.1345 9.89795 11.1074C9.87115 11.0804 9.85598 11.0439 9.85596 11.0059V10.998Z"
|
||||
fill="black"
|
||||
stroke="var(--text-color)"
|
||||
stroke-width="0.555304"
|
||||
strokeWidth="0.555304"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1419,17 +1419,17 @@ export const NavigationIcon = () => {
|
||||
<path
|
||||
d="M9.99988 4.58398C7.0132 4.58398 4.5835 7.01369 4.5835 10.0004C4.5835 12.9872 7.0132 15.4173 9.99988 15.4173C12.9867 15.4173 15.4168 12.9872 15.4168 10.0004C15.4168 7.01369 12.9867 4.58398 9.99988 4.58398ZM9.99988 14.7764C7.36664 14.7764 5.22402 12.634 5.22402 10.0004C5.22402 7.36713 7.3666 5.22451 9.99988 5.22451C12.6335 5.22451 14.7759 7.36709 14.7759 10.0004C14.7759 12.634 12.6335 14.7764 9.99988 14.7764Z"
|
||||
fill="var(--text-color)"
|
||||
fill-opacity="0.85"
|
||||
fillOpacity="0.85"
|
||||
/>
|
||||
<path
|
||||
d="M9.92024 6.74255L8.37659 12.8023C8.36662 12.8418 8.38635 12.8823 8.42392 12.8985C8.46109 12.9147 8.50461 12.9018 8.52685 12.8671L10.0191 10.5407L11.4739 12.8667C11.4894 12.8915 11.5166 12.9055 11.544 12.9055C11.5548 12.9055 11.5655 12.9033 11.5764 12.8991C11.6142 12.8829 11.6341 12.8419 11.6244 12.8023L10.0807 6.74255C10.0619 6.66915 9.93845 6.66915 9.92024 6.74255ZM10.0899 10.3422C10.0747 10.3184 10.0487 10.3039 10.0203 10.3039H10.0201C9.99177 10.3039 9.96531 10.3178 9.95019 10.3416L8.66357 12.3474L10.0001 7.09867L11.3321 12.3286L10.0899 10.3422Z"
|
||||
fill="var(--text-color)"
|
||||
fill-opacity="0.85"
|
||||
fillOpacity="0.85"
|
||||
/>
|
||||
<path
|
||||
d="M11.5438 12.9321C11.5066 12.9321 11.4708 12.9126 11.4514 12.8814L10.0187 10.59L8.54867 12.8817C8.52069 12.9257 8.46184 12.9438 8.41374 12.9235C8.3643 12.9019 8.33822 12.8484 8.35165 12.7966L9.89512 6.73667C9.90622 6.69241 9.94861 6.66211 10.0003 6.66211C10.0519 6.66211 10.0946 6.69199 10.1059 6.73667L11.649 12.7966C11.6624 12.8488 11.6362 12.9024 11.5865 12.9235C11.5723 12.9293 11.5584 12.9321 11.5438 12.9321ZM10.0189 10.4928L10.0411 10.5279L11.4957 12.8537C11.5097 12.8765 11.5396 12.8863 11.5664 12.8754C11.5917 12.8645 11.6057 12.8365 11.5988 12.8095L10.0553 6.7495C10.0486 6.72325 10.0216 6.71384 10.0002 6.71384C9.9787 6.71384 9.95185 6.72325 9.94515 6.7495L8.4019 12.8095C8.39461 12.8365 8.40842 12.8645 8.4339 12.8754C8.45842 12.8852 8.48985 12.8766 8.50444 12.8537L10.0189 10.4928ZM8.59871 12.4969L9.99995 6.99367L11.3991 12.4844L10.0678 10.3568C10.0465 10.3226 9.99244 10.3244 9.97215 10.3562L8.59871 12.4969Z"
|
||||
fill="var(--text-color)"
|
||||
fill-opacity="0.85"
|
||||
fillOpacity="0.85"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1447,8 +1447,8 @@ export const HangTagIcon = () => {
|
||||
<path
|
||||
d="M14.7002 5.2998V9.37598L9.5 14.5762L5.42383 10.5L10.624 5.2998H14.7002ZM12 7.2002C11.5581 7.2002 11.2002 7.55806 11.2002 8C11.2002 8.44194 11.5581 8.7998 12 8.7998C12.4419 8.7998 12.7998 8.44194 12.7998 8C12.7998 7.55806 12.4419 7.2002 12 7.2002Z"
|
||||
stroke="var(--text-color)"
|
||||
stroke-opacity="0.85"
|
||||
stroke-width="0.6"
|
||||
strokeOpacity="0.85"
|
||||
strokeWidth="0.6"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
@@ -1471,20 +1471,20 @@ export const DecalInfoIcon = () => {
|
||||
<path
|
||||
d="M10.0002 15.4173C12.9917 15.4173 15.4168 12.9922 15.4168 10.0007C15.4168 7.00911 12.9917 4.58398 10.0002 4.58398C7.00862 4.58398 4.5835 7.00911 4.5835 10.0007C4.5835 12.9922 7.00862 15.4173 10.0002 15.4173Z"
|
||||
stroke="var(--text-color)"
|
||||
stroke-opacity="0.85"
|
||||
stroke-width="0.8125"
|
||||
strokeOpacity="0.85"
|
||||
strokeWidth="0.8125"
|
||||
/>
|
||||
<path
|
||||
d="M10 12.709V9.45898"
|
||||
stroke="var(--text-color)"
|
||||
stroke-opacity="0.85"
|
||||
stroke-width="0.8125"
|
||||
strokeOpacity="0.85"
|
||||
strokeWidth="0.8125"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.0002 7.29167C10.2993 7.29167 10.5418 7.53418 10.5418 7.83333C10.5418 8.13249 10.2993 8.375 10.0002 8.375C9.70101 8.375 9.4585 8.13249 9.4585 7.83333C9.4585 7.53418 9.70101 7.29167 10.0002 7.29167Z"
|
||||
fill="var(--text-color)"
|
||||
fill-opacity="0.85"
|
||||
fillOpacity="0.85"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1505,13 +1505,13 @@ export const LayeringBottomIcon = () => {
|
||||
width="10.9233"
|
||||
height="9.75071"
|
||||
stroke="var(--text-color)"
|
||||
stroke-width="0.714277"
|
||||
strokeWidth="0.714277"
|
||||
/>
|
||||
<path
|
||||
d="M5.49121 0.599609H14.3867C14.9559 0.599609 15.3994 1.01267 15.3994 1.5V9.5C15.3993 9.98728 14.9559 10.3994 14.3867 10.3994H5.49121C4.92207 10.3994 4.47858 9.98728 4.47852 9.5V1.5C4.47852 1.01268 4.92203 0.599609 5.49121 0.599609Z"
|
||||
fill="#6F42C1"
|
||||
stroke="white"
|
||||
stroke-width="0.2"
|
||||
strokeWidth="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M7.87686 6.85212L9.54491 8.3521C9.5966 8.39897 9.65809 8.43616 9.72585 8.46155C9.7936 8.48693 9.86628 8.5 9.93968 8.5C10.0131 8.5 10.0858 8.48693 10.1535 8.46155C10.2213 8.43616 10.2828 8.39897 10.3345 8.3521L12.0025 6.85212C12.1072 6.75797 12.166 6.63027 12.166 6.49713C12.166 6.36398 12.1072 6.23628 12.0025 6.14213C11.8978 6.04798 11.7558 5.99509 11.6077 5.99509C11.4597 5.99509 11.3177 6.04798 11.213 6.14213L10.4957 6.79212V2.99717C10.4957 2.86456 10.4371 2.73739 10.3328 2.64362C10.2286 2.54985 10.0871 2.49718 9.93968 2.49718C9.79222 2.49718 9.65079 2.54985 9.54652 2.64362C9.44224 2.73739 9.38366 2.86456 9.38366 2.99717V6.79212L8.6664 6.14213C8.61472 6.09527 8.55322 6.05807 8.48546 6.03269C8.41771 6.0073 8.34503 5.99423 8.27163 5.99423C8.19823 5.99423 8.12556 6.0073 8.0578 6.03269C7.99005 6.05807 7.92855 6.09527 7.87686 6.14213C7.82475 6.18861 7.78338 6.24391 7.75516 6.30484C7.72693 6.36577 7.71239 6.43112 7.71239 6.49713C7.71239 6.56313 7.72693 6.62848 7.75516 6.68941C7.78338 6.75034 7.82475 6.80564 7.87686 6.85212Z"
|
||||
@@ -1536,13 +1536,13 @@ export const LayeringTopIcon = () => {
|
||||
width="10.9233"
|
||||
height="9.75071"
|
||||
stroke="var(--text-color)"
|
||||
stroke-width="0.714277"
|
||||
strokeWidth="0.714277"
|
||||
/>
|
||||
<path
|
||||
d="M5.49121 0.599609H14.3867C14.9559 0.599609 15.3994 1.01267 15.3994 1.5V9.5C15.3993 9.98728 14.9559 10.3994 14.3867 10.3994H5.49121C4.92207 10.3994 4.47858 9.98728 4.47852 9.5V1.5C4.47852 1.01268 4.92203 0.599609 5.49121 0.599609Z"
|
||||
fill="#6F42C1"
|
||||
stroke="white"
|
||||
stroke-width="0.2"
|
||||
strokeWidth="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M12.002 4.14397L10.334 2.64399C10.2823 2.59713 10.2208 2.55993 10.1531 2.53455C10.0853 2.50916 10.0126 2.49609 9.93923 2.49609C9.86583 2.49609 9.79315 2.50916 9.7254 2.53455C9.65764 2.55993 9.59614 2.59713 9.54446 2.64399L7.87641 4.14397C7.77171 4.23812 7.71289 4.36582 7.71289 4.49897C7.71289 4.63212 7.77171 4.75981 7.87641 4.85396C7.98111 4.94811 8.12311 5.00101 8.27118 5.00101C8.41925 5.00101 8.56125 4.94811 8.66595 4.85396L9.38321 4.20397V7.99892C9.38321 8.13153 9.44179 8.25871 9.54606 8.35247C9.65034 8.44624 9.79176 8.49892 9.93923 8.49892C10.0867 8.49892 10.2281 8.44624 10.3324 8.35247C10.4367 8.25871 10.4952 8.13153 10.4952 7.99892V4.20397L11.2125 4.85396C11.2642 4.90083 11.3257 4.93802 11.3934 4.96341C11.4612 4.98879 11.5339 5.00186 11.6073 5.00186C11.6807 5.00186 11.7533 4.98879 11.8211 4.96341C11.8889 4.93802 11.9504 4.90083 12.002 4.85396C12.0542 4.80748 12.0955 4.75218 12.1238 4.69125C12.152 4.63033 12.1665 4.56497 12.1665 4.49897C12.1665 4.43296 12.152 4.36761 12.1238 4.30668C12.0955 4.24575 12.0542 4.19045 12.002 4.14397Z"
|
||||
@@ -1629,7 +1629,7 @@ export const ClockThreeIcon = () => {
|
||||
<path
|
||||
d="M5.5 3.20833V5.5H6.875M9.625 5.5C9.625 7.77819 7.77819 9.625 5.5 9.625C3.22183 9.625 1.375 7.77819 1.375 5.5C1.375 3.22183 3.22183 1.375 5.5 1.375C7.77819 1.375 9.625 3.22183 9.625 5.5Z"
|
||||
stroke="#FCFDFD"
|
||||
stroke-width="0.5"
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
@@ -1689,7 +1689,7 @@ export const TargetIcon = () => {
|
||||
<path
|
||||
d="M9.625 5.5C9.625 7.77815 7.77815 9.625 5.5 9.625M9.625 5.5C9.625 3.22182 7.77815 1.375 5.5 1.375M9.625 5.5H7.975M5.5 9.625C3.22182 9.625 1.375 7.77815 1.375 5.5M5.5 9.625V7.975M5.5 1.375C3.22182 1.375 1.375 3.22182 1.375 5.5M5.5 1.375V3.025M1.375 5.5H3.025"
|
||||
stroke="#FCFDFD"
|
||||
stroke-width="0.5"
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
|
||||
@@ -15,270 +15,293 @@ 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";
|
||||
import {
|
||||
AlertIcon,
|
||||
DecalInfoIcon,
|
||||
HangTagIcon,
|
||||
NavigationIcon,
|
||||
} from "../../icons/ExportCommonIcons";
|
||||
// -------------------------------------
|
||||
|
||||
interface AssetProp {
|
||||
filename: string;
|
||||
thumbnail?: string;
|
||||
category: string;
|
||||
description?: string;
|
||||
tags: string;
|
||||
url?: string;
|
||||
uploadDate?: number;
|
||||
isArchieve?: boolean;
|
||||
animated?: boolean;
|
||||
price?: number;
|
||||
CreatedBy?: string;
|
||||
filename: string;
|
||||
thumbnail?: string;
|
||||
category: string;
|
||||
description?: string;
|
||||
tags: string;
|
||||
url?: string;
|
||||
uploadDate?: number;
|
||||
isArchieve?: boolean;
|
||||
animated?: boolean;
|
||||
price?: number;
|
||||
CreatedBy?: string;
|
||||
}
|
||||
interface CategoryListProp {
|
||||
assetImage?: string;
|
||||
assetName?: string;
|
||||
categoryImage: string;
|
||||
category: string;
|
||||
assetImage?: string;
|
||||
assetName?: string;
|
||||
categoryImage: string;
|
||||
category: string;
|
||||
}
|
||||
const Assets: React.FC = () => {
|
||||
const { setSelectedItem } = useSelectedItem();
|
||||
const [searchValue, setSearchValue] = useState<string>("");
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const [categoryAssets, setCategoryAssets] = useState<AssetProp[]>([]);
|
||||
const [filtereredAssets, setFiltereredAssets] = useState<AssetProp[]>([]);
|
||||
const [categoryList, setCategoryList] = useState<CategoryListProp[]>([]);
|
||||
const [isLoading, setisLoading] = useState<boolean>(false); // Loading state for assets
|
||||
const { setSelectedItem } = useSelectedItem();
|
||||
const [searchValue, setSearchValue] = useState<string>("");
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const [categoryAssets, setCategoryAssets] = useState<AssetProp[]>([]);
|
||||
const [filtereredAssets, setFiltereredAssets] = useState<AssetProp[]>([]);
|
||||
const [categoryList, setCategoryList] = useState<CategoryListProp[]>([]);
|
||||
const [isLoading, setisLoading] = useState<boolean>(false); // Loading state for assets
|
||||
|
||||
const handleSearchChange = (value: string) => {
|
||||
const searchTerm = value.toLowerCase();
|
||||
setSearchValue(value);
|
||||
if (searchTerm.trim() === "" && !selectedCategory) {
|
||||
setCategoryAssets([]);
|
||||
return;
|
||||
}
|
||||
const filteredModels = filtereredAssets?.filter((model) => {
|
||||
if (!model?.tags || !model?.filename || !model?.category) return false;
|
||||
if (searchTerm.startsWith(":") && searchTerm.length > 1) {
|
||||
const tagSearchTerm = searchTerm.slice(1);
|
||||
return model.tags.toLowerCase().includes(tagSearchTerm);
|
||||
} else if (selectedCategory) {
|
||||
return (
|
||||
model.category
|
||||
.toLowerCase()
|
||||
.includes(selectedCategory.toLowerCase()) &&
|
||||
model.filename.toLowerCase().includes(searchTerm)
|
||||
);
|
||||
} else {
|
||||
return model.filename.toLowerCase().includes(searchTerm);
|
||||
}
|
||||
});
|
||||
const handleSearchChange = (value: string) => {
|
||||
const searchTerm = value.toLowerCase();
|
||||
setSearchValue(value);
|
||||
if (searchTerm.trim() === "" && !selectedCategory) {
|
||||
setCategoryAssets([]);
|
||||
return;
|
||||
}
|
||||
const filteredModels = filtereredAssets?.filter((model) => {
|
||||
if (!model?.tags || !model?.filename || !model?.category) return false;
|
||||
if (searchTerm.startsWith(":") && searchTerm.length > 1) {
|
||||
const tagSearchTerm = searchTerm.slice(1);
|
||||
return model.tags.toLowerCase().includes(tagSearchTerm);
|
||||
} else if (selectedCategory) {
|
||||
return (
|
||||
model.category
|
||||
.toLowerCase()
|
||||
.includes(selectedCategory.toLowerCase()) &&
|
||||
model.filename.toLowerCase().includes(searchTerm)
|
||||
);
|
||||
} else {
|
||||
return model.filename.toLowerCase().includes(searchTerm);
|
||||
}
|
||||
});
|
||||
|
||||
setCategoryAssets(filteredModels);
|
||||
setCategoryAssets(filteredModels);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const filteredAssets = async () => {
|
||||
try {
|
||||
const filt = await fetchAssets();
|
||||
setFiltereredAssets(filt);
|
||||
} catch {
|
||||
echo.error("Filter asset not found");
|
||||
}
|
||||
};
|
||||
filteredAssets();
|
||||
}, [categoryAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const filteredAssets = async () => {
|
||||
try {
|
||||
const filt = await fetchAssets();
|
||||
setFiltereredAssets(filt);
|
||||
} catch {
|
||||
echo.error("Filter asset not found");
|
||||
useEffect(() => {
|
||||
setCategoryList([
|
||||
{ category: "Fenestration", categoryImage: feneration },
|
||||
{ category: "Decals", categoryImage: decal },
|
||||
{ category: "Vehicles", categoryImage: vehicle },
|
||||
{ category: "Workstation", categoryImage: workStation },
|
||||
{ category: "Machines", categoryImage: machines },
|
||||
{ category: "Workers", categoryImage: worker },
|
||||
{ category: "Storage", categoryImage: storage },
|
||||
{ category: "Safety", categoryImage: safety },
|
||||
{ category: "Office", categoryImage: office },
|
||||
]);
|
||||
}, []);
|
||||
|
||||
const fetchCategoryAssets = async (asset: any) => {
|
||||
setisLoading(true);
|
||||
setSelectedCategory(asset);
|
||||
try {
|
||||
const res = await getCategoryAsset(asset);
|
||||
setCategoryAssets(res);
|
||||
setFiltereredAssets(res);
|
||||
setisLoading(false); // End loading
|
||||
// eslint-disable-next-line
|
||||
} catch (error) {
|
||||
echo.error("failed to fetch assets");
|
||||
setisLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const activeSubcategories = [
|
||||
{ name: "Safety", icon: <AlertIcon /> },
|
||||
{ name: "Navigation", icon: <NavigationIcon /> },
|
||||
{ name: "Branding", icon: <HangTagIcon /> },
|
||||
{ name: "Informational", icon: <DecalInfoIcon /> },
|
||||
];
|
||||
|
||||
const { selectedSubCategory, setSelectedSubCategory } = useDecalStore();
|
||||
return (
|
||||
<div className="assets-container-main">
|
||||
<Search onChange={handleSearchChange} />
|
||||
<div className="assets-list-section">
|
||||
<section>
|
||||
{(() => {
|
||||
if (isLoading) {
|
||||
return <SkeletonUI type="asset" />; // Show skeleton when loading
|
||||
}
|
||||
};
|
||||
filteredAssets();
|
||||
}, [categoryAssets]);
|
||||
if (searchValue) {
|
||||
return (
|
||||
<div className="assets-result">
|
||||
<div className="assets-wrapper">
|
||||
<div className="searched-content">
|
||||
<p>Results for {searchValue}</p>
|
||||
</div>
|
||||
<div className="assets-container">
|
||||
{categoryAssets?.map((asset: any, index: number) => (
|
||||
<div
|
||||
key={`${index}-${asset.filename}`}
|
||||
className="assets"
|
||||
id={asset.filename}
|
||||
title={asset.filename}
|
||||
>
|
||||
<img
|
||||
src={asset?.thumbnail}
|
||||
alt={asset.filename}
|
||||
className="asset-image"
|
||||
onPointerDown={() => {
|
||||
setSelectedItem({
|
||||
name: asset.filename,
|
||||
id: asset.AssetID,
|
||||
type:
|
||||
asset.type === "undefined"
|
||||
? undefined
|
||||
: asset.type,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
useEffect(() => {
|
||||
setCategoryList([
|
||||
{ category: "Fenestration", categoryImage: feneration },
|
||||
{ category: "Decals", categoryImage: decal },
|
||||
{ category: "Vehicles", categoryImage: vehicle },
|
||||
{ category: "Workstation", categoryImage: workStation },
|
||||
{ category: "Machines", categoryImage: machines },
|
||||
{ category: "Workers", categoryImage: worker },
|
||||
{ category: "Storage", categoryImage: storage },
|
||||
{ category: "Safety", categoryImage: safety },
|
||||
{ category: "Office", categoryImage: office },
|
||||
]);
|
||||
}, []);
|
||||
<div className="asset-name">
|
||||
{asset.filename
|
||||
.split("_")
|
||||
.map(
|
||||
(word: any) =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
)
|
||||
.join(" ")}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const fetchCategoryAssets = async (asset: any) => {
|
||||
setisLoading(true);
|
||||
setSelectedCategory(asset);
|
||||
try {
|
||||
const res = await getCategoryAsset(asset);
|
||||
setCategoryAssets(res);
|
||||
setFiltereredAssets(res);
|
||||
setisLoading(false); // End loading
|
||||
// eslint-disable-next-line
|
||||
} catch (error) {
|
||||
echo.error("failed to fetch assets");
|
||||
setisLoading(false);
|
||||
}
|
||||
};
|
||||
if (selectedCategory) {
|
||||
return (
|
||||
<div className="assets-wrapper">
|
||||
<h2>
|
||||
{selectedCategory}
|
||||
<button
|
||||
className="back-button"
|
||||
id="asset-backButtom"
|
||||
onClick={() => {
|
||||
setSelectedCategory(null);
|
||||
setSelectedSubCategory(null);
|
||||
setCategoryAssets([]);
|
||||
}}
|
||||
>
|
||||
← Back
|
||||
</button>
|
||||
</h2>
|
||||
|
||||
{selectedCategory === "Decals" && (
|
||||
<>
|
||||
<div className="catogory-asset-filter">
|
||||
{activeSubcategories.map((cat, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`catogory-asset-filter-wrapper ${
|
||||
selectedSubCategory === cat.name ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setSelectedSubCategory(cat.name)}
|
||||
>
|
||||
<div className="sub-catagory">{cat.icon}</div>
|
||||
<div className="sub-catagory">{cat.name}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="assets-container">
|
||||
{categoryAssets?.map((asset: any, index: number) => (
|
||||
<div
|
||||
key={`${index}-${asset}`}
|
||||
className="assets"
|
||||
id={asset.filename}
|
||||
title={asset.filename}
|
||||
>
|
||||
<img
|
||||
src={asset?.thumbnail}
|
||||
alt={asset.filename}
|
||||
className="asset-image"
|
||||
onPointerDown={() => {
|
||||
setSelectedItem({
|
||||
name: asset.filename,
|
||||
id: asset.AssetID,
|
||||
type:
|
||||
asset.type === "undefined"
|
||||
? undefined
|
||||
: asset.type,
|
||||
category: asset.category,
|
||||
subType: asset.subType,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div className="asset-name">
|
||||
{asset.filename
|
||||
.split("_")
|
||||
.map(
|
||||
(word: any) =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
)
|
||||
.join(" ")}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{categoryAssets.length === 0 && (
|
||||
<div className="no-asset">
|
||||
🚧 The asset shelf is empty. We're working on filling it
|
||||
up!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const activeSubcategories = [
|
||||
{ name: "Safety", icon: <AlertIcon /> },
|
||||
{ name: "Navigation", icon: <NavigationIcon /> },
|
||||
{ name: "Branding", icon: <HangTagIcon /> },
|
||||
{ name: "Informational", icon: <DecalInfoIcon /> }
|
||||
]
|
||||
|
||||
|
||||
const { selectedSubCategory, setSelectedSubCategory } = useDecalStore();
|
||||
return (
|
||||
<div className="assets-container-main">
|
||||
<Search onChange={handleSearchChange} />
|
||||
<div className="assets-list-section">
|
||||
<section>
|
||||
{(() => {
|
||||
if (isLoading) {
|
||||
return <SkeletonUI type="asset" />; // Show skeleton when loading
|
||||
}
|
||||
if (searchValue) {
|
||||
return (
|
||||
<div className="assets-result">
|
||||
<div className="assets-wrapper">
|
||||
<div className="searched-content">
|
||||
<p>Results for {searchValue}</p>
|
||||
</div>
|
||||
<div className="assets-container">
|
||||
{categoryAssets?.map((asset: any, index: number) => (
|
||||
<div
|
||||
key={`${index}-${asset.filename}`}
|
||||
className="assets"
|
||||
id={asset.filename}
|
||||
title={asset.filename}
|
||||
>
|
||||
<img
|
||||
src={asset?.thumbnail}
|
||||
alt={asset.filename}
|
||||
className="asset-image"
|
||||
onPointerDown={() => {
|
||||
setSelectedItem({
|
||||
name: asset.filename,
|
||||
id: asset.AssetID,
|
||||
type: asset.type === "undefined" ? undefined : asset.type
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="asset-name">
|
||||
{asset.filename
|
||||
.split("_")
|
||||
.map(
|
||||
(word: any) =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
)
|
||||
.join(" ")}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedCategory) {
|
||||
return (
|
||||
<div className="assets-wrapper">
|
||||
<h2>
|
||||
{selectedCategory}
|
||||
<button
|
||||
className="back-button"
|
||||
id="asset-backButtom"
|
||||
onClick={() => {
|
||||
setSelectedCategory(null);
|
||||
setCategoryAssets([]);
|
||||
}}
|
||||
>
|
||||
← Back
|
||||
</button>
|
||||
</h2>
|
||||
|
||||
{selectedCategory === "Decals" &&
|
||||
<>
|
||||
<div className="catogory-asset-filter">
|
||||
{activeSubcategories.map((cat, index) => (
|
||||
<div className={`catogory-asset-filter-wrapper ${selectedSubCategory === cat.name ? "active" : ""}`} onClick={() => setSelectedSubCategory(cat.name)}>
|
||||
<div className="sub-catagory">{cat.icon}</div>
|
||||
<div className="sub-catagory">{cat.name}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
<div className="assets-container">
|
||||
{categoryAssets?.map((asset: any, index: number) => (
|
||||
<div
|
||||
key={`${index}-${asset}`}
|
||||
className="assets"
|
||||
id={asset.filename}
|
||||
title={asset.filename}
|
||||
>
|
||||
<img
|
||||
src={asset?.thumbnail}
|
||||
alt={asset.filename}
|
||||
className="asset-image"
|
||||
onPointerDown={() => {
|
||||
setSelectedItem({
|
||||
name: asset.filename,
|
||||
id: asset.AssetID,
|
||||
type: asset.type === "undefined" ? undefined : asset.type,
|
||||
category: asset.category,
|
||||
subType: asset.subType
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div className="asset-name">
|
||||
{asset.filename.split("_").map((word: any) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ")}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{categoryAssets.length === 0 && (
|
||||
<div className="no-asset">
|
||||
🚧 The asset shelf is empty. We're working on filling it up!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="assets-wrapper">
|
||||
<h2>Categories</h2>
|
||||
<div className="categories-container">
|
||||
{Array.from(
|
||||
new Set(categoryList.map((asset) => asset.category))
|
||||
).map((category, index) => {
|
||||
const categoryInfo = categoryList.find(
|
||||
(asset) => asset.category === category
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={`${index}-${category}`}
|
||||
className="category"
|
||||
id={category}
|
||||
onClick={() => fetchCategoryAssets(category)}
|
||||
>
|
||||
<img
|
||||
src={categoryInfo?.categoryImage ?? ""}
|
||||
alt={category}
|
||||
className="category-image"
|
||||
draggable={false}
|
||||
/>
|
||||
<div className="category-name">{category}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="assets-wrapper">
|
||||
<h2>Categories</h2>
|
||||
<div className="categories-container">
|
||||
{Array.from(
|
||||
new Set(categoryList.map((asset) => asset.category))
|
||||
).map((category, index) => {
|
||||
const categoryInfo = categoryList.find(
|
||||
(asset) => asset.category === category
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={`${index}-${category}`}
|
||||
className="category"
|
||||
id={category}
|
||||
onClick={() => fetchCategoryAssets(category)}
|
||||
>
|
||||
<img
|
||||
src={categoryInfo?.categoryImage ?? ""}
|
||||
alt={category}
|
||||
className="category-image"
|
||||
draggable={false}
|
||||
/>
|
||||
<div className="category-name">{category}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Assets;
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Header from "./Header";
|
||||
import useModuleStore, { useSubModuleStore } from "../../../store/useModuleStore";
|
||||
import useModuleStore, {
|
||||
useSubModuleStore,
|
||||
} from "../../../store/useModuleStore";
|
||||
import {
|
||||
AnalysisIcon,
|
||||
FilePackageIcon,
|
||||
MechanicsIcon,
|
||||
PropertiesIcon,
|
||||
SimulationIcon,
|
||||
AnalysisIcon,
|
||||
FilePackageIcon,
|
||||
MechanicsIcon,
|
||||
PropertiesIcon,
|
||||
SimulationIcon,
|
||||
} from "../../icons/SimulationIcons";
|
||||
import { useToggleStore } from "../../../store/useUIToggleStore";
|
||||
import Visualization from "./visualization/Visualization";
|
||||
import Analysis from "./analysis/Analysis";
|
||||
import Simulations from "./simulation/Simulations";
|
||||
import useVersionHistoryVisibleStore, {
|
||||
useDecalStore,
|
||||
useSaveVersion,
|
||||
useSelectedFloorItem,
|
||||
useToolMode,
|
||||
useDecalStore,
|
||||
useSaveVersion,
|
||||
useSelectedFloorItem,
|
||||
useToolMode,
|
||||
} from "../../../store/builder/store";
|
||||
import {
|
||||
useSelectedEventData,
|
||||
useSelectedEventSphere,
|
||||
useSelectedEventData,
|
||||
useSelectedEventSphere,
|
||||
} from "../../../store/simulation/useSimulationStore";
|
||||
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
|
||||
import GlobalProperties from "./properties/GlobalProperties";
|
||||
@@ -33,277 +35,319 @@ import WallProperties from "./properties/WallProperties";
|
||||
import FloorProperties from "./properties/FloorProperties";
|
||||
import SelectedWallProperties from "./properties/SelectedWallProperties";
|
||||
import SelectedFloorProperties from "./properties/SelectedFloorProperties";
|
||||
import DecalTransformation from "./decals/DecalTransformation";
|
||||
import ResourceManagement from "./resourceManagement/ResourceManagement";
|
||||
import DecalProperties from "./properties/DecalProperties";
|
||||
|
||||
type DisplayComponent =
|
||||
| "versionHistory"
|
||||
| "globalProperties"
|
||||
| "aisleProperties"
|
||||
| "wallProperties"
|
||||
| "floorProperties"
|
||||
| "assetProperties"
|
||||
| "selectedWallProperties"
|
||||
| "selectedFloorProperties"
|
||||
| "zoneProperties"
|
||||
| "simulations"
|
||||
| "mechanics"
|
||||
| "analysis"
|
||||
| "visualization"
|
||||
| "decals"
|
||||
| "resourceManagement"
|
||||
| "none";
|
||||
| "versionHistory"
|
||||
| "globalProperties"
|
||||
| "aisleProperties"
|
||||
| "wallProperties"
|
||||
| "floorProperties"
|
||||
| "assetProperties"
|
||||
| "selectedWallProperties"
|
||||
| "selectedFloorProperties"
|
||||
| "zoneProperties"
|
||||
| "simulations"
|
||||
| "mechanics"
|
||||
| "analysis"
|
||||
| "visualization"
|
||||
| "selectedDecalProperties"
|
||||
| "resourceManagement"
|
||||
| "none";
|
||||
|
||||
const SideBarRight: React.FC = () => {
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toggleUIRight } = useToggleStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { subModule, setSubModule } = useSubModuleStore();
|
||||
const { selectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
|
||||
const { isVersionSaved } = useSaveVersion();
|
||||
const { selectedSubCategory } = useDecalStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toggleUIRight } = useToggleStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { subModule, setSubModule } = useSubModuleStore();
|
||||
const { selectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
const { viewVersionHistory, setVersionHistoryVisible } =
|
||||
useVersionHistoryVisibleStore();
|
||||
const { isVersionSaved } = useSaveVersion();
|
||||
const { selectedSubCategory } = useDecalStore();
|
||||
|
||||
const [displayComponent, setDisplayComponent] = useState<DisplayComponent>("none");
|
||||
const [displayComponent, setDisplayComponent] =
|
||||
useState<DisplayComponent>("none");
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "simulation") setSubModule("properties");
|
||||
if (activeModule === "simulation") setSubModule("simulations");
|
||||
}, [activeModule, setSubModule]);
|
||||
useEffect(() => {
|
||||
if (activeModule !== "simulation") setSubModule("properties");
|
||||
if (activeModule === "simulation") setSubModule("simulations");
|
||||
}, [activeModule, setSubModule]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "mechanics" && selectedEventData && selectedEventSphere) {
|
||||
setSubModule("mechanics");
|
||||
} else if (!selectedEventData && !selectedEventSphere) {
|
||||
if (activeModule === "simulation") {
|
||||
setSubModule("simulations");
|
||||
}
|
||||
useEffect(() => {
|
||||
if (
|
||||
activeModule !== "mechanics" &&
|
||||
selectedEventData &&
|
||||
selectedEventSphere
|
||||
) {
|
||||
setSubModule("mechanics");
|
||||
} else if (!selectedEventData && !selectedEventSphere) {
|
||||
if (activeModule === "simulation") {
|
||||
setSubModule("simulations");
|
||||
}
|
||||
}
|
||||
if (activeModule !== "simulation") {
|
||||
setSubModule("properties");
|
||||
}
|
||||
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule === "visualization") {
|
||||
setDisplayComponent("visualization");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isVersionSaved && activeModule === "simulation") {
|
||||
if (subModule === "simulations") {
|
||||
setDisplayComponent("simulations");
|
||||
return;
|
||||
}
|
||||
if (subModule === "mechanics") {
|
||||
setDisplayComponent("mechanics");
|
||||
return;
|
||||
}
|
||||
if (subModule === "analysis") {
|
||||
setDisplayComponent("analysis");
|
||||
return;
|
||||
}
|
||||
if (subModule === "resourceManagement") {
|
||||
setDisplayComponent("resourceManagement");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (activeModule === "simulation" || activeModule === "builder") {
|
||||
if (subModule === "resourceManagement") {
|
||||
setDisplayComponent("resourceManagement");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (subModule === "properties" && activeModule !== "visualization") {
|
||||
if (selectedFloorItem) {
|
||||
setDisplayComponent("assetProperties");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!selectedFloorItem &&
|
||||
!selectedFloor &&
|
||||
!selectedAisle &&
|
||||
selectedWall
|
||||
) {
|
||||
setDisplayComponent("selectedWallProperties");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!selectedFloorItem &&
|
||||
!selectedWall &&
|
||||
!selectedAisle &&
|
||||
selectedFloor
|
||||
) {
|
||||
setDisplayComponent("selectedFloorProperties");
|
||||
return;
|
||||
}
|
||||
if (viewVersionHistory) {
|
||||
setDisplayComponent("versionHistory");
|
||||
return;
|
||||
}
|
||||
if (selectedSubCategory) {
|
||||
setDisplayComponent("selectedDecalProperties");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!selectedFloorItem &&
|
||||
!selectedFloor &&
|
||||
!selectedWall &&
|
||||
!selectedSubCategory
|
||||
) {
|
||||
if (toolMode === "Aisle") {
|
||||
setDisplayComponent("aisleProperties");
|
||||
return;
|
||||
}
|
||||
if (activeModule !== "simulation") {
|
||||
setSubModule("properties");
|
||||
if (toolMode === "Wall") {
|
||||
setDisplayComponent("wallProperties");
|
||||
return;
|
||||
}
|
||||
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (activeModule === "visualization") {
|
||||
setDisplayComponent("visualization");
|
||||
return;
|
||||
if (toolMode === "Floor") {
|
||||
setDisplayComponent("floorProperties");
|
||||
return;
|
||||
}
|
||||
setDisplayComponent("globalProperties");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isVersionSaved && activeModule === "simulation") {
|
||||
if (subModule === "simulations") {
|
||||
setDisplayComponent("simulations");
|
||||
return;
|
||||
}
|
||||
if (subModule === "mechanics") {
|
||||
setDisplayComponent("mechanics");
|
||||
return;
|
||||
}
|
||||
if (subModule === "analysis") {
|
||||
setDisplayComponent("analysis");
|
||||
return;
|
||||
}
|
||||
if (subModule === "resourceManagement") {
|
||||
setDisplayComponent("resourceManagement");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (
|
||||
subModule === "zoneProperties" &&
|
||||
(activeModule === "builder" || activeModule === "simulation")
|
||||
) {
|
||||
setDisplayComponent("zoneProperties");
|
||||
return;
|
||||
}
|
||||
|
||||
setDisplayComponent("none");
|
||||
}, [
|
||||
viewVersionHistory,
|
||||
activeModule,
|
||||
subModule,
|
||||
isVersionSaved,
|
||||
selectedFloorItem,
|
||||
selectedWall,
|
||||
selectedFloor,
|
||||
selectedAisle,
|
||||
toolMode,
|
||||
selectedSubCategory,
|
||||
]);
|
||||
|
||||
const renderComponent = () => {
|
||||
switch (displayComponent) {
|
||||
case "versionHistory":
|
||||
return <VersionHistory />;
|
||||
case "globalProperties":
|
||||
return <GlobalProperties />;
|
||||
case "aisleProperties":
|
||||
return <AisleProperties />;
|
||||
case "wallProperties":
|
||||
return <WallProperties />;
|
||||
case "floorProperties":
|
||||
return <FloorProperties />;
|
||||
case "assetProperties":
|
||||
return <AssetProperties />;
|
||||
case "selectedWallProperties":
|
||||
return <SelectedWallProperties />;
|
||||
case "selectedFloorProperties":
|
||||
return <SelectedFloorProperties />;
|
||||
case "zoneProperties":
|
||||
return <ZoneProperties />;
|
||||
case "simulations":
|
||||
return <Simulations />;
|
||||
case "mechanics":
|
||||
return <EventProperties />;
|
||||
case "analysis":
|
||||
return <Analysis />;
|
||||
case "visualization":
|
||||
return <Visualization />;
|
||||
case "selectedDecalProperties":
|
||||
return <DecalProperties />;
|
||||
case "resourceManagement":
|
||||
return <ResourceManagement />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
if (activeModule === "simulation" || activeModule === "builder") {
|
||||
if (subModule === "resourceManagement") {
|
||||
setDisplayComponent("resourceManagement");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (subModule === "properties" && activeModule !== "visualization") {
|
||||
if (selectedFloorItem) {
|
||||
setDisplayComponent("assetProperties");
|
||||
return;
|
||||
}
|
||||
if (!selectedFloorItem && !selectedFloor && !selectedAisle && selectedWall) {
|
||||
setDisplayComponent("selectedWallProperties");
|
||||
return;
|
||||
}
|
||||
if (!selectedFloorItem && !selectedWall && !selectedAisle && selectedFloor) {
|
||||
setDisplayComponent("selectedFloorProperties");
|
||||
return;
|
||||
}
|
||||
if (viewVersionHistory) {
|
||||
setDisplayComponent("versionHistory");
|
||||
return;
|
||||
}
|
||||
if (selectedSubCategory) {
|
||||
setDisplayComponent("decals");
|
||||
return;
|
||||
}
|
||||
if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedSubCategory) {
|
||||
|
||||
|
||||
if (toolMode === "Aisle") {
|
||||
setDisplayComponent("aisleProperties");
|
||||
return;
|
||||
}
|
||||
if (toolMode === "Wall") {
|
||||
setDisplayComponent("wallProperties");
|
||||
return;
|
||||
}
|
||||
if (toolMode === "Floor") {
|
||||
setDisplayComponent("floorProperties");
|
||||
return;
|
||||
}
|
||||
setDisplayComponent("globalProperties");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (subModule === "zoneProperties" && (activeModule === "builder" || activeModule === "simulation")) {
|
||||
setDisplayComponent("zoneProperties");
|
||||
return;
|
||||
}
|
||||
|
||||
setDisplayComponent("none");
|
||||
}, [viewVersionHistory, activeModule, subModule, isVersionSaved, selectedFloorItem, selectedWall, selectedFloor, selectedAisle, toolMode, selectedSubCategory]);
|
||||
|
||||
const renderComponent = () => {
|
||||
|
||||
switch (displayComponent) {
|
||||
case "versionHistory":
|
||||
return <VersionHistory />;
|
||||
case "globalProperties":
|
||||
return <GlobalProperties />;
|
||||
case "aisleProperties":
|
||||
return <AisleProperties />;
|
||||
case "wallProperties":
|
||||
return <WallProperties />;
|
||||
case "floorProperties":
|
||||
return <FloorProperties />;
|
||||
case "assetProperties":
|
||||
return <AssetProperties />;
|
||||
case "selectedWallProperties":
|
||||
return <SelectedWallProperties />;
|
||||
case "selectedFloorProperties":
|
||||
return <SelectedFloorProperties />;
|
||||
case "zoneProperties":
|
||||
return <ZoneProperties />;
|
||||
case "simulations":
|
||||
return <Simulations />;
|
||||
case "mechanics":
|
||||
return <EventProperties />;
|
||||
case "analysis":
|
||||
return <Analysis />;
|
||||
case "visualization":
|
||||
return <Visualization />;
|
||||
case "decals":
|
||||
return <DecalTransformation />;
|
||||
case "resourceManagement":
|
||||
return <ResourceManagement />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`sidebar-right-wrapper ${toggleUIRight && (!isVersionSaved || activeModule !== "simulation") ? "open" : "closed"
|
||||
}`}
|
||||
>
|
||||
<Header />
|
||||
{toggleUIRight && (
|
||||
return (
|
||||
<div
|
||||
className={`sidebar-right-wrapper ${
|
||||
toggleUIRight && (!isVersionSaved || activeModule !== "simulation")
|
||||
? "open"
|
||||
: "closed"
|
||||
}`}
|
||||
>
|
||||
<Header />
|
||||
{toggleUIRight && (
|
||||
<>
|
||||
{(!isVersionSaved || activeModule !== "simulation") && (
|
||||
<div className="sidebar-actions-container">
|
||||
{activeModule !== "simulation" && (
|
||||
<>
|
||||
{(!isVersionSaved || activeModule !== "simulation") && (
|
||||
<div className="sidebar-actions-container">
|
||||
|
||||
{activeModule !== "simulation" && (
|
||||
<>
|
||||
<button
|
||||
id="sidebar-action-list-properties"
|
||||
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setSubModule("properties");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">properties</div>
|
||||
<PropertiesIcon isActive={subModule === "properties"} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<button
|
||||
id="sidebar-action-list-simulation"
|
||||
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setSubModule("simulations");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">simulations</div>
|
||||
<SimulationIcon isActive={subModule === "simulations"} />
|
||||
</button>
|
||||
<button
|
||||
id="sidebar-action-list-mechanics"
|
||||
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setSubModule("mechanics");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">mechanics</div>
|
||||
<MechanicsIcon isActive={subModule === "mechanics"} />
|
||||
</button>
|
||||
<button
|
||||
id="sidebar-action-list-analysis"
|
||||
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setSubModule("analysis");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">analysis</div>
|
||||
<AnalysisIcon isActive={subModule === "analysis"} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{(activeModule === "builder" || activeModule === "simulation") && (
|
||||
<button
|
||||
id="sidebar-action-list-properties"
|
||||
className={`sidebar-action-list ${subModule === "resourceManagement" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setSubModule("resourceManagement");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">Resource Management</div>
|
||||
<FilePackageIcon isActive={subModule === "resourceManagement"} />
|
||||
</button>
|
||||
)}
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
{displayComponent !== "none" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
{renderComponent()}
|
||||
{/* <ResourceManagement /> */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
id="sidebar-action-list-properties"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "properties" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("properties");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">properties</div>
|
||||
<PropertiesIcon isActive={subModule === "properties"} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
)}
|
||||
|
||||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<button
|
||||
id="sidebar-action-list-simulation"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "simulations" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("simulations");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">simulations</div>
|
||||
<SimulationIcon isActive={subModule === "simulations"} />
|
||||
</button>
|
||||
<button
|
||||
id="sidebar-action-list-mechanics"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "mechanics" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("mechanics");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">mechanics</div>
|
||||
<MechanicsIcon isActive={subModule === "mechanics"} />
|
||||
</button>
|
||||
<button
|
||||
id="sidebar-action-list-analysis"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "analysis" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("analysis");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">analysis</div>
|
||||
<AnalysisIcon isActive={subModule === "analysis"} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{(activeModule === "builder" ||
|
||||
activeModule === "simulation") && (
|
||||
<button
|
||||
id="sidebar-action-list-properties"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "resourceManagement" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("resourceManagement");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">Resource Management</div>
|
||||
<FilePackageIcon
|
||||
isActive={subModule === "resourceManagement"}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{displayComponent !== "none" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
{renderComponent()}
|
||||
{/* <ResourceManagement /> */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideBarRight;
|
||||
export default SideBarRight;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import React from "react";
|
||||
import { EyeDroperIcon } from "../../../icons/ExportCommonIcons";
|
||||
// import { useThree } from "@react-three/fiber";
|
||||
|
||||
interface PositionInputProps {
|
||||
onChange: (value: [number, number, number]) => void; // Callback for value change
|
||||
@@ -24,7 +22,7 @@ const Vector3Input: React.FC<PositionInputProps> = ({
|
||||
if (!value) return;
|
||||
const updatedValue = [...value] as [number, number, number];
|
||||
updatedValue[index] = parseFloat(newValue) || 0;
|
||||
// console.log('updatedValue: ', updatedValue);
|
||||
console.log('updatedValue: ', updatedValue);
|
||||
onChange(updatedValue);
|
||||
};
|
||||
|
||||
@@ -42,8 +40,8 @@ const Vector3Input: React.FC<PositionInputProps> = ({
|
||||
<input
|
||||
className="custom-input-field"
|
||||
type={type}
|
||||
value={value?.[i] !== undefined ? value[i].toFixed(2) : ""}
|
||||
// onChange={(e) => handleChange(i, e.target.value)}
|
||||
value={value?.[i] !== undefined ? value[i].toFixed(1) : ""}
|
||||
onChange={(e) => handleChange(i, e.target.value)}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import RotationInput from '../customInput/RotationInput';
|
||||
import PositionInput from '../customInput/PositionInputs';
|
||||
import { LockIcon } from '../../../icons/RealTimeVisulationIcons';
|
||||
import { LayeringBottomIcon, LayeringTopIcon, ValueUpdateIcon } from '../../../icons/ExportCommonIcons';
|
||||
import decalImage from "../../../../assets/image/sampleDecal.png"
|
||||
const DecalTransformation = () => {
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className='decal-transformation-container'>
|
||||
{["position", "rotation", "scale"].map((transformation) => (
|
||||
<div className="transformation-wrapper">
|
||||
<div className="header">{transformation}</div>
|
||||
<div className="input-wrapppers">
|
||||
<input type="number" name="" id="" />
|
||||
<div className="icon"><ValueUpdateIcon /></div>
|
||||
<input type="number" name="" id="" />
|
||||
<div className="icon"><ValueUpdateIcon /></div>
|
||||
<input type="number" name="" id="" />
|
||||
<div className="icon"><ValueUpdateIcon /></div>
|
||||
<div className="icon"><LockIcon /></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="transformation-wrapper opacity">
|
||||
<div className="header">opacity</div>
|
||||
<div className="input-wrapppers">
|
||||
<input type="number" name="" id="" />
|
||||
<div className="icon"><ValueUpdateIcon /></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="transformation-wrapper opacity">
|
||||
<div className="header">Layering</div>
|
||||
|
||||
<div className="layers">
|
||||
<div className="icon">
|
||||
<LayeringBottomIcon />
|
||||
</div>
|
||||
<div className="icon">
|
||||
<LayeringTopIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="preview">
|
||||
<img src={decalImage} alt="" />
|
||||
<div className="replace-btn">replace</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DecalTransformation;
|
||||
|
||||
@@ -103,32 +103,32 @@ const AssetProperties: React.FC = () => {
|
||||
</section>
|
||||
<div className="header">Animations</div>
|
||||
<section className="animations-lists">
|
||||
{assets.map((asset) => (
|
||||
<>
|
||||
{asset.modelUuid === selectedFloorItem.uuid &&
|
||||
asset.animations &&
|
||||
asset.animations.length > 0 &&
|
||||
asset.animations.map((animation, index) => (
|
||||
<div key={index} className="animations-list-wrapper">
|
||||
<div
|
||||
onClick={() => handleAnimationClick(animation)}
|
||||
onMouseEnter={() => setHoveredIndex(index)}
|
||||
onMouseLeave={() => setHoveredIndex(null)}
|
||||
className="animations-list"
|
||||
style={{
|
||||
background:
|
||||
hoveredIndex === index
|
||||
? "#7b4cd3"
|
||||
: "var(--background-color)",
|
||||
}}
|
||||
>
|
||||
{animation.charAt(0).toUpperCase() +
|
||||
animation.slice(1).toLowerCase()}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
))}
|
||||
{assets.map((asset) => {
|
||||
if (asset.modelUuid !== selectedFloorItem.uuid || !asset.animations)
|
||||
return null;
|
||||
|
||||
return asset.animations.map((animation, index) => (
|
||||
<div key={index} className="animations-list-wrapper">
|
||||
<div
|
||||
onClick={() => handleAnimationClick(animation)}
|
||||
onMouseEnter={() => setHoveredIndex(index)}
|
||||
onMouseLeave={() => setHoveredIndex(null)}
|
||||
className="animations-list"
|
||||
style={{
|
||||
background:
|
||||
hoveredIndex === index
|
||||
? "var(--background-color-button)"
|
||||
: "var(--background-color)",
|
||||
color:
|
||||
hoveredIndex === index ? "var(--text-button-color)" : "",
|
||||
}}
|
||||
>
|
||||
{animation.charAt(0).toUpperCase() +
|
||||
animation.slice(1).toLowerCase()}
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
})}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
LayeringBottomIcon,
|
||||
LayeringTopIcon,
|
||||
} from "../../../icons/ExportCommonIcons";
|
||||
import InputRange from "../../../ui/inputs/InputRange";
|
||||
import RotationInput from "../customInput/RotationInput";
|
||||
import Vector3Input from "../customInput/Vector3Input";
|
||||
|
||||
const DecalProperties = () => {
|
||||
return (
|
||||
<div className="decal-transformation-container">
|
||||
<div className="header">Decal Propertis</div>
|
||||
<section>
|
||||
<RotationInput
|
||||
onChange={() => {}}
|
||||
value={10}
|
||||
/>
|
||||
<Vector3Input
|
||||
onChange={(value) => console.log(value)}
|
||||
header="Scale"
|
||||
value={[0, 0, 0] as [number, number, number]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<InputRange
|
||||
label="Opacity"
|
||||
value={1}
|
||||
min={0}
|
||||
step={0.1}
|
||||
max={1}
|
||||
onChange={(value: number) => console.log(value)}
|
||||
key={"6"}
|
||||
/>
|
||||
|
||||
<div className="transformation-wrapper opacity">
|
||||
<div className="transformation-header">Layering</div>
|
||||
|
||||
<div className="layers-list">
|
||||
<button className="layer-move-btn">
|
||||
<LayeringBottomIcon />
|
||||
</button>
|
||||
<button className="layer-move-btn">
|
||||
<LayeringTopIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DecalProperties;
|
||||
@@ -3,7 +3,7 @@ import InputRange from "../../../../../ui/inputs/InputRange";
|
||||
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
|
||||
import SwapAction from "./SwapAction";
|
||||
|
||||
interface AssemblyActionProps {
|
||||
interface ManufactureActionProps {
|
||||
processTime: {
|
||||
value: number;
|
||||
min: number;
|
||||
@@ -11,7 +11,7 @@ interface AssemblyActionProps {
|
||||
disabled?: boolean,
|
||||
onChange: (value: number) => void;
|
||||
};
|
||||
assemblyCount: {
|
||||
manufactureCount: {
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
@@ -26,9 +26,9 @@ interface AssemblyActionProps {
|
||||
clearPoints: () => void;
|
||||
}
|
||||
|
||||
const AssemblyAction: React.FC<AssemblyActionProps> = ({
|
||||
const ManufactureAction: React.FC<ManufactureActionProps> = ({
|
||||
processTime,
|
||||
assemblyCount,
|
||||
manufactureCount,
|
||||
swapOptions,
|
||||
swapDefaultOption,
|
||||
onSwapSelect,
|
||||
@@ -46,18 +46,18 @@ const AssemblyAction: React.FC<AssemblyActionProps> = ({
|
||||
onChange={processTime.onChange}
|
||||
/>
|
||||
|
||||
{assemblyCount && (
|
||||
{manufactureCount && (
|
||||
<InputWithDropDown
|
||||
label="Assembly Count"
|
||||
value={assemblyCount.value.toString()}
|
||||
min={assemblyCount.min}
|
||||
max={assemblyCount.max}
|
||||
disabled={assemblyCount.disabled}
|
||||
defaultValue={assemblyCount.defaultValue}
|
||||
step={assemblyCount.step}
|
||||
label="Manufacture Count"
|
||||
value={manufactureCount.value.toString()}
|
||||
min={manufactureCount.min}
|
||||
max={manufactureCount.max}
|
||||
disabled={manufactureCount.disabled}
|
||||
defaultValue={manufactureCount.defaultValue}
|
||||
step={manufactureCount.step}
|
||||
activeOption="unit"
|
||||
onClick={() => { }}
|
||||
onChange={(value) => assemblyCount.onChange(parseInt(value))}
|
||||
onChange={(value) => manufactureCount.onChange(parseInt(value))}
|
||||
/>
|
||||
)}
|
||||
<SwapAction
|
||||
@@ -82,4 +82,4 @@ const AssemblyAction: React.FC<AssemblyActionProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default AssemblyAction;
|
||||
export default ManufactureAction;
|
||||
@@ -7,7 +7,7 @@ import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
|
||||
import Trigger from "../trigger/Trigger";
|
||||
import ActionsList from "../components/ActionsList";
|
||||
import WorkerAction from "../actions/WorkerAction";
|
||||
import AssemblyAction from "../actions/AssemblyAction";
|
||||
import ManufactureAction from "../actions/ManufactureAction";
|
||||
|
||||
import { useSelectedEventData, useSelectedAction } from "../../../../../../store/simulation/useSimulationStore";
|
||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
@@ -17,10 +17,10 @@ import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
function HumanMechanics() {
|
||||
const [activeOption, setActiveOption] = useState<"worker" | "assembly" | "operator">("worker");
|
||||
const [activeOption, setActiveOption] = useState<"worker" | "manufacturer" | "operator" | "assembler">("worker");
|
||||
const [speed, setSpeed] = useState("0.5");
|
||||
const [loadCount, setLoadCount] = useState(0);
|
||||
const [assemblyCount, setAssemblyCount] = useState(0);
|
||||
const [manufactureCount, setManufactureCount] = useState(0);
|
||||
const [loadCapacity, setLoadCapacity] = useState("1");
|
||||
const [processTime, setProcessTime] = useState(10);
|
||||
const [swappedMaterial, setSwappedMaterial] = useState("Default material");
|
||||
@@ -58,7 +58,7 @@ function HumanMechanics() {
|
||||
setLoadCapacity(firstAction.loadCapacity.toString());
|
||||
setActiveOption(firstAction.actionType);
|
||||
setLoadCount(firstAction.loadCount || 0);
|
||||
setAssemblyCount(firstAction.assemblyCount || 0);
|
||||
setManufactureCount(firstAction.manufactureCount || 0);
|
||||
setProcessTime(firstAction.processTime || 10);
|
||||
setSwappedMaterial(firstAction.swapMaterial || "Default material");
|
||||
}
|
||||
@@ -79,7 +79,7 @@ function HumanMechanics() {
|
||||
|
||||
const newCurrentAction = getActionByUuid(selectedProduct.productUuid, actionUuid);
|
||||
|
||||
if (newCurrentAction && (newCurrentAction.actionType === 'assembly' || newCurrentAction?.actionType === 'worker' || newCurrentAction?.actionType === "operator")) {
|
||||
if (newCurrentAction && (newCurrentAction.actionType === 'manufacturer' || newCurrentAction?.actionType === 'worker' || newCurrentAction?.actionType === "operator")) {
|
||||
if (!selectedAction.actionId) {
|
||||
setSelectedAction(newCurrentAction.actionUuid, newCurrentAction.actionName);
|
||||
}
|
||||
@@ -87,9 +87,9 @@ function HumanMechanics() {
|
||||
setActiveOption(newCurrentAction.actionType);
|
||||
setLoadCapacity(newCurrentAction.loadCapacity.toString());
|
||||
setLoadCount(newCurrentAction.loadCount || 0);
|
||||
setAssemblyCount(newCurrentAction.assemblyCount || 0);
|
||||
setManufactureCount(newCurrentAction.manufactureCount || 0);
|
||||
|
||||
if (newCurrentAction.actionType === 'assembly') {
|
||||
if (newCurrentAction.actionType === 'manufacturer') {
|
||||
setProcessTime(newCurrentAction.processTime || 10);
|
||||
setSwappedMaterial(newCurrentAction.swapMaterial || "Default material");
|
||||
}
|
||||
@@ -118,7 +118,7 @@ function HumanMechanics() {
|
||||
const handleSelectActionType = (actionType: string) => {
|
||||
if (!selectedAction.actionId || !currentAction || !selectedPointData) return;
|
||||
|
||||
const updatedAction = { ...currentAction, actionType: actionType as "worker" | "assembly" | "operator" };
|
||||
const updatedAction = { ...currentAction, actionType: actionType as "worker" | "manufacturer" | "operator" | "assembler" };
|
||||
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
|
||||
const updatedPoint = { ...selectedPointData, actions: updatedActions };
|
||||
|
||||
@@ -203,10 +203,10 @@ function HumanMechanics() {
|
||||
setLoadCount(value);
|
||||
};
|
||||
|
||||
const handleAssemblyCountChange = (value: number) => {
|
||||
const handleManufactureCountChange = (value: number) => {
|
||||
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
||||
|
||||
const updatedAction = { ...currentAction, assemblyCount: value };
|
||||
const updatedAction = { ...currentAction, manufactureCount: value };
|
||||
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
|
||||
const updatedPoint = { ...selectedPointData, actions: updatedActions };
|
||||
|
||||
@@ -222,7 +222,7 @@ function HumanMechanics() {
|
||||
|
||||
setCurrentAction(updatedAction);
|
||||
setSelectedPointData(updatedPoint);
|
||||
setAssemblyCount(value);
|
||||
setManufactureCount(value);
|
||||
};
|
||||
|
||||
const handleProcessTimeChange = (value: number) => {
|
||||
@@ -274,8 +274,8 @@ function HumanMechanics() {
|
||||
|
||||
const updatedAction: HumanAction = JSON.parse(JSON.stringify(currentAction));
|
||||
|
||||
if (updatedAction.actionType === 'assembly') {
|
||||
updatedAction.assemblyPoint = { position: null, rotation: null, }
|
||||
if (updatedAction.actionType === 'manufacturer') {
|
||||
updatedAction.manufacturePoint = { position: null, rotation: null, }
|
||||
} else {
|
||||
updatedAction.pickUpPoint = { position: null, rotation: null, };
|
||||
updatedAction.dropPoint = { position: null, rotation: null, }
|
||||
@@ -307,6 +307,11 @@ function HumanMechanics() {
|
||||
actionType: "worker",
|
||||
loadCount: 1,
|
||||
assemblyCount: 1,
|
||||
assemblyCondition: {
|
||||
conditionType: 'material',
|
||||
materialType: "Default material"
|
||||
},
|
||||
manufactureCount: 1,
|
||||
loadCapacity: 1,
|
||||
processTime: 10,
|
||||
triggers: [],
|
||||
@@ -397,7 +402,7 @@ function HumanMechanics() {
|
||||
<LabledDropdown
|
||||
label="Action Type"
|
||||
defaultOption={activeOption}
|
||||
options={["worker", "assembly", "operator"]}
|
||||
options={["worker", "manufacturer", "operator"]}
|
||||
onSelect={handleSelectActionType}
|
||||
disabled={false}
|
||||
/>
|
||||
@@ -425,22 +430,22 @@ function HumanMechanics() {
|
||||
clearPoints={handleClearPoints}
|
||||
/>
|
||||
}
|
||||
{currentAction.actionType === 'assembly' &&
|
||||
<AssemblyAction
|
||||
{currentAction.actionType === 'manufacturer' &&
|
||||
<ManufactureAction
|
||||
processTime={{
|
||||
value: processTime,
|
||||
min: 1,
|
||||
max: 60,
|
||||
onChange: handleProcessTimeChange,
|
||||
}}
|
||||
assemblyCount={{
|
||||
value: assemblyCount,
|
||||
manufactureCount={{
|
||||
value: manufactureCount,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
defaultValue: "1",
|
||||
disabled: false,
|
||||
onChange: handleAssemblyCountChange,
|
||||
onChange: handleManufactureCountChange,
|
||||
}}
|
||||
swapOptions={["Default material", "Material 1", "Material 2", "Material 3"]}
|
||||
swapDefaultOption={swappedMaterial}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
|
||||
interface InputToggleProps {
|
||||
label: string; // Represents the toggle state (on/off)
|
||||
label: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
onClick?: () => void; // Function to handle toggle clicks
|
||||
onChange?: (value: number) => void; // Function to handle toggle clicks
|
||||
step?: number; // Added step prop
|
||||
onClick?: () => void;
|
||||
onChange?: (value: number) => void;
|
||||
disabled?: boolean;
|
||||
value?: number;
|
||||
onPointerUp?: (value: number) => void;
|
||||
@@ -17,54 +18,46 @@ const InputRange: React.FC<InputToggleProps> = ({
|
||||
onChange,
|
||||
min,
|
||||
max,
|
||||
step = 1, // default step
|
||||
disabled,
|
||||
value,
|
||||
onPointerUp,
|
||||
}) => {
|
||||
const [rangeValue, setRangeValue] = useState<number>(value ? value : 5);
|
||||
const [rangeValue, setRangeValue] = useState<number>(value ?? 5);
|
||||
|
||||
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
const newValue = parseInt(e.target.value); // Parse the value to an integer
|
||||
const newValue = parseFloat(e.target.value);
|
||||
setRangeValue(newValue);
|
||||
|
||||
setRangeValue(newValue); // Update the local state
|
||||
|
||||
if (onChange) {
|
||||
onChange(newValue); // Call the onChange function if it exists
|
||||
}
|
||||
if (onChange) onChange(newValue);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
value && setRangeValue(value);
|
||||
if (value !== undefined) setRangeValue(value);
|
||||
}, [value]);
|
||||
|
||||
function handlePointerUp(e: React.PointerEvent<HTMLInputElement>) {
|
||||
const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
|
||||
|
||||
if (onPointerUp) {
|
||||
onPointerUp(newValue); // Call the callback function if it exists
|
||||
}
|
||||
const newValue = parseFloat(e.currentTarget.value);
|
||||
if (onPointerUp) onPointerUp(newValue);
|
||||
}
|
||||
function handlekey(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
|
||||
|
||||
if (onPointerUp) {
|
||||
onPointerUp(newValue); // Call the callback function if it exists
|
||||
}
|
||||
function handleKey(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
const newValue = parseFloat(e.currentTarget.value);
|
||||
if (onPointerUp) onPointerUp(newValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="input-range-container">
|
||||
<label
|
||||
htmlFor={`range-input ${value}`}
|
||||
className="label"
|
||||
onClick={onClick}
|
||||
>
|
||||
<label htmlFor={`range-input-${value}`} className="label" onClick={onClick}>
|
||||
{label}
|
||||
</label>
|
||||
<div className="input-container">
|
||||
<input
|
||||
id={`range-input ${value}`}
|
||||
id={`range-input-${value}`}
|
||||
type="range"
|
||||
min={min}
|
||||
max={max}
|
||||
step={step} // added here
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
value={rangeValue}
|
||||
@@ -73,15 +66,14 @@ const InputRange: React.FC<InputToggleProps> = ({
|
||||
<input
|
||||
type="number"
|
||||
min={min}
|
||||
className="input-value"
|
||||
max={max}
|
||||
step={step} // added here
|
||||
value={rangeValue}
|
||||
onChange={handleChange}
|
||||
disabled={disabled}
|
||||
className="input-value"
|
||||
onKeyUp={(e) => {
|
||||
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
||||
handlekey(e);
|
||||
}
|
||||
if (e.key === "ArrowUp" || e.key === "ArrowDown") handleKey(e);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,11 @@ const RegularDropDown: React.FC<DropdownProps> = ({
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [filteredOptions, setFilteredOptions] = useState<string[]>(options);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const [position, setPosition] = useState<{ top: number; left: number; width: number }>({
|
||||
const [position, setPosition] = useState<{
|
||||
top: number;
|
||||
left: number;
|
||||
width: number;
|
||||
}>({
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 0,
|
||||
@@ -88,7 +92,13 @@ const RegularDropDown: React.FC<DropdownProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="regularDropdown-container" ref={dropdownRef}>
|
||||
<div
|
||||
className="regularDropdown-container"
|
||||
ref={dropdownRef}
|
||||
onPointerLeave={() => {
|
||||
setIsOpen(false);
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
|
||||
<div className="key">{selectedOption || header}</div>
|
||||
|
||||
@@ -163,7 +163,10 @@ const LogList: React.FC = () => {
|
||||
<RenderInNewWindow
|
||||
title="Log list"
|
||||
theme={localStorage.getItem("theme")}
|
||||
onClose={() => setOpen(false)}
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
setIsLogListVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="log-list-new-window-wrapper">
|
||||
<Logs
|
||||
|
||||
@@ -272,6 +272,11 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
||||
actionType: "worker",
|
||||
loadCount: 1,
|
||||
assemblyCount: 1,
|
||||
assemblyCondition: {
|
||||
conditionType: 'material',
|
||||
materialType: "Default material"
|
||||
},
|
||||
manufactureCount: 1,
|
||||
loadCapacity: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
|
||||
@@ -390,6 +390,11 @@ async function handleModelLoad(
|
||||
actionType: "worker",
|
||||
loadCount: 1,
|
||||
assemblyCount: 1,
|
||||
assemblyCondition: {
|
||||
conditionType: 'material',
|
||||
materialType: "Default material"
|
||||
},
|
||||
manufactureCount: 1,
|
||||
loadCapacity: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
Shape,
|
||||
Vector2,
|
||||
DoubleSide,
|
||||
TextureLoader,
|
||||
RepeatWrapping,
|
||||
SRGBColorSpace,
|
||||
NoColorSpace,
|
||||
} from "three";
|
||||
import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, } from "three";
|
||||
import { useLoader } from "@react-three/fiber";
|
||||
import { Extrude } from "@react-three/drei";
|
||||
import useModuleStore from "../../../../../store/useModuleStore";
|
||||
@@ -36,212 +28,188 @@ import material4MetalicMap from "../../../../../assets/textures/floor/tex3/metal
|
||||
import material4NormalMap from "../../../../../assets/textures/floor/tex3/metal_plate_nor_gl_1k.png";
|
||||
|
||||
function FloorInstance({ floor }: { floor: Floor }) {
|
||||
const { togglView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedFloor, setSelectedFloor, setSelectedDecal } =
|
||||
useBuilderStore();
|
||||
const savedTheme = localStorage.getItem("theme");
|
||||
const { togglView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedFloor, setSelectedFloor, setSelectedDecal } = useBuilderStore();
|
||||
const savedTheme = localStorage.getItem("theme");
|
||||
|
||||
const materials: Record<
|
||||
string,
|
||||
{
|
||||
map: string;
|
||||
roughnessMap?: string;
|
||||
metalnessMap?: string;
|
||||
normalMap?: string;
|
||||
textureTileScale?: [number, number];
|
||||
const materials: Record<
|
||||
string,
|
||||
{
|
||||
map: string;
|
||||
roughnessMap?: string;
|
||||
metalnessMap?: string;
|
||||
normalMap?: string;
|
||||
textureTileScale?: [number, number];
|
||||
}
|
||||
> = {
|
||||
"Default Material": { map: savedTheme === "dark" ? texturePathDark : texturePath, },
|
||||
"Material 1": { map: material1 },
|
||||
"Material 2": {
|
||||
map: material2Map,
|
||||
roughnessMap: material2MetalicRoughnessMap,
|
||||
metalnessMap: material2MetalicRoughnessMap,
|
||||
normalMap: material2NormalMap,
|
||||
textureTileScale: [0.1, 0.1],
|
||||
},
|
||||
"Material 3": {
|
||||
map: material3Map,
|
||||
roughnessMap: material3MetalicRoughnessMap,
|
||||
metalnessMap: material3MetalicRoughnessMap,
|
||||
normalMap: material3NormalMap,
|
||||
textureTileScale: [0.35, 0.5],
|
||||
},
|
||||
"Material 4": {
|
||||
map: material4Map,
|
||||
roughnessMap: material4RoughnessMap,
|
||||
metalnessMap: material4MetalicMap,
|
||||
normalMap: material4NormalMap,
|
||||
},
|
||||
};
|
||||
|
||||
const shape = useMemo(() => {
|
||||
const shape = new Shape();
|
||||
const points = floor.points.map((p) => new Vector2(p.position[0], p.position[2]));
|
||||
if (points.length < 3) return null;
|
||||
shape.moveTo(points[0].x, points[0].y);
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
shape.lineTo(points[i].x, points[i].y);
|
||||
}
|
||||
return shape;
|
||||
}, [floor]);
|
||||
|
||||
const textureScale = Constants.floorConfig.textureScale;
|
||||
|
||||
// Helper function to handle texture maps and filter out null values
|
||||
function getMaterialMaps(material: any, defaultMap: any) {
|
||||
const materialMap = material.map || defaultMap;
|
||||
const normalMap = material.normalMap || null;
|
||||
const roughnessMap = material.roughnessMap || null;
|
||||
const metalnessMap = material.metalnessMap || null;
|
||||
|
||||
return [materialMap, normalMap, roughnessMap, metalnessMap].filter((texture): texture is string => texture !== null);
|
||||
}
|
||||
> = {
|
||||
"Default Material": {
|
||||
map: savedTheme === "dark" ? texturePathDark : texturePath,
|
||||
},
|
||||
"Material 1": {
|
||||
map: material1,
|
||||
},
|
||||
"Material 2": {
|
||||
map: material2Map,
|
||||
roughnessMap: material2MetalicRoughnessMap,
|
||||
metalnessMap: material2MetalicRoughnessMap,
|
||||
normalMap: material2NormalMap,
|
||||
textureTileScale: [0.1, 0.1],
|
||||
},
|
||||
"Material 3": {
|
||||
map: material3Map,
|
||||
roughnessMap: material3MetalicRoughnessMap,
|
||||
metalnessMap: material3MetalicRoughnessMap,
|
||||
normalMap: material3NormalMap,
|
||||
textureTileScale: [0.35, 0.5],
|
||||
},
|
||||
"Material 4": {
|
||||
map: material4Map,
|
||||
roughnessMap: material4RoughnessMap,
|
||||
metalnessMap: material4MetalicMap,
|
||||
normalMap: material4NormalMap,
|
||||
},
|
||||
};
|
||||
|
||||
const shape = useMemo(() => {
|
||||
const shape = new Shape();
|
||||
const points = floor.points.map(
|
||||
(p) => new Vector2(p.position[0], p.position[2])
|
||||
);
|
||||
if (points.length < 3) return null;
|
||||
shape.moveTo(points[0].x, points[0].y);
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
shape.lineTo(points[i].x, points[i].y);
|
||||
}
|
||||
return shape;
|
||||
}, [floor]);
|
||||
// Default material map
|
||||
const defaultMaterialMap = materials["Default Material"].map;
|
||||
|
||||
const textureScale = Constants.floorConfig.textureScale;
|
||||
// Get top and side material maps
|
||||
const topMaterial = materials[floor.topMaterial];
|
||||
const sideMaterial = materials[floor.sideMaterial];
|
||||
|
||||
// Helper function to handle texture maps and filter out null values
|
||||
function getMaterialMaps(material: any, defaultMap: any) {
|
||||
const materialMap = material.map || defaultMap;
|
||||
const normalMap = material.normalMap || null;
|
||||
const roughnessMap = material.roughnessMap || null;
|
||||
const metalnessMap = material.metalnessMap || null;
|
||||
// Get the filtered lists for top and side textures
|
||||
const topTexturesList = getMaterialMaps(topMaterial, defaultMaterialMap);
|
||||
const sideTexturesList = getMaterialMaps(sideMaterial, defaultMaterialMap);
|
||||
|
||||
return [materialMap, normalMap, roughnessMap, metalnessMap].filter(
|
||||
(texture): texture is string => texture !== null
|
||||
);
|
||||
}
|
||||
// Use loader to load top and side textures
|
||||
const [topTexture, topNormalTexture, topRoughnessTexture, topMetalicTexture] = useLoader(TextureLoader, topTexturesList);
|
||||
|
||||
// Default material map
|
||||
const defaultMaterialMap = materials["Default Material"].map;
|
||||
const [sideTexture, sideNormalTexture, sideRoughnessTexture, sideMetalicTexture] = useLoader(TextureLoader, sideTexturesList);
|
||||
|
||||
// Get top and side material maps
|
||||
const topMaterial = materials[floor.topMaterial];
|
||||
const sideMaterial = materials[floor.sideMaterial];
|
||||
// Early exit if materials are missing
|
||||
if (!materials[floor.topMaterial] || !materials[floor.sideMaterial]) return null;
|
||||
|
||||
// Get the filtered lists for top and side textures
|
||||
const topTexturesList = getMaterialMaps(topMaterial, defaultMaterialMap);
|
||||
const sideTexturesList = getMaterialMaps(sideMaterial, defaultMaterialMap);
|
||||
|
||||
// Use loader to load top and side textures
|
||||
const [topTexture, topNormalTexture, topRoughnessTexture, topMetalicTexture] =
|
||||
useLoader(TextureLoader, topTexturesList);
|
||||
|
||||
const [
|
||||
sideTexture,
|
||||
sideNormalTexture,
|
||||
sideRoughnessTexture,
|
||||
sideMetalicTexture,
|
||||
] = useLoader(TextureLoader, sideTexturesList);
|
||||
|
||||
// Early exit if materials are missing
|
||||
if (!materials[floor.topMaterial] || !materials[floor.sideMaterial])
|
||||
return null;
|
||||
|
||||
// Combine and pair textures with their corresponding material
|
||||
const textureMaterialMap = [
|
||||
{
|
||||
textures: [
|
||||
topTexture,
|
||||
topNormalTexture,
|
||||
topRoughnessTexture,
|
||||
topMetalicTexture,
|
||||
],
|
||||
materialKey: floor.topMaterial,
|
||||
},
|
||||
{
|
||||
textures: [
|
||||
sideTexture,
|
||||
sideNormalTexture,
|
||||
sideRoughnessTexture,
|
||||
sideMetalicTexture,
|
||||
],
|
||||
materialKey: floor.sideMaterial,
|
||||
},
|
||||
];
|
||||
|
||||
// Apply texture settings
|
||||
textureMaterialMap.forEach(({ textures, materialKey }) => {
|
||||
const tileScale = materials[materialKey]?.textureTileScale ?? [
|
||||
textureScale,
|
||||
textureScale,
|
||||
// Combine and pair textures with their corresponding material
|
||||
const textureMaterialMap = [
|
||||
{
|
||||
textures: [
|
||||
topTexture,
|
||||
topNormalTexture,
|
||||
topRoughnessTexture,
|
||||
topMetalicTexture,
|
||||
],
|
||||
materialKey: floor.topMaterial,
|
||||
},
|
||||
{
|
||||
textures: [
|
||||
sideTexture,
|
||||
sideNormalTexture,
|
||||
sideRoughnessTexture,
|
||||
sideMetalicTexture,
|
||||
],
|
||||
materialKey: floor.sideMaterial,
|
||||
},
|
||||
];
|
||||
|
||||
textures.forEach((tex, idx) => {
|
||||
if (!tex) return;
|
||||
tex.wrapS = tex.wrapT = RepeatWrapping;
|
||||
tex.repeat.set(tileScale[0], tileScale[1]);
|
||||
tex.anisotropy = 16;
|
||||
// First texture is always the color map (use SRGB), others should be linear
|
||||
tex.colorSpace = idx < 1 ? SRGBColorSpace : NoColorSpace;
|
||||
// Apply texture settings
|
||||
textureMaterialMap.forEach(({ textures, materialKey }) => {
|
||||
const tileScale = materials[materialKey]?.textureTileScale ?? [
|
||||
textureScale,
|
||||
textureScale,
|
||||
];
|
||||
|
||||
textures.forEach((tex, idx) => {
|
||||
if (!tex) return;
|
||||
tex.wrapS = tex.wrapT = RepeatWrapping;
|
||||
tex.repeat.set(tileScale[0], tileScale[1]);
|
||||
tex.anisotropy = 16;
|
||||
// First texture is always the color map (use SRGB), others should be linear
|
||||
tex.colorSpace = idx < 1 ? SRGBColorSpace : NoColorSpace;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (!shape) return null;
|
||||
if (!shape) return null;
|
||||
|
||||
return (
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
name={`Floor-${floor.floorUuid}`}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
position={[
|
||||
0,
|
||||
!floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2,
|
||||
0,
|
||||
]}
|
||||
userData={floor}
|
||||
onDoubleClick={(e) => {
|
||||
if (!togglView && activeModule === "builder") {
|
||||
if (e.object.userData.floorUuid) {
|
||||
e.stopPropagation();
|
||||
setSelectedFloor(e.object);
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
if (
|
||||
selectedFloor &&
|
||||
selectedFloor.userData.floorUuid === floor.floorUuid
|
||||
) {
|
||||
setSelectedFloor(null);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Extrude
|
||||
name={`Floor-${floor.floorUuid}`}
|
||||
args={[
|
||||
shape,
|
||||
{
|
||||
depth: !floor.isBeveled ? floor.floorDepth : floor.floorDepth - 0.1,
|
||||
bevelEnabled: floor.isBeveled,
|
||||
bevelSegments: floor.bevelStrength,
|
||||
bevelOffset: -0.1,
|
||||
bevelSize: 0.1,
|
||||
bevelThickness: 0.1,
|
||||
},
|
||||
]}
|
||||
userData={floor}
|
||||
>
|
||||
<meshPhysicalMaterial
|
||||
attach="material-0"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={topTexture}
|
||||
roughnessMap={topRoughnessTexture}
|
||||
metalnessMap={topMetalicTexture}
|
||||
normalMap={topNormalTexture}
|
||||
roughness={1.5}
|
||||
metalness={1.0}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
<meshStandardMaterial
|
||||
attach="material-1"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={sideTexture?.clone()}
|
||||
roughnessMap={sideRoughnessTexture?.clone()}
|
||||
metalnessMap={sideMetalicTexture?.clone()}
|
||||
normalMap={sideNormalTexture?.clone()}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</mesh>
|
||||
);
|
||||
return (
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
name={`Floor-${floor.floorUuid}`}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
position={[0, !floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2, 0,]}
|
||||
userData={floor}
|
||||
onDoubleClick={(e) => {
|
||||
if (!togglView && activeModule === "builder") {
|
||||
if (e.object.userData.floorUuid) {
|
||||
e.stopPropagation();
|
||||
setSelectedFloor(e.object);
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
if (selectedFloor && selectedFloor.userData.floorUuid === floor.floorUuid) {
|
||||
setSelectedFloor(null);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Extrude
|
||||
name={`Floor-${floor.floorUuid}`}
|
||||
args={[shape,
|
||||
{
|
||||
depth: !floor.isBeveled ? floor.floorDepth : floor.floorDepth - 0.1,
|
||||
bevelEnabled: floor.isBeveled,
|
||||
bevelSegments: floor.bevelStrength,
|
||||
bevelOffset: -0.1,
|
||||
bevelSize: 0.1,
|
||||
bevelThickness: 0.1,
|
||||
},
|
||||
]}
|
||||
userData={floor}
|
||||
>
|
||||
<meshPhysicalMaterial
|
||||
attach="material-0"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={topTexture}
|
||||
roughnessMap={topRoughnessTexture}
|
||||
metalnessMap={topMetalicTexture}
|
||||
normalMap={topNormalTexture}
|
||||
roughness={1.5}
|
||||
metalness={1.0}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
<meshStandardMaterial
|
||||
attach="material-1"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={sideTexture?.clone()}
|
||||
roughnessMap={sideRoughnessTexture?.clone()}
|
||||
metalnessMap={sideMetalicTexture?.clone()}
|
||||
normalMap={sideNormalTexture?.clone()}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
export default FloorInstance;
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useParams } from 'react-router-dom';
|
||||
import { getUserData } from '../../../functions/getUserData';
|
||||
import { handleCanvasCursors } from '../../../utils/mouseUtils/handleCanvasCursors';
|
||||
import { useSelectedPoints } from '../../../store/simulation/useSimulationStore';
|
||||
import { calculateAssetTransformationOnWall } from '../wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall';
|
||||
|
||||
// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
// import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi';
|
||||
@@ -18,6 +19,8 @@ import { useSelectedPoints } from '../../../store/simulation/useSimulationStore'
|
||||
// import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi';
|
||||
// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi';
|
||||
// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi';
|
||||
// import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
|
||||
// import { deleteWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi';
|
||||
|
||||
interface LineProps {
|
||||
points: [Point, Point];
|
||||
@@ -30,9 +33,10 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
const [isDeletable, setIsDeletable] = useState(false);
|
||||
const { socket } = useSocketStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
|
||||
const { wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore } = useSceneContext();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore();
|
||||
const { getWallAssetsByWall, updateWallAsset, removeWallAsset } = wallAssetStore();
|
||||
const { removeWallByPoints, setPosition: setWallPosition, getWallByPoints, getConnectedWallsByWallId } = wallStore();
|
||||
const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId, getFloorsByPoints } = floorStore();
|
||||
const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId, getZonesByPoints } = zoneStore();
|
||||
const { userId, organization } = getUserData();
|
||||
@@ -110,6 +114,33 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
const removedWall = removeWallByPoints(points);
|
||||
if (removedWall && projectId) {
|
||||
|
||||
const assetsOnWall = getWallAssetsByWall(removedWall.wallUuid);
|
||||
|
||||
assetsOnWall.forEach((asset) => {
|
||||
if (projectId && asset) {
|
||||
|
||||
removeWallAsset(asset.modelUuid);
|
||||
|
||||
// API
|
||||
|
||||
// deleteWallAssetApi(projectId, selectedVersion?.versionId || '', asset.modelUuid, asset.wallUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
modelUuid: asset.modelUuid,
|
||||
wallUuid: asset.wallUuid
|
||||
}
|
||||
|
||||
socket.emit('v1:wall-asset:delete', data);
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// API
|
||||
|
||||
// deleteWallApi(projectId, selectedVersion?.versionId || '', removedWall.wallUuid);
|
||||
@@ -361,13 +392,16 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
const offset = new THREE.Vector3().subVectors(midPoint, hit);
|
||||
setDragOffset(offset);
|
||||
|
||||
if (points[0].pointType === 'Wall') {
|
||||
const walls = getWallsByPointId(points[0].pointUuid);
|
||||
setInitialPositions({ walls });
|
||||
} else if (points[0].pointType === 'Floor') {
|
||||
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
|
||||
const wall = getWallByPoints(points);
|
||||
if (wall) {
|
||||
const walls = getConnectedWallsByWallId(wall.wallUuid, false);
|
||||
setInitialPositions({ walls });
|
||||
}
|
||||
} else if (points[0].pointType === 'Floor' && points[0].pointType === 'Floor') {
|
||||
const floors = getFloorsByPointId(points[0].pointUuid);
|
||||
setInitialPositions({ floors });
|
||||
} else if (points[0].pointType === 'Zone') {
|
||||
} else if (points[0].pointType === 'Zone' && points[0].pointType === 'Zone') {
|
||||
const zones = getZonesByPointId(points[0].pointUuid);
|
||||
setInitialPositions({ zones });
|
||||
}
|
||||
@@ -378,14 +412,48 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
if (toolMode !== 'move' || !dragOffset) return;
|
||||
handleCanvasCursors('default');
|
||||
setDragOffset(null);
|
||||
|
||||
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
|
||||
const updatedWalls1 = getWallsByPointId(points[0].pointUuid);
|
||||
const updatedWalls2 = getWallsByPointId(points[1].pointUuid);
|
||||
const updatedWalls = [...updatedWalls1, ...updatedWalls2].filter((wall, index, self) => index === self.findIndex((w) => w.wallUuid === wall.wallUuid));
|
||||
const wall = getWallByPoints(points);
|
||||
if (!wall) return;
|
||||
const updatedWalls = getConnectedWallsByWallId(wall.wallUuid, false);
|
||||
|
||||
if (updatedWalls.length > 0 && projectId) {
|
||||
updatedWalls.forEach(updatedWall => {
|
||||
|
||||
const initialWall = initialPositions.walls?.find(w => w.wallUuid === updatedWall.wallUuid);
|
||||
|
||||
if (initialWall) {
|
||||
const assetsOnWall = getWallAssetsByWall(updatedWall.wallUuid);
|
||||
|
||||
assetsOnWall.forEach(asset => {
|
||||
const { position, rotation } = calculateAssetTransformationOnWall(asset, initialWall, updatedWall);
|
||||
|
||||
const updatedWallAsset = updateWallAsset(asset.modelUuid, {
|
||||
position: [position[0], asset.position[1], position[2]],
|
||||
rotation: rotation
|
||||
});
|
||||
|
||||
if (projectId && updatedWallAsset) {
|
||||
// API
|
||||
|
||||
// upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallAssetData: updatedWallAsset,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:wall-asset:add', data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
@@ -19,9 +19,12 @@ import { useSceneContext } from '../../scene/sceneContext';
|
||||
// import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi';
|
||||
// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi';
|
||||
// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi';
|
||||
// import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
|
||||
// import { deleteWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi';
|
||||
|
||||
import { getUserData } from '../../../functions/getUserData';
|
||||
import { handleCanvasCursors } from '../../../utils/mouseUtils/handleCanvasCursors';
|
||||
import { calculateAssetTransformationOnWall } from '../wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall';
|
||||
|
||||
function Point({ point }: { readonly point: Point }) {
|
||||
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
||||
@@ -32,12 +35,13 @@ function Point({ point }: { readonly point: Point }) {
|
||||
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
||||
const { socket } = useSocketStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
|
||||
const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore } = useSceneContext();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
|
||||
const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore();
|
||||
const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore();
|
||||
const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore();
|
||||
const { getWallAssetsByWall, updateWallAsset, removeWallAsset } = wallAssetStore();
|
||||
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
|
||||
const { hoveredPoint, hoveredLine, setHoveredPoint } = useBuilderStore();
|
||||
const { selectedPoints } = useSelectedPoints();
|
||||
@@ -48,7 +52,7 @@ function Point({ point }: { readonly point: Point }) {
|
||||
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
|
||||
const colors = getColor(point);
|
||||
|
||||
const [initialPositions, setInitialPositions] = useState<{
|
||||
const [initialStates, setInitialStates] = useState<{
|
||||
aisles?: Aisle[],
|
||||
walls?: Wall[],
|
||||
floors?: Floor[],
|
||||
@@ -163,16 +167,16 @@ function Point({ point }: { readonly point: Point }) {
|
||||
|
||||
if (point.pointType === 'Aisle') {
|
||||
const aisles = getAislesByPointId(point.pointUuid);
|
||||
setInitialPositions({ aisles });
|
||||
setInitialStates({ aisles });
|
||||
} else if (point.pointType === 'Wall') {
|
||||
const walls = getWallsByPointId(point.pointUuid);
|
||||
setInitialPositions({ walls });
|
||||
setInitialStates({ walls });
|
||||
} else if (point.pointType === 'Floor') {
|
||||
const floors = getFloorsByPointId(point.pointUuid);
|
||||
setInitialPositions({ floors });
|
||||
setInitialStates({ floors });
|
||||
} else if (point.pointType === 'Zone') {
|
||||
const zones = getZonesByPointId(point.pointUuid);
|
||||
setInitialPositions({ zones });
|
||||
setInitialStates({ zones });
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -204,8 +208,8 @@ function Point({ point }: { readonly point: Point }) {
|
||||
})
|
||||
})
|
||||
|
||||
if (initialPositions.aisles && initialPositions.aisles.length > 0) {
|
||||
const updatedPoints = initialPositions.aisles.map((aisle) => ({
|
||||
if (initialStates.aisles && initialStates.aisles.length > 0) {
|
||||
const updatedPoints = initialStates.aisles.map((aisle) => ({
|
||||
type: "Aisle" as const,
|
||||
lineData: aisle,
|
||||
newData: updatedAisles.find(a => a.aisleUuid === aisle.aisleUuid),
|
||||
@@ -226,6 +230,39 @@ function Point({ point }: { readonly point: Point }) {
|
||||
if (updatedWalls && updatedWalls.length > 0 && projectId) {
|
||||
updatedWalls.forEach((updatedWall) => {
|
||||
|
||||
const initialWall = initialStates.walls?.find(w => w.wallUuid === updatedWall.wallUuid);
|
||||
|
||||
if (initialWall) {
|
||||
const assetsOnWall = getWallAssetsByWall(updatedWall.wallUuid);
|
||||
|
||||
assetsOnWall.forEach(asset => {
|
||||
const { position, rotation } = calculateAssetTransformationOnWall(asset, initialWall, updatedWall);
|
||||
|
||||
const updatedWallAsset = updateWallAsset(asset.modelUuid, {
|
||||
position: [position[0], asset.position[1], position[2]],
|
||||
rotation: rotation
|
||||
});
|
||||
|
||||
if (projectId && updatedWallAsset) {
|
||||
// API
|
||||
|
||||
// upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallAssetData: updatedWallAsset,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:wall-asset:add', data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
@@ -244,8 +281,8 @@ function Point({ point }: { readonly point: Point }) {
|
||||
});
|
||||
}
|
||||
|
||||
if (initialPositions.walls && initialPositions.walls.length > 0) {
|
||||
const updatedPoints = initialPositions.walls.map((wall) => ({
|
||||
if (initialStates.walls && initialStates.walls.length > 0) {
|
||||
const updatedPoints = initialStates.walls.map((wall) => ({
|
||||
type: "Wall" as const,
|
||||
lineData: wall,
|
||||
newData: updatedWalls.find(w => w.wallUuid === wall.wallUuid),
|
||||
@@ -283,8 +320,8 @@ function Point({ point }: { readonly point: Point }) {
|
||||
});
|
||||
}
|
||||
|
||||
if (initialPositions.floors && initialPositions.floors.length > 0) {
|
||||
const updatedPoints = initialPositions.floors.map((floor) => ({
|
||||
if (initialStates.floors && initialStates.floors.length > 0) {
|
||||
const updatedPoints = initialStates.floors.map((floor) => ({
|
||||
type: "Floor" as const,
|
||||
lineData: floor,
|
||||
newData: updatedFloors.find(f => f.floorUuid === floor.floorUuid),
|
||||
@@ -322,8 +359,8 @@ function Point({ point }: { readonly point: Point }) {
|
||||
});
|
||||
}
|
||||
|
||||
if (initialPositions.zones && initialPositions.zones.length > 0) {
|
||||
const updatedPoints = initialPositions.zones.map((zone) => ({
|
||||
if (initialStates.zones && initialStates.zones.length > 0) {
|
||||
const updatedPoints = initialStates.zones.map((zone) => ({
|
||||
type: "Zone" as const,
|
||||
lineData: zone,
|
||||
newData: updatedZones.find(z => z.zoneUuid === zone.zoneUuid),
|
||||
@@ -340,7 +377,7 @@ function Point({ point }: { readonly point: Point }) {
|
||||
}
|
||||
}
|
||||
|
||||
setInitialPositions({});
|
||||
setInitialStates({});
|
||||
}
|
||||
|
||||
const handlePointClick = (point: Point) => {
|
||||
@@ -393,6 +430,33 @@ function Point({ point }: { readonly point: Point }) {
|
||||
if (removedWalls.length > 0) {
|
||||
setHoveredPoint(null);
|
||||
removedWalls.forEach(wall => {
|
||||
const assetsOnWall = getWallAssetsByWall(wall.wallUuid);
|
||||
|
||||
assetsOnWall.forEach((asset) => {
|
||||
if (projectId && asset) {
|
||||
|
||||
removeWallAsset(asset.modelUuid);
|
||||
|
||||
// API
|
||||
|
||||
// deleteWallAssetApi(projectId, selectedVersion?.versionId || '', asset.modelUuid, asset.wallUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
modelUuid: asset.modelUuid,
|
||||
wallUuid: asset.wallUuid
|
||||
}
|
||||
|
||||
socket.emit('v1:wall-asset:delete', data);
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
@@ -10,16 +10,16 @@ import { useToggleView, useWallVisibility } from '../../../../../store/builder/s
|
||||
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
|
||||
import * as Constants from '../../../../../types/world/worldConstants';
|
||||
|
||||
// import DecalInstance from '../../../Decal/decalInstance';
|
||||
import DecalInstance from '../../../Decal/decalInstance';
|
||||
|
||||
import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
|
||||
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
|
||||
|
||||
function Wall({ wall }: { readonly wall: Wall }) {
|
||||
const { wallStore, wallAssetStore } = useSceneContext();
|
||||
const { walls, addDecal } = wallStore();
|
||||
const { wallAssets, getAssetsByWall } = wallAssetStore();
|
||||
const assets = getAssetsByWall(wall.wallUuid);
|
||||
const { walls } = wallStore();
|
||||
const { wallAssets, getWallAssetsByWall, setVisibility } = wallAssetStore();
|
||||
const assets = getWallAssetsByWall(wall.wallUuid);
|
||||
const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore();
|
||||
const { togglView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
@@ -28,6 +28,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
const { getWallType, isWallFlipped } = useWallClassification(walls);
|
||||
const [visible, setVisible] = useState(true);
|
||||
const meshRef = useRef<any>();
|
||||
const prevVisibleRef = useRef<boolean | null>(null);
|
||||
const wallType = getWallType(wall);
|
||||
|
||||
const wallFlipped = isWallFlipped(wall);
|
||||
@@ -80,7 +81,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
map: wall.outsideMaterial === 'Default Material' ? defaultWallTexture : material1WallTexture,
|
||||
}),
|
||||
];
|
||||
}, [defaultWallTexture, material1WallTexture, wall]);
|
||||
}, [defaultWallTexture, material1WallTexture, wall, visible]);
|
||||
|
||||
const geometry = useMemo(() => new THREE.BoxGeometry(wallLength, wall.wallHeight, wall.wallThickness), [wallLength, wall.wallHeight, wall.wallThickness]);
|
||||
|
||||
@@ -89,15 +90,23 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
const v = new THREE.Vector3();
|
||||
const u = new THREE.Vector3();
|
||||
|
||||
if (!wallVisibility && wallType.type === 'room') {
|
||||
let nextVisible = true;
|
||||
|
||||
if (!wallVisibility && wallType.type === "room") {
|
||||
meshRef.current.getWorldDirection(v);
|
||||
camera.getWorldDirection(u);
|
||||
if (!u || !v) return;
|
||||
setVisible((2 * v.dot(u)) <= 0.1);
|
||||
} else {
|
||||
setVisible(true);
|
||||
nextVisible = (2 * v.dot(u)) <= 0.1;
|
||||
}
|
||||
})
|
||||
|
||||
if (prevVisibleRef.current !== nextVisible) {
|
||||
prevVisibleRef.current = nextVisible;
|
||||
setVisible(nextVisible);
|
||||
assets.forEach((asset) => {
|
||||
setVisibility(asset.modelUuid, nextVisible);
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh
|
||||
@@ -150,19 +159,6 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
e.stopPropagation();
|
||||
setSelectedWall(e.object);
|
||||
setSelectedDecal(null);
|
||||
|
||||
if (wall.decals.length > 0) return;
|
||||
const decal: Decal = {
|
||||
decalUuid: THREE.MathUtils.generateUUID(),
|
||||
decalName: 'Decal',
|
||||
decalId: 'Default Decal',
|
||||
decalPosition: [0, 0, wall.wallThickness / 2 + 0.001],
|
||||
decalRotation: 0,
|
||||
decalScale: 1,
|
||||
decalType: { type: 'Wall', wallUuid: wall.wallUuid }
|
||||
}
|
||||
addDecal(wall.wallUuid, decal);
|
||||
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -174,9 +170,9 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
>
|
||||
<MeshDiscardMaterial />
|
||||
|
||||
{/* {wall.decals.map((decal) => (
|
||||
{wall.decals.map((decal) => (
|
||||
<DecalInstance zPosition={wall.wallThickness / 2 + 0.001} visible={visible} key={decal.decalUuid} decal={decal} />
|
||||
))} */}
|
||||
))}
|
||||
</mesh>
|
||||
</mesh>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import * as THREE from 'three';
|
||||
|
||||
const calculateAssetTransformationOnWall = (
|
||||
asset: WallAsset,
|
||||
initialWall: Wall,
|
||||
newWall: Wall
|
||||
): { position: [number, number, number], rotation: [number, number, number] } => {
|
||||
const [initialStartPoint, initialEndPoint] = initialWall.points;
|
||||
const [newStartPoint, newEndPoint] = newWall.points;
|
||||
|
||||
const initialWallVector = new THREE.Vector3(initialEndPoint.position[0] - initialStartPoint.position[0], 0, initialEndPoint.position[2] - initialStartPoint.position[2]);
|
||||
|
||||
const assetVector = new THREE.Vector3(asset.position[0] - initialStartPoint.position[0], 0, asset.position[2] - initialStartPoint.position[2]);
|
||||
|
||||
const initialWallLength = initialWallVector.length();
|
||||
const initialWallNormalized = initialWallVector.normalize();
|
||||
const dotProduct = assetVector.dot(initialWallNormalized);
|
||||
|
||||
const projection = initialWallNormalized.clone().multiplyScalar(dotProduct);
|
||||
const perpendicular = new THREE.Vector3().subVectors(assetVector, projection);
|
||||
const distanceFromWall = perpendicular.length();
|
||||
|
||||
const crossProduct = new THREE.Vector3().crossVectors(initialWallNormalized, perpendicular).y;
|
||||
const signedDistance = distanceFromWall * (crossProduct >= 0 ? 1 : -1);
|
||||
|
||||
const percentage = Math.max(0, Math.min(1, dotProduct / initialWallLength));
|
||||
|
||||
const newWallVector = new THREE.Vector3(newEndPoint.position[0] - newStartPoint.position[0], 0, newEndPoint.position[2] - newStartPoint.position[2]);
|
||||
|
||||
const x = newStartPoint.position[0] + (newEndPoint.position[0] - newStartPoint.position[0]) * percentage;
|
||||
const z = newStartPoint.position[2] + (newEndPoint.position[2] - newStartPoint.position[2]) * percentage;
|
||||
|
||||
const newWallNormal = new THREE.Vector3(-newWallVector.z, 0, newWallVector.x).normalize();
|
||||
|
||||
const offsetX = newWallNormal.x * signedDistance;
|
||||
const offsetZ = newWallNormal.z * signedDistance;
|
||||
|
||||
const wallAngle = Math.atan2(newWallVector.z, newWallVector.x);
|
||||
|
||||
return {
|
||||
position: [x + offsetX, asset.position[1], z + offsetZ],
|
||||
rotation: [0, -wallAngle, 0]
|
||||
};
|
||||
};
|
||||
|
||||
export { calculateAssetTransformationOnWall };
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useSceneContext } from '../../../scene/sceneContext'
|
||||
import { useToggleView } from '../../../../store/builder/store';
|
||||
import WallAssetInstance from './Instances/wallAssetInstance';
|
||||
import WallAssetInstance from './Instance/wallAssetInstance';
|
||||
|
||||
function WallAssetInstances() {
|
||||
const { wallAssetStore } = useSceneContext();
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useVersionContext } from '../version/versionContext';
|
||||
import { getUserData } from '../../../functions/getUserData';
|
||||
import closestPointOnLineSegment from '../line/helpers/getClosestPointOnLineSegment';
|
||||
|
||||
import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
|
||||
// import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
|
||||
|
||||
function WallAssetCreator() {
|
||||
const { socket } = useSocketStore();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
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';
|
||||
@@ -12,6 +12,8 @@ function ContextControls() {
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { setContextAction } = useContextActionStore();
|
||||
const { setIsRenameMode } = useRenameModeStore();
|
||||
const rightDrag = useRef(false);
|
||||
const isRightMouseDown = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 1) {
|
||||
@@ -68,8 +70,28 @@ function ContextControls() {
|
||||
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);
|
||||
@@ -85,6 +107,9 @@ function ContextControls() {
|
||||
};
|
||||
|
||||
if (selectedAssets.length > 0) {
|
||||
canvasElement.addEventListener('pointerdown', onPointerDown);
|
||||
canvasElement.addEventListener('pointermove', onPointerMove);
|
||||
canvasElement.addEventListener('pointerup', onPointerUp);
|
||||
canvasElement.addEventListener('contextmenu', handleContextClick)
|
||||
} else {
|
||||
setCanRender(false);
|
||||
@@ -95,6 +120,9 @@ function ContextControls() {
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener('pointerdown', onPointerDown);
|
||||
canvasElement.removeEventListener('pointermove', onPointerMove);
|
||||
canvasElement.removeEventListener('pointerup', onPointerUp);
|
||||
canvasElement.removeEventListener('contextmenu', handleContextClick);
|
||||
};
|
||||
}, [gl, selectedAssets]);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useSceneContext } from "../../../sceneContext";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import { useSelectedPoints } from "../../../../../store/simulation/useSimulationStore";
|
||||
import useModuleStore from "../../../../../store/useModuleStore";
|
||||
import { calculateAssetTransformationOnWall } from "../../../../builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall";
|
||||
|
||||
// import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi";
|
||||
// import { upsertWallApi } from "../../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
@@ -19,7 +20,7 @@ function MoveControls2D({
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
pastedObjects,
|
||||
setpastedObjects,
|
||||
setPastedObjects,
|
||||
duplicatedObjects,
|
||||
setDuplicatedObjects,
|
||||
rotatedObjects,
|
||||
@@ -36,7 +37,8 @@ function MoveControls2D({
|
||||
const { projectId } = useParams();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
|
||||
const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore } = useSceneContext();
|
||||
const { getWallAssetsByWall, updateWallAsset } = wallAssetStore();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const { setPosition: setAislePosition, getAislesByPointId, getAisleById } = aisleStore();
|
||||
const { setPosition: setWallPosition, getWallsByPointId, getWallById } = wallStore();
|
||||
@@ -45,6 +47,12 @@ function MoveControls2D({
|
||||
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
||||
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
|
||||
const [initialStates, setInitialStates] = useState<Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }>>({});
|
||||
const [initial, setInitial] = useState<{
|
||||
aisles?: Aisle[],
|
||||
walls?: Wall[],
|
||||
floors?: Floor[],
|
||||
zones?: Zone[]
|
||||
}>({});
|
||||
const [isMoving, setIsMoving] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -66,7 +74,7 @@ function MoveControls2D({
|
||||
const onPointerUp = (event: PointerEvent) => {
|
||||
if (!isMoving && movedObjects.length > 0 && event.button === 0) {
|
||||
event.preventDefault();
|
||||
placeMovedAssets();
|
||||
placeMovedPoints();
|
||||
}
|
||||
if (!isMoving && movedObjects.length > 0 && event.button === 2) {
|
||||
event.preventDefault();
|
||||
@@ -110,7 +118,7 @@ function MoveControls2D({
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
}, [camera, controls, scene, toggleView, selectedPoints, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects]);
|
||||
}, [camera, controls, scene, toggleView, selectedPoints, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, initial]);
|
||||
|
||||
useEffect(() => {
|
||||
if (toolMode !== 'move' || !toggleView) {
|
||||
@@ -169,13 +177,35 @@ function MoveControls2D({
|
||||
if (selectedPoints.length === 0) return;
|
||||
|
||||
const states: Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }> = {};
|
||||
const initials: {
|
||||
aisles?: Aisle[] | undefined;
|
||||
walls?: Wall[];
|
||||
floors?: Floor[];
|
||||
zones?: Zone[];
|
||||
} = {}
|
||||
|
||||
selectedPoints.forEach((point: THREE.Object3D) => {
|
||||
states[point.uuid] = {
|
||||
position: new THREE.Vector3().copy(point.position),
|
||||
rotation: point.rotation ? new THREE.Euler().copy(point.rotation) : undefined
|
||||
};
|
||||
|
||||
if (point.userData.pointType === "Aisle") {
|
||||
const aisles = getAislesByPointId(point.userData.pointUuid);
|
||||
initials.aisles = [...(initials.aisles ?? []), ...aisles,].filter((aisle, index, self) => index === self.findIndex((a) => a.aisleUuid === aisle.aisleUuid));
|
||||
} else if (point.userData.pointType === "Wall") {
|
||||
const walls = getWallsByPointId(point.userData.pointUuid);
|
||||
initials.walls = [...(initials.walls ?? []), ...walls,].filter((wall, index, self) => index === self.findIndex((w) => w.wallUuid === wall.wallUuid));
|
||||
} else if (point.userData.pointType === "Floor") {
|
||||
const floors = getFloorsByPointId(point.userData.pointUuid);
|
||||
initials.floors = [...(initials.floors ?? []), ...floors,].filter((floor, index, self) => index === self.findIndex((f) => f.floorUuid === floor.floorUuid));
|
||||
} else if (point.userData.pointType === "Zone") {
|
||||
const zones = getZonesByPointId(point.userData.pointUuid);
|
||||
initials.zones = [...(initials.zones ?? []), ...zones,].filter((zone, index, self) => index === self.findIndex((z) => z.zoneUuid === zone.zoneUuid));
|
||||
}
|
||||
});
|
||||
|
||||
setInitial(initials)
|
||||
setInitialStates(states);
|
||||
|
||||
const positions: Record<string, THREE.Vector3> = {};
|
||||
@@ -221,7 +251,7 @@ function MoveControls2D({
|
||||
}, 0)
|
||||
};
|
||||
|
||||
const placeMovedAssets = () => {
|
||||
const placeMovedPoints = () => {
|
||||
if (movedObjects.length === 0) return;
|
||||
|
||||
const undoPoints: UndoRedo2DDataTypeSchema[] = [];
|
||||
@@ -229,6 +259,7 @@ function MoveControls2D({
|
||||
const processedWalls: UndoRedo2DDataTypeSchema[] = [];
|
||||
const processedFloors: UndoRedo2DDataTypeSchema[] = [];
|
||||
const processedZones: UndoRedo2DDataTypeSchema[] = [];
|
||||
const wallAssetUpdates: WallAsset[] = [];
|
||||
|
||||
movedObjects.forEach((movedObject: THREE.Object3D) => {
|
||||
if (movedObject.userData.pointUuid) {
|
||||
@@ -262,11 +293,11 @@ function MoveControls2D({
|
||||
lineData: {
|
||||
...updatedAisle,
|
||||
points: [
|
||||
updatedAisle.points[0].pointUuid === point.pointUuid
|
||||
? { ...updatedAisle.points[0], position: [old.position.x, old.position.y, old.position.z] }
|
||||
initialStates[updatedAisle.points[0].pointUuid] ?
|
||||
{ ...updatedAisle.points[0], position: initialStates[updatedAisle.points[0].pointUuid].position }
|
||||
: updatedAisle.points[0],
|
||||
updatedAisle.points[1].pointUuid === point.pointUuid
|
||||
? { ...updatedAisle.points[1], position: [old.position.x, old.position.y, old.position.z] }
|
||||
initialStates[updatedAisle.points[1].pointUuid] ?
|
||||
{ ...updatedAisle.points[1], position: initialStates[updatedAisle.points[1].pointUuid].position }
|
||||
: updatedAisle.points[1]
|
||||
] as [Point, Point],
|
||||
},
|
||||
@@ -281,6 +312,24 @@ function MoveControls2D({
|
||||
if (updatedWalls?.length && projectId) {
|
||||
updatedWalls.forEach(updatedWall => {
|
||||
|
||||
const initialWall = initial.walls?.find(w => w.wallUuid === updatedWall.wallUuid);
|
||||
|
||||
if (initialWall) {
|
||||
const assetsOnWall = getWallAssetsByWall(updatedWall.wallUuid);
|
||||
|
||||
assetsOnWall.forEach((asset) => {
|
||||
const { position, rotation } = calculateAssetTransformationOnWall(asset, initialWall, updatedWall);
|
||||
|
||||
const updatedWallAsset: WallAsset = {
|
||||
...asset,
|
||||
position: [position[0], asset.position[1], position[2]],
|
||||
rotation,
|
||||
};
|
||||
|
||||
wallAssetUpdates.push(updatedWallAsset);
|
||||
});
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
@@ -302,11 +351,11 @@ function MoveControls2D({
|
||||
lineData: {
|
||||
...updatedWall,
|
||||
points: [
|
||||
updatedWall.points[0].pointUuid === point.pointUuid
|
||||
? { ...updatedWall.points[0], position: [old.position.x, old.position.y, old.position.z] }
|
||||
initialStates[updatedWall.points[0].pointUuid] ?
|
||||
{ ...updatedWall.points[0], position: initialStates[updatedWall.points[0].pointUuid].position }
|
||||
: updatedWall.points[0],
|
||||
updatedWall.points[1].pointUuid === point.pointUuid
|
||||
? { ...updatedWall.points[1], position: [old.position.x, old.position.y, old.position.z] }
|
||||
initialStates[updatedWall.points[1].pointUuid] ?
|
||||
{ ...updatedWall.points[1], position: initialStates[updatedWall.points[1].pointUuid].position }
|
||||
: updatedWall.points[1]
|
||||
] as [Point, Point],
|
||||
},
|
||||
@@ -399,6 +448,36 @@ function MoveControls2D({
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
if (wallAssetUpdates.length > 0) {
|
||||
wallAssetUpdates.filter((wallAssets, index, self) => index === self.findIndex((w) => w.modelUuid === wallAssets.modelUuid));
|
||||
wallAssetUpdates.forEach((updatedWallAsset) => {
|
||||
if (projectId && updatedWallAsset) {
|
||||
|
||||
updateWallAsset(updatedWallAsset.modelUuid, {
|
||||
position: updatedWallAsset.position,
|
||||
rotation: updatedWallAsset.rotation
|
||||
});
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallAssetData: updatedWallAsset,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:wall-asset:add', data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (processedWalls.length > 0) {
|
||||
const wallMap = new Map<string, UndoRedo2DDataTypeSchema[]>();
|
||||
|
||||
@@ -525,7 +604,7 @@ function MoveControls2D({
|
||||
};
|
||||
|
||||
const clearSelection = () => {
|
||||
setpastedObjects([]);
|
||||
setPastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setMovedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
|
||||
@@ -28,7 +28,7 @@ const SelectionControls2D: React.FC = () => {
|
||||
const [movedObjects, setMovedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [rotatedObjects, setRotatedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [copiedObjects, setCopiedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [pastedObjects, setpastedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [pastedObjects, setPastedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [duplicatedObjects, setDuplicatedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
@@ -38,7 +38,8 @@ const SelectionControls2D: React.FC = () => {
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { hoveredLine, hoveredPoint } = useBuilderStore();
|
||||
const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
|
||||
const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore } = useSceneContext();
|
||||
const { getWallAssetsByWall, removeWallAsset } = wallAssetStore();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const { removePoint: removeAislePoint } = aisleStore();
|
||||
const { removePoint: removeWallPoint } = wallStore();
|
||||
@@ -216,7 +217,7 @@ const SelectionControls2D: React.FC = () => {
|
||||
}, [selectionBox, pointer, controls, selectedPoints, setSelectedPoints]);
|
||||
|
||||
const clearSelection = () => {
|
||||
setpastedObjects([]);
|
||||
setPastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
clearSelectedPoints();
|
||||
};
|
||||
@@ -271,6 +272,34 @@ const SelectionControls2D: React.FC = () => {
|
||||
const removedWalls = removeWallPoint(point.pointUuid);
|
||||
if (removedWalls.length > 0) {
|
||||
removedWalls.forEach(wall => {
|
||||
|
||||
const assetsOnWall = getWallAssetsByWall(wall.wallUuid);
|
||||
|
||||
assetsOnWall.forEach((asset) => {
|
||||
if (projectId && asset) {
|
||||
|
||||
removeWallAsset(asset.modelUuid);
|
||||
|
||||
// API
|
||||
|
||||
// deleteWallAssetApi(projectId, selectedVersion?.versionId || '', asset.modelUuid, asset.wallUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
modelUuid: asset.modelUuid,
|
||||
wallUuid: asset.wallUuid
|
||||
}
|
||||
|
||||
socket.emit('v1:wall-asset:delete', data);
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
@@ -594,7 +623,7 @@ const SelectionControls2D: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
<MoveControls2D movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} />
|
||||
<MoveControls2D movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setPastedObjects={setPastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} />
|
||||
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -12,17 +12,7 @@ import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
|
||||
// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||
|
||||
const CopyPasteControls3D = ({
|
||||
copiedObjects,
|
||||
setCopiedObjects,
|
||||
pastedObjects,
|
||||
setpastedObjects,
|
||||
setDuplicatedObjects,
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
}: any) => {
|
||||
const CopyPasteControls3D = () => {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
@@ -32,7 +22,7 @@ const CopyPasteControls3D = ({
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { addEvent } = eventStore();
|
||||
const { projectId } = useParams();
|
||||
const { assets, addAsset, updateAsset, removeAsset, getAssetById } = assetStore();
|
||||
const { assets, addAsset, updateAsset, removeAsset, getAssetById, copiedObjects, setCopiedObjects, pastedObjects, setPastedObjects, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects } = assetStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { userId, organization } = getUserData();
|
||||
@@ -177,7 +167,7 @@ const CopyPasteControls3D = ({
|
||||
return clone;
|
||||
});
|
||||
|
||||
setpastedObjects(newPastedObjects);
|
||||
setPastedObjects(newPastedObjects);
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
@@ -431,6 +421,11 @@ const CopyPasteControls3D = ({
|
||||
actionType: "worker",
|
||||
loadCapacity: 1,
|
||||
assemblyCount: 1,
|
||||
assemblyCondition: {
|
||||
conditionType: 'material',
|
||||
materialType: "Default material"
|
||||
},
|
||||
manufactureCount: 1,
|
||||
loadCount: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
@@ -593,7 +588,7 @@ const CopyPasteControls3D = ({
|
||||
|
||||
const clearSelection = () => {
|
||||
setMovedObjects([]);
|
||||
setpastedObjects([]);
|
||||
setPastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
|
||||
@@ -13,15 +13,7 @@ import { handleAssetPositionSnap } from "./functions/handleAssetPositionSnap";
|
||||
|
||||
// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||
|
||||
const DuplicationControls3D = ({
|
||||
duplicatedObjects,
|
||||
setDuplicatedObjects,
|
||||
setpastedObjects,
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
}: any) => {
|
||||
const DuplicationControls3D = () => {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
@@ -31,7 +23,7 @@ const DuplicationControls3D = ({
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { addEvent } = eventStore();
|
||||
const { projectId } = useParams();
|
||||
const { assets, addAsset, updateAsset, removeAsset, getAssetById } = assetStore();
|
||||
const { assets, addAsset, updateAsset, removeAsset, getAssetById, duplicatedObjects, setDuplicatedObjects, setPastedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects } = assetStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { userId, organization } = getUserData();
|
||||
@@ -498,6 +490,11 @@ const DuplicationControls3D = ({
|
||||
actionType: "worker",
|
||||
loadCapacity: 1,
|
||||
assemblyCount: 1,
|
||||
assemblyCondition: {
|
||||
conditionType: 'material',
|
||||
materialType: "Default material"
|
||||
},
|
||||
manufactureCount: 1,
|
||||
loadCount: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
@@ -660,7 +657,7 @@ const DuplicationControls3D = ({
|
||||
|
||||
const clearSelection = () => {
|
||||
setMovedObjects([]);
|
||||
setpastedObjects([]);
|
||||
setPastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
|
||||
@@ -15,17 +15,7 @@ import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
|
||||
function MoveControls3D({
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
pastedObjects,
|
||||
setpastedObjects,
|
||||
duplicatedObjects,
|
||||
setDuplicatedObjects,
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
boundingBoxRef,
|
||||
}: any) {
|
||||
function MoveControls3D({ boundingBoxRef }: any) {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
|
||||
@@ -38,7 +28,7 @@ function MoveControls3D({
|
||||
const { projectId } = useParams();
|
||||
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { updateAsset, getAssetById } = assetStore();
|
||||
const { updateAsset, getAssetById, movedObjects, setMovedObjects, pastedObjects, setPastedObjects, duplicatedObjects, setDuplicatedObjects, rotatedObjects, setRotatedObjects } = assetStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
|
||||
@@ -186,7 +176,7 @@ function MoveControls3D({
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent]);
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]);
|
||||
|
||||
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
|
||||
const pointPosition = new THREE.Vector3().copy(point.position);
|
||||
@@ -220,7 +210,7 @@ function MoveControls3D({
|
||||
}
|
||||
});
|
||||
setAxisConstraint(null);
|
||||
}, 100)
|
||||
}, 50)
|
||||
}, [movedObjects, initialStates, updateAsset]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -462,7 +452,7 @@ function MoveControls3D({
|
||||
};
|
||||
|
||||
const clearSelection = () => {
|
||||
setpastedObjects([]);
|
||||
setPastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setMovedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
|
||||
@@ -14,16 +14,7 @@ import { handleAssetRotationSnap } from "./functions/handleAssetRotationSnap";
|
||||
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
|
||||
function RotateControls3D({
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
pastedObjects,
|
||||
setpastedObjects,
|
||||
duplicatedObjects,
|
||||
setDuplicatedObjects
|
||||
}: any) {
|
||||
function RotateControls3D() {
|
||||
const { camera, gl, scene, pointer, raycaster } = useThree();
|
||||
|
||||
const { toggleView } = useToggleView();
|
||||
@@ -35,7 +26,7 @@ function RotateControls3D({
|
||||
const { projectId } = useParams();
|
||||
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { updateAsset } = assetStore();
|
||||
const { updateAsset, rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, pastedObjects, setPastedObjects, duplicatedObjects, setDuplicatedObjects } = assetStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
|
||||
@@ -161,7 +152,7 @@ function RotateControls3D({
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
}, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent]);
|
||||
}, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations]);
|
||||
|
||||
const resetToInitialRotations = useCallback(() => {
|
||||
setTimeout(() => {
|
||||
@@ -185,7 +176,7 @@ function RotateControls3D({
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 100)
|
||||
}, 50)
|
||||
}, [rotatedObjects, initialRotations, initialPositions, updateAsset]);
|
||||
|
||||
useFrame(() => {
|
||||
@@ -397,7 +388,7 @@ function RotateControls3D({
|
||||
}, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId]);
|
||||
|
||||
const clearSelection = () => {
|
||||
setpastedObjects([]);
|
||||
setPastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setMovedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import * as THREE from "three";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { SelectionHelper } from "../selectionHelper";
|
||||
@@ -23,18 +23,13 @@ const SelectionControls3D: React.FC = () => {
|
||||
const { camera, controls, gl, scene, raycaster, pointer } = useThree();
|
||||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const [movedObjects, setMovedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [rotatedObjects, setRotatedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [copiedObjects, setCopiedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [pastedObjects, setpastedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const [duplicatedObjects, setDuplicatedObjects] = useState<THREE.Object3D[]>([]);
|
||||
const boundingBoxRef = useRef<THREE.Mesh>();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const { contextAction, setContextAction } = useContextActionStore()
|
||||
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { removeAsset, getAssetById } = assetStore();
|
||||
const { removeAsset, getAssetById, movedObjects, rotatedObjects, copiedObjects, pastedObjects, duplicatedObjects, setPastedObjects, setDuplicatedObjects } = assetStore();
|
||||
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
@@ -267,13 +262,13 @@ const SelectionControls3D: React.FC = () => {
|
||||
}, [selectionBox, pointer, controls, selectedAssets, setSelectedAssets]);
|
||||
|
||||
const clearSelection = () => {
|
||||
setpastedObjects([]);
|
||||
setPastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
};
|
||||
|
||||
const deleteSelection = () => {
|
||||
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
|
||||
if (selectedAssets.length > 0 && duplicatedObjects.length === 0 && pastedObjects.length === 0) {
|
||||
|
||||
const undoActions: UndoRedo3DAction[] = [];
|
||||
const assetsToDelete: AssetData[] = [];
|
||||
@@ -366,21 +361,22 @@ const SelectionControls3D: React.FC = () => {
|
||||
selectedUUIDs.forEach((uuid: string) => {
|
||||
removeAsset(uuid);
|
||||
});
|
||||
|
||||
echo.success("Selected models removed!");
|
||||
clearSelection();
|
||||
}
|
||||
echo.success("Selected models removed!");
|
||||
clearSelection();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<MoveControls3D movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||
<MoveControls3D boundingBoxRef={boundingBoxRef} />
|
||||
|
||||
<RotateControls3D rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} />
|
||||
<RotateControls3D />
|
||||
|
||||
<DuplicationControls3D duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||
<DuplicationControls3D />
|
||||
|
||||
<CopyPasteControls3D copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||
<CopyPasteControls3D />
|
||||
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@ import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import { useLoadingProgress, useSocketStore } from "../../store/builder/store";
|
||||
import { Color, SRGBColorSpace } from "three";
|
||||
import { compressImage } from "../../utils/compressImage";
|
||||
|
||||
export default function Scene({ layout }: { readonly layout: "Main Layout" | "Comparison Layout"; }) {
|
||||
const map = useMemo(() => [
|
||||
@@ -31,23 +32,24 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co
|
||||
const { loadingProgress } = useLoadingProgress();
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId && loadingProgress > 1) return;
|
||||
if (!projectId || loadingProgress !== 0) return;
|
||||
getAllProjects(userId, organization).then((projects) => {
|
||||
if (!projects || !projects.Projects) return;
|
||||
let project = projects.Projects.find((val: any) => val.projectUuid === projectId || val._id === projectId);
|
||||
const canvas = document.getElementById("sceneCanvas")?.getElementsByTagName("canvas")[0];
|
||||
if (!canvas) return;
|
||||
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
|
||||
const updateProjects = {
|
||||
projectId: project?._id,
|
||||
organization,
|
||||
userId,
|
||||
projectName: project?.projectName,
|
||||
thumbnail: screenshotDataUrl,
|
||||
};
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:update", updateProjects);
|
||||
}
|
||||
compressImage((canvas as HTMLCanvasElement)?.toDataURL("image/png")).then((screenshotDataUrl) => {
|
||||
const updateProjects = {
|
||||
projectId: project?._id,
|
||||
organization,
|
||||
userId,
|
||||
projectName: project?.projectName,
|
||||
thumbnail: screenshotDataUrl,
|
||||
};
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:update", updateProjects);
|
||||
}
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
@@ -64,7 +66,7 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co
|
||||
onContextMenu={(e) => { e.preventDefault(); }}
|
||||
performance={{ min: 0.9, max: 1.0 }}
|
||||
onCreated={(e) => { e.scene.background = layout === "Main Layout" ? null : new Color(0x19191d); }}
|
||||
gl={{ outputColorSpace: SRGBColorSpace, powerPreference: "high-performance", antialias: true }}
|
||||
gl={{ outputColorSpace: SRGBColorSpace, powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }}
|
||||
>
|
||||
<Setup />
|
||||
<Collaboration />
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback } from "react";
|
||||
import { useSceneContext } from "../../../../scene/sceneContext";
|
||||
import { useProductContext } from "../../../products/productContext";
|
||||
|
||||
export function useAssemblyHandler() {
|
||||
export function useManufacturerHandler() {
|
||||
const { materialStore, humanStore, productStore } = useSceneContext();
|
||||
const { getMaterialById } = materialStore();
|
||||
const { getModelUuidByActionUuid } = productStore();
|
||||
@@ -10,12 +10,12 @@ export function useAssemblyHandler() {
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { incrementHumanLoad, addCurrentMaterial, addCurrentAction } = humanStore();
|
||||
|
||||
const assemblyLogStatus = (materialUuid: string, status: string) => {
|
||||
const manufactureLogStatus = (materialUuid: string, status: string) => {
|
||||
echo.info(`${materialUuid}, ${status}`);
|
||||
}
|
||||
|
||||
const handleAssembly = useCallback((action: HumanAction, materialId?: string) => {
|
||||
if (!action || action.actionType !== 'assembly' || !materialId) return;
|
||||
const handleManufacturer = useCallback((action: HumanAction, materialId?: string) => {
|
||||
if (!action || action.actionType !== 'manufacturer' || !materialId) return;
|
||||
|
||||
const material = getMaterialById(materialId);
|
||||
if (!material) return;
|
||||
@@ -27,11 +27,11 @@ export function useAssemblyHandler() {
|
||||
addCurrentAction(modelUuid, action.actionUuid);
|
||||
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
|
||||
|
||||
assemblyLogStatus(material.materialName, `performing assembly action`);
|
||||
manufactureLogStatus(material.materialName, `performing manufacturer action`);
|
||||
|
||||
}, [getMaterialById]);
|
||||
|
||||
return {
|
||||
handleAssembly,
|
||||
handleManufacturer,
|
||||
};
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { useWorkerHandler } from './actionHandler/useWorkerHandler';
|
||||
import { useAssemblyHandler } from './actionHandler/useAssemblyHandler';
|
||||
import { useManufacturerHandler } from './actionHandler/useManufacturerHandler';
|
||||
|
||||
export function useHumanActions() {
|
||||
const { handleWorker } = useWorkerHandler();
|
||||
const { handleAssembly } = useAssemblyHandler();
|
||||
const { handleManufacturer } = useManufacturerHandler();
|
||||
|
||||
const handleWorkerAction = useCallback((action: HumanAction, materialId: string) => {
|
||||
handleWorker(action, materialId);
|
||||
}, [handleWorker]);
|
||||
|
||||
const handleAssemblyAction = useCallback((action: HumanAction, materialId: string) => {
|
||||
handleAssembly(action, materialId);
|
||||
}, [handleAssembly]);
|
||||
const handleManufactureAction = useCallback((action: HumanAction, materialId: string) => {
|
||||
handleManufacturer(action, materialId);
|
||||
}, [handleManufacturer]);
|
||||
|
||||
const handleHumanAction = useCallback((action: HumanAction, materialId: string) => {
|
||||
if (!action) return;
|
||||
@@ -21,13 +21,13 @@ export function useHumanActions() {
|
||||
case 'worker':
|
||||
handleWorkerAction(action, materialId);
|
||||
break;
|
||||
case 'assembly':
|
||||
handleAssemblyAction(action, materialId);
|
||||
case 'manufacturer':
|
||||
handleManufactureAction(action, materialId);
|
||||
break;
|
||||
default:
|
||||
console.warn(`Unknown Human action type: ${action.actionType}`);
|
||||
}
|
||||
}, [handleWorkerAction, handleAssemblyAction]);
|
||||
}, [handleWorkerAction, handleManufactureAction]);
|
||||
|
||||
const cleanup = useCallback(() => {
|
||||
}, []);
|
||||
|
||||
@@ -41,7 +41,7 @@ export function useActionHandler() {
|
||||
case 'store': case 'retrieve':
|
||||
handleStorageAction(action as StorageAction, materialId as string);
|
||||
break;
|
||||
case 'worker': case 'assembly':
|
||||
case 'worker': case 'manufacturer':
|
||||
handleHumanAction(action as HumanAction, materialId as string);
|
||||
break;
|
||||
case 'pickAndDrop':
|
||||
|
||||
@@ -25,7 +25,7 @@ export function useHumanEventManager() {
|
||||
const addHumanToMonitor = (humanId: string, callback: () => void, actionUuid: string) => {
|
||||
const human = getHumanById(humanId);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, actionUuid);
|
||||
if (!human || !action || (action.actionType !== 'assembly' && action.actionType !== 'worker' && action.actionType !== 'operator') || !humanEventManagerRef.current) return;
|
||||
if (!human || !action || (action.actionType !== 'manufacturer' && action.actionType !== 'worker' && action.actionType !== 'operator') || !humanEventManagerRef.current) return;
|
||||
|
||||
let state = humanEventManagerRef.current.humanStates.find(h => h.humanId === humanId);
|
||||
if (!state) {
|
||||
@@ -42,8 +42,8 @@ export function useHumanEventManager() {
|
||||
existingAction.isMonitored = true;
|
||||
existingAction.isCompleted = false;
|
||||
}
|
||||
} else if (existingAction.actionType === 'assembly') {
|
||||
if (currentCount < existingAction.maxAssemblyCount) {
|
||||
} else if (existingAction.actionType === 'manufacturer') {
|
||||
if (currentCount < existingAction.maxManufactureCount) {
|
||||
existingAction.callback = callback;
|
||||
existingAction.isMonitored = true;
|
||||
existingAction.isCompleted = false;
|
||||
@@ -57,7 +57,7 @@ export function useHumanEventManager() {
|
||||
actionUuid,
|
||||
actionName: action.actionName,
|
||||
maxLoadCount: action.loadCount ?? 0,
|
||||
maxAssemblyCount: action.assemblyCount ?? 0,
|
||||
maxManufactureCount: action.manufactureCount ?? 0,
|
||||
count: 0,
|
||||
isMonitored: true,
|
||||
isCompleted: false,
|
||||
@@ -101,11 +101,11 @@ export function useHumanEventManager() {
|
||||
if (currentAction.actionType === 'worker' || currentAction.actionType === 'operator') {
|
||||
if ((action.actionType === 'worker' || action.actionType === 'operator') && human.currentLoad < currentAction.loadCapacity) {
|
||||
conditionMet = true;
|
||||
} else if (action.actionType === 'assembly') {
|
||||
} else if (action.actionType === 'manufacturer') {
|
||||
conditionMet = true;
|
||||
}
|
||||
} else if (currentAction.actionType === 'assembly') {
|
||||
if (action.actionType === 'assembly') {
|
||||
} else if (currentAction.actionType === 'manufacturer') {
|
||||
if (action.actionType === 'manufacturer') {
|
||||
conditionMet = true;
|
||||
} else if ((action.actionType === 'worker' || action.actionType === 'operator') && human.currentLoad < currentAction.loadCapacity) {
|
||||
conditionMet = true;
|
||||
@@ -122,7 +122,7 @@ export function useHumanEventManager() {
|
||||
action.count = (action.count ?? 0) + 1;
|
||||
action.isMonitored = false;
|
||||
if (((action.actionType === 'worker' || action.actionType === 'operator') && action.count >= action.maxLoadCount) ||
|
||||
(action.actionType === 'assembly' && action.count >= action.maxAssemblyCount)) {
|
||||
(action.actionType === 'manufacturer' && action.count >= action.maxManufactureCount)) {
|
||||
action.isCompleted = true;
|
||||
}
|
||||
humanState.isCooldown = true;
|
||||
|
||||
@@ -6,14 +6,14 @@ import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useRese
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
|
||||
interface AssemblerAnimatorProps {
|
||||
interface ManufacturerAnimatorProps {
|
||||
path: [number, number, number][];
|
||||
handleCallBack: () => void;
|
||||
reset: () => void;
|
||||
human: HumanStatus;
|
||||
}
|
||||
|
||||
function AssemblerAnimator({ path, handleCallBack, human, reset }: Readonly<AssemblerAnimatorProps>) {
|
||||
function ManufacturerAnimator({ path, handleCallBack, human, reset }: Readonly<ManufacturerAnimatorProps>) {
|
||||
const { humanStore, assetStore, productStore } = useSceneContext();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
@@ -27,16 +27,16 @@ function AssemblerAnimator({ path, handleCallBack, human, reset }: Readonly<Asse
|
||||
const progressRef = useRef<number>(0);
|
||||
const completedRef = useRef<boolean>(false);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.assemblyPoint?.rotation || [0, 0, 0]);
|
||||
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.manufacturePoint?.rotation || [0, 0, 0]);
|
||||
const [restRotation, setRestingRotation] = useState<boolean>(true);
|
||||
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
|
||||
const { scene } = useThree();
|
||||
|
||||
useEffect(() => {
|
||||
if (!human.currentAction?.actionUuid) return;
|
||||
if (human.currentPhase === 'init-assembly' && path.length > 0) {
|
||||
if (human.currentPhase === 'init-manufacture' && path.length > 0) {
|
||||
setCurrentPath(path);
|
||||
setObjectRotation((action as HumanAction)?.assemblyPoint?.rotation ?? null);
|
||||
setObjectRotation((action as HumanAction)?.manufacturePoint?.rotation ?? null);
|
||||
}
|
||||
}, [human.currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);
|
||||
|
||||
@@ -169,4 +169,4 @@ function AssemblerAnimator({ path, handleCallBack, human, reset }: Readonly<Asse
|
||||
);
|
||||
}
|
||||
|
||||
export default AssemblerAnimator;
|
||||
export default ManufacturerAnimator;
|
||||
@@ -50,7 +50,7 @@ const MaterialAnimator = ({ human }: { human: HumanStatus; }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasLoad && action && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup') && (
|
||||
{hasLoad && action && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-manufacture' && human.currentPhase !== 'drop-pickup') && (
|
||||
<MaterialModel
|
||||
matRef={meshRef}
|
||||
materialId={`human-${human.currentMaterials[0].materialId}` || ''}
|
||||
|
||||
@@ -127,13 +127,13 @@ function WorkerAnimator({ path, handleCallBack, human, reset, startUnloadingProc
|
||||
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
|
||||
const position = start.clone().lerp(end, t);
|
||||
object.position.copy(position);
|
||||
if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-manufacture' && human.currentPhase !== 'drop-pickup')) {
|
||||
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
}
|
||||
} else {
|
||||
if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-manufacture' && human.currentPhase !== 'drop-pickup')) {
|
||||
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
|
||||
@@ -8,9 +8,9 @@ import { useTriggerHandler } from '../../../../triggers/triggerHandler/useTrigge
|
||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../../products/productContext';
|
||||
|
||||
import AssemblerAnimator from '../../animator/assemblerAnimator';
|
||||
import ManufacturerAnimator from '../../animator/manufacturerAnimator';
|
||||
|
||||
function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
function ManufacturerInstance({ human }: { human: HumanStatus }) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { scene } = useThree();
|
||||
@@ -99,24 +99,24 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
if (isPlaying) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
if (!action || !(action as HumanAction).assemblyPoint || (action as HumanAction).actionType === 'worker') return;
|
||||
if (!action || !(action as HumanAction).manufacturePoint || (action as HumanAction).actionType === 'worker') return;
|
||||
|
||||
if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
|
||||
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!humanMesh) return;
|
||||
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), (action as HumanAction)?.assemblyPoint?.position || [0, 0, 0]);
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), (action as HumanAction)?.manufacturePoint?.position || [0, 0, 0]);
|
||||
setPath(toPickupPath);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'init-assembly');
|
||||
setCurrentPhase(human.modelUuid, 'init-manufacture');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in manufacture');
|
||||
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'waiting') {
|
||||
if (human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current !== 'working_standing') {
|
||||
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, false);
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setCurrentPhase(human.modelUuid, 'assembling');
|
||||
setCurrentPhase(human.modelUuid, 'manufacturing');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
|
||||
processStartTimeRef.current = performance.now();
|
||||
@@ -127,16 +127,16 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
hasLoggedCompleted.current = false;
|
||||
|
||||
if (!processAnimationIdRef.current) {
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackManufactureProcess);
|
||||
}
|
||||
}
|
||||
} else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
|
||||
if ((action as HumanAction).assemblyPoint && human.currentPhase === 'assembling') {
|
||||
if ((action as HumanAction).manufacturePoint && human.currentPhase === 'manufacturing') {
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in manufacture');
|
||||
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
@@ -150,7 +150,7 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
}
|
||||
}, [human, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
||||
|
||||
const trackAssemblyProcess = useCallback(() => {
|
||||
const trackManufactureProcess = useCallback(() => {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
const now = performance.now();
|
||||
@@ -163,7 +163,7 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
if (!lastPauseTimeRef.current) {
|
||||
lastPauseTimeRef.current = now;
|
||||
}
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackManufactureProcess);
|
||||
return;
|
||||
} else if (lastPauseTimeRef.current) {
|
||||
accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
|
||||
@@ -178,7 +178,7 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
if (human.currentMaterials.length > 0) {
|
||||
setMaterial(human.currentMaterials[0].materialId, (action as HumanAction).swapMaterial || 'Default Material');
|
||||
}
|
||||
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
|
||||
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in manufacture.`);
|
||||
}
|
||||
|
||||
if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
|
||||
@@ -188,27 +188,27 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed assembly process.`);
|
||||
humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed manufacture process.`);
|
||||
return;
|
||||
}
|
||||
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackManufactureProcess);
|
||||
}, [human.modelUuid, human.currentMaterials]);
|
||||
|
||||
function handleCallBack() {
|
||||
if (human.currentPhase === 'init-assembly') {
|
||||
if (human.currentPhase === 'init-manufacture') {
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached assembly point, waiting for material');
|
||||
humanStatus(human.modelUuid, 'Reached manufacture point, waiting for material');
|
||||
setPath([]);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AssemblerAnimator
|
||||
<ManufacturerAnimator
|
||||
path={path}
|
||||
handleCallBack={handleCallBack}
|
||||
human={human}
|
||||
@@ -218,4 +218,4 @@ function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
)
|
||||
}
|
||||
|
||||
export default AssemblerInstance;
|
||||
export default ManufacturerInstance;
|
||||
@@ -4,7 +4,7 @@ import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
|
||||
import MaterialAnimator from '../animator/materialAnimator';
|
||||
import AssemblerInstance from './actions/assemberInstance';
|
||||
import ManufacturerInstance from './actions/manufacturerInstance';
|
||||
import WorkerInstance from './actions/workerInstance';
|
||||
import OperatorInstance from './actions/operatorInstance';
|
||||
|
||||
@@ -84,8 +84,8 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
{action && action.actionType === 'worker' &&
|
||||
<WorkerInstance human={human} />
|
||||
}
|
||||
{action && action.actionType === 'assembly' &&
|
||||
<AssemblerInstance human={human} />
|
||||
{action && action.actionType === 'manufacturer' &&
|
||||
<ManufacturerInstance human={human} />
|
||||
}
|
||||
{action && action.actionType === 'operator' &&
|
||||
<OperatorInstance human={human} />
|
||||
|
||||
@@ -10,16 +10,16 @@ import { useVersionContext } from '../../../../builder/version/versionContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb";
|
||||
import startEnd from "../../../../../assets/gltf-glb/ui/human-ui-orange.glb";
|
||||
import assembly from "../../../../../assets/gltf-glb/ui/human-ui-assembly.glb";
|
||||
import manufacture from "../../../../../assets/gltf-glb/ui/human-ui-manufacture.glb";
|
||||
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||
|
||||
function HumanUi() {
|
||||
const { scene: startScene } = useGLTF(startPoint) as any;
|
||||
const { scene: endScene } = useGLTF(startEnd) as any;
|
||||
const { scene: assemblyScene } = useGLTF(assembly) as any;
|
||||
const { scene: manufactureScene } = useGLTF(manufacture) as any;
|
||||
const startMarker = useRef<Group>(null);
|
||||
const endMarker = useRef<Group>(null);
|
||||
const assemblyMarker = useRef<Group>(null);
|
||||
const manufactureMarker = useRef<Group>(null);
|
||||
const outerGroup = useRef<Group>(null);
|
||||
const prevMousePos = useRef({ x: 0, y: 0 });
|
||||
const { controls, raycaster, camera } = useThree();
|
||||
@@ -31,9 +31,9 @@ function HumanUi() {
|
||||
const { updateEvent, getActionByUuid } = productStore();
|
||||
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
|
||||
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
|
||||
const [assemblyPosition, setAssemblyPosition] = useState<[number, number, number]>([0, 1, 0]);
|
||||
const [manufacturePosition, setManufacturePosition] = useState<[number, number, number]>([0, 1, 0]);
|
||||
const [startRotation, setStartRotation] = useState<[number, number, number]>([0, Math.PI, 0]);
|
||||
const [assemblyRotation, setAssemblyRotation] = useState<[number, number, number]>([0, 0, 0]);
|
||||
const [manufactureRotation, setManufactureRotation] = useState<[number, number, number]>([0, 0, 0]);
|
||||
const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
|
||||
const { isDragging, setIsDragging } = useIsDragging();
|
||||
const { isRotating, setIsRotating } = useIsRotating();
|
||||
@@ -51,7 +51,7 @@ function HumanUi() {
|
||||
const { projectId } = useParams();
|
||||
|
||||
const currentAction = getActionByUuid(selectedProduct.productUuid, selectedAction.actionId || '');
|
||||
const isAssembly = currentAction?.actionType === 'assembly';
|
||||
const isManufacture = currentAction?.actionType === 'manufacturer';
|
||||
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
@@ -90,15 +90,15 @@ function HumanUi() {
|
||||
const action = selectedHuman.point.actions.find(a => a.actionUuid === selectedAction.actionId);
|
||||
if (!action) return;
|
||||
|
||||
if (isAssembly) {
|
||||
if (action.assemblyPoint?.position && outerGroup.current) {
|
||||
const worldPos = new Vector3(...action.assemblyPoint.position);
|
||||
if (isManufacture) {
|
||||
if (action.manufacturePoint?.position && outerGroup.current) {
|
||||
const worldPos = new Vector3(...action.manufacturePoint.position);
|
||||
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
|
||||
setAssemblyPosition([localPosition.x, 1, localPosition.z]);
|
||||
setAssemblyRotation(action.assemblyPoint.rotation || [0, 0, 0]);
|
||||
setManufacturePosition([localPosition.x, 1, localPosition.z]);
|
||||
setManufactureRotation(action.manufacturePoint.rotation || [0, 0, 0]);
|
||||
} else {
|
||||
setAssemblyPosition([0, 1, 0]);
|
||||
setAssemblyRotation([0, 0, 0]);
|
||||
setManufacturePosition([0, 1, 0]);
|
||||
setManufactureRotation([0, 0, 0]);
|
||||
}
|
||||
} else {
|
||||
if (action.pickUpPoint?.position && outerGroup.current) {
|
||||
@@ -125,8 +125,8 @@ function HumanUi() {
|
||||
|
||||
const handlePointerDown = (
|
||||
e: any,
|
||||
state: "start" | "end" | "assembly",
|
||||
rotation: "start" | "end" | "assembly"
|
||||
state: "start" | "end" | "manufacture",
|
||||
rotation: "start" | "end" | "manufacture"
|
||||
) => {
|
||||
e.stopPropagation();
|
||||
const intersection = new Vector3();
|
||||
@@ -153,7 +153,7 @@ function HumanUi() {
|
||||
const marker =
|
||||
state === "start" ? startMarker.current :
|
||||
state === "end" ? endMarker.current :
|
||||
assemblyMarker.current;
|
||||
manufactureMarker.current;
|
||||
if (marker && localPoint) {
|
||||
const markerPos = new Vector3().copy(marker.position);
|
||||
dragOffset.current.copy(markerPos.sub(localPoint));
|
||||
@@ -176,17 +176,17 @@ function HumanUi() {
|
||||
const updatedActions = selectedHuman.point.actions.map(action => {
|
||||
if (action.actionUuid !== currentAction.actionUuid) return action;
|
||||
|
||||
if (isAssembly) {
|
||||
if (!assemblyMarker.current || !outerGroup.current) return action;
|
||||
if (isManufacture) {
|
||||
if (!manufactureMarker.current || !outerGroup.current) return action;
|
||||
|
||||
const worldPosAssembly = new Vector3(...assemblyPosition);
|
||||
const globalAssemblyPosition = outerGroup.current.localToWorld(worldPosAssembly.clone());
|
||||
const worldPosManufacture = new Vector3(...manufacturePosition);
|
||||
const globalManufacturePosition = outerGroup.current.localToWorld(worldPosManufacture.clone());
|
||||
|
||||
return {
|
||||
...action,
|
||||
assemblyPoint: {
|
||||
position: [globalAssemblyPosition.x, globalAssemblyPosition.y, globalAssemblyPosition.z] as [number, number, number],
|
||||
rotation: assemblyRotation
|
||||
manufacturePoint: {
|
||||
position: [globalManufacturePosition.x, globalManufacturePosition.y, globalManufacturePosition.z] as [number, number, number],
|
||||
rotation: manufactureRotation
|
||||
},
|
||||
};
|
||||
} else {
|
||||
@@ -246,8 +246,8 @@ function HumanUi() {
|
||||
setStartPosition([localPoint.x, 1, localPoint.z]);
|
||||
} else if (isDragging === "end") {
|
||||
setEndPosition([localPoint.x, 1, localPoint.z]);
|
||||
} else if (isDragging === "assembly") {
|
||||
setAssemblyPosition([localPoint.x, 1, localPoint.z]);
|
||||
} else if (isDragging === "manufacture") {
|
||||
setManufacturePosition([localPoint.x, 1, localPoint.z]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -260,7 +260,7 @@ function HumanUi() {
|
||||
const marker =
|
||||
isRotating === "start" ? startMarker.current :
|
||||
isRotating === "end" ? endMarker.current :
|
||||
assemblyMarker.current;
|
||||
manufactureMarker.current;
|
||||
|
||||
if (marker) {
|
||||
const rotationSpeed = 10;
|
||||
@@ -279,7 +279,7 @@ function HumanUi() {
|
||||
marker.rotation.z,
|
||||
]);
|
||||
} else {
|
||||
setAssemblyRotation([
|
||||
setManufactureRotation([
|
||||
marker.rotation.x,
|
||||
marker.rotation.y,
|
||||
marker.rotation.z,
|
||||
@@ -303,7 +303,7 @@ function HumanUi() {
|
||||
return () => {
|
||||
window.removeEventListener("pointerup", handleGlobalPointerUp);
|
||||
};
|
||||
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, assemblyPosition, assemblyRotation]);
|
||||
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, manufacturePosition, manufactureRotation]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -313,16 +313,16 @@ function HumanUi() {
|
||||
ref={outerGroup}
|
||||
rotation={[0, Math.PI, 0]}
|
||||
>
|
||||
{isAssembly ? (
|
||||
{isManufacture ? (
|
||||
<MarkerPrimitive
|
||||
name="assemblyMarker"
|
||||
refProp={assemblyMarker}
|
||||
object={assemblyScene}
|
||||
position={assemblyPosition}
|
||||
rotation={assemblyRotation}
|
||||
name="manufactureMarker"
|
||||
refProp={manufactureMarker}
|
||||
object={manufactureScene}
|
||||
position={manufacturePosition}
|
||||
rotation={manufactureRotation}
|
||||
outerGroupRef={outerGroup}
|
||||
type="assembly"
|
||||
subtype="assembly"
|
||||
type="manufacture"
|
||||
subtype="manufacture"
|
||||
color="#0f87f7"
|
||||
setIsDragging={setIsDragging}
|
||||
setIsRotating={setIsRotating}
|
||||
|
||||
@@ -1341,7 +1341,7 @@ export function useTriggerHandler() {
|
||||
handleAction(action, material.materialId);
|
||||
}
|
||||
|
||||
} else if (material && action.actionType === 'assembly') {
|
||||
} else if (material && action.actionType === 'manufacturer') {
|
||||
|
||||
setPreviousLocation(material.materialId, {
|
||||
modelUuid: material.current.modelUuid,
|
||||
|
||||
@@ -251,7 +251,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
}
|
||||
} else if (model.type === 'human') {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
|
||||
if (action && (triggeredAction?.actionType === 'assembly' || triggeredAction?.actionType === 'worker')) {
|
||||
if (action && (triggeredAction?.actionType === 'manufacturer' || triggeredAction?.actionType === 'worker')) {
|
||||
handleMaterialDropToHuman(model, triggeredAction);
|
||||
}
|
||||
} else if (model.type === 'crane') {
|
||||
|
||||
@@ -1,92 +1,134 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { LogoIconLarge } from '../components/icons/Logo';
|
||||
import EmailInput from '../components/forgotPassword/EmailInput';
|
||||
import OTPVerification from '../components/forgotPassword/OTP_Verification';
|
||||
import PasswordSetup from '../components/forgotPassword/PasswordSetup';
|
||||
import ConfirmationMessage from '../components/forgotPassword/ConfirmationMessgae';
|
||||
import React, { useState, useEffect, FormEvent } from "react";
|
||||
import { LogoIconLarge } from "../components/icons/Logo";
|
||||
import EmailInput from "../components/forgotPassword/EmailInput";
|
||||
import OTPVerification from "../components/forgotPassword/OTP_Verification";
|
||||
import PasswordSetup from "../components/forgotPassword/PasswordSetup";
|
||||
import ConfirmationMessage from "../components/forgotPassword/ConfirmationMessgae";
|
||||
import { changePasswordApi } from "../services/factoryBuilder/signInSignUp/changePasswordApi";
|
||||
import { checkEmailApi } from "../services/factoryBuilder/signInSignUp/checkEmailApi";
|
||||
import { verifyOtpApi } from "../services/factoryBuilder/signInSignUp/verifyOtpApi";
|
||||
|
||||
const ForgotPassword: React.FC = () => {
|
||||
const [step, setStep] = useState(1);
|
||||
const [email, setEmail] = useState('');
|
||||
const [code, setCode] = useState('');
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [timer, setTimer] = useState(30);
|
||||
const [step, setStep] = useState(1);
|
||||
const [email, setEmail] = useState("");
|
||||
const [code, setCode] = useState("");
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [timer, setTimer] = useState(30);
|
||||
const [resetToken, setResetToken] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
let countdown: NodeJS.Timeout;
|
||||
if (step === 2 && timer > 0) {
|
||||
countdown = setTimeout(() => setTimer(prev => prev - 1), 1000);
|
||||
}
|
||||
return () => clearTimeout(countdown);
|
||||
}, [step, timer]);
|
||||
|
||||
const handleSubmitEmail = () => {
|
||||
setStep(2);
|
||||
setTimer(30);
|
||||
useEffect(() => {
|
||||
let countdown: NodeJS.Timeout;
|
||||
if (step === 2 && timer > 0) {
|
||||
countdown = setTimeout(() => setTimer((prev) => prev - 1), 1000);
|
||||
}
|
||||
const resendCode = () => {
|
||||
// TODO: call API to resend code
|
||||
setTimer(30);
|
||||
};
|
||||
return () => clearTimeout(countdown);
|
||||
}, [step, timer]);
|
||||
|
||||
return (
|
||||
<div className='forgot-password-page auth-container'>
|
||||
<div className='forgot-password-wrapper'>
|
||||
<div className='logo-icon'>
|
||||
<LogoIconLarge />
|
||||
</div>
|
||||
const resendCode = async () => {
|
||||
// TODO: call API to resend code
|
||||
setTimer(30);
|
||||
try {
|
||||
const emailResponse = await checkEmailApi(email);
|
||||
|
||||
if (emailResponse.message == "OTP sent Successfully") {
|
||||
setCode(emailResponse.OTP);
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
const handleEmailSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
setTimer(30);
|
||||
try {
|
||||
const emailResponse = await checkEmailApi(email);
|
||||
|
||||
{step === 1 && (
|
||||
<>
|
||||
<EmailInput
|
||||
email={email}
|
||||
setEmail={setEmail}
|
||||
onSubmit={handleSubmitEmail}
|
||||
/>
|
||||
<a href='/' className='login continue-button'>Login</a>
|
||||
</>
|
||||
if (emailResponse.message == "OTP sent Successfully") {
|
||||
setStep(2);
|
||||
setCode(emailResponse.OTP);
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
|
||||
)}
|
||||
const handleOTPSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
try {
|
||||
const otpResponse = await verifyOtpApi(email, Number(code));
|
||||
|
||||
if (otpResponse.message == "OTP verified successfully") {
|
||||
setResetToken(otpResponse.resetToken);
|
||||
setStep(3);
|
||||
} else {
|
||||
alert(otpResponse.message);
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
|
||||
{step === 2 && (
|
||||
<>
|
||||
<OTPVerification
|
||||
email={email}
|
||||
timer={timer}
|
||||
setCode={setCode}
|
||||
onSubmit={() => setStep(3)}
|
||||
resendCode={resendCode}
|
||||
/>
|
||||
<a href='/' className='login'>Login</a>
|
||||
</>
|
||||
const handlePasswordSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
try {
|
||||
const passwordResponse = await changePasswordApi(
|
||||
resetToken,
|
||||
newPassword,
|
||||
confirmPassword
|
||||
);
|
||||
if (passwordResponse.message === "Password reset successfull!!") {
|
||||
setStep(4);
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
|
||||
)}
|
||||
|
||||
|
||||
{step === 3 && (
|
||||
<>
|
||||
<PasswordSetup
|
||||
newPassword={newPassword}
|
||||
confirmPassword={confirmPassword}
|
||||
setNewPassword={setNewPassword}
|
||||
setConfirmPassword={setConfirmPassword}
|
||||
onSubmit={() => setStep(4)}
|
||||
/>
|
||||
<a href='/' className='login'>Login</a>
|
||||
</>
|
||||
|
||||
)}
|
||||
|
||||
|
||||
{step === 4 && <ConfirmationMessage />}
|
||||
|
||||
|
||||
</div>
|
||||
return (
|
||||
<div className="forgot-password-page auth-container">
|
||||
<div className="forgot-password-wrapper">
|
||||
<div className="logo-icon">
|
||||
<LogoIconLarge />
|
||||
</div>
|
||||
);
|
||||
|
||||
{step === 1 && (
|
||||
<>
|
||||
<EmailInput
|
||||
email={email}
|
||||
setEmail={setEmail}
|
||||
onSubmit={handleEmailSubmit}
|
||||
/>
|
||||
<a href="/" className="login continue-button">
|
||||
Login
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
|
||||
{step === 2 && (
|
||||
<>
|
||||
<OTPVerification
|
||||
code={code}
|
||||
email={email}
|
||||
timer={timer}
|
||||
setCode={setCode}
|
||||
onSubmit={handleOTPSubmit}
|
||||
resendCode={resendCode}
|
||||
/>
|
||||
<a href="/" className="login">
|
||||
Login
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
|
||||
{step === 3 && (
|
||||
<>
|
||||
<PasswordSetup
|
||||
newPassword={newPassword}
|
||||
confirmPassword={confirmPassword}
|
||||
setNewPassword={setNewPassword}
|
||||
setConfirmPassword={setConfirmPassword}
|
||||
onSubmit={handlePasswordSubmit}
|
||||
/>
|
||||
<a href="/" className="login">
|
||||
Login
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
|
||||
{step === 4 && <ConfirmationMessage />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPassword;
|
||||
|
||||
@@ -34,7 +34,7 @@ const UserAuth: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
initializeFingerprint();
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
const { userId, organization } = getUserData();
|
||||
|
||||
@@ -47,7 +47,6 @@ const UserAuth: React.FC = () => {
|
||||
setError("");
|
||||
setOrganization(organization);
|
||||
setUserName(res.message.name);
|
||||
// console.log(' res.userId: ', res.message.userId);
|
||||
localStorage.setItem("userId", res.message.userId);
|
||||
localStorage.setItem("email", res.message.email);
|
||||
localStorage.setItem("userName", res.message.name);
|
||||
@@ -55,33 +54,46 @@ const UserAuth: React.FC = () => {
|
||||
localStorage.setItem("refreshToken", res.message.refreshToken);
|
||||
|
||||
try {
|
||||
const projects = await recentlyViewed(organization, res.message.userId);
|
||||
const projects = await recentlyViewed(
|
||||
organization,
|
||||
res.message.userId
|
||||
);
|
||||
if (res.message.isShare) {
|
||||
if (Object.values(projects.RecentlyViewed).length > 0) {
|
||||
const recent_opend_projectID = (Object.values(projects?.RecentlyViewed || {})[0] as any)?._id;
|
||||
if (Object.values(projects?.RecentlyViewed).filter((val: any) => val._id == recent_opend_projectID)) {
|
||||
setLoadingProgress(1)
|
||||
navigate(`/projects/${recent_opend_projectID}`)
|
||||
const recent_opend_projectID = (
|
||||
Object.values(projects?.RecentlyViewed || {})[0] as any
|
||||
)?._id;
|
||||
if (
|
||||
Object.values(projects?.RecentlyViewed).filter(
|
||||
(val: any) => val._id == recent_opend_projectID
|
||||
)
|
||||
) {
|
||||
setLoadingProgress(1);
|
||||
navigate(`/projects/${recent_opend_projectID}`);
|
||||
} else {
|
||||
navigate("/Dashboard")
|
||||
navigate("/Dashboard");
|
||||
}
|
||||
} else {
|
||||
setLoadingProgress(1);
|
||||
navigate("/Dashboard");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching recent projects:", error);
|
||||
}
|
||||
} else if (res.message === "User Not Found!!! Kindly signup...") {
|
||||
setError("Account not found");
|
||||
} else if (res.message === "Email & Password is invalid...Check the credentials") {
|
||||
setError(res.message)
|
||||
} else if (res.message === "Already LoggedIn on another browser....Please logout!!!") {
|
||||
} else if (
|
||||
res.message === "Email & Password is invalid...Check the credentials"
|
||||
) {
|
||||
setError(res.message);
|
||||
} else if (
|
||||
res.message ===
|
||||
"Already LoggedIn on another browser....Please logout!!!"
|
||||
) {
|
||||
setError("Already logged in on another browser. Please logout first.");
|
||||
navigate("/");
|
||||
setError("")
|
||||
setError("");
|
||||
// setError("");
|
||||
// setOrganization(organization);
|
||||
// setUserName(res.ForceLogoutData.userName);
|
||||
@@ -179,6 +191,7 @@ const UserAuth: React.FC = () => {
|
||||
name="email"
|
||||
value={email}
|
||||
placeholder="Email"
|
||||
autoComplete="email"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -188,6 +201,7 @@ const UserAuth: React.FC = () => {
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={password}
|
||||
placeholder="Password"
|
||||
autoComplete="current-password"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -201,7 +215,11 @@ const UserAuth: React.FC = () => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isSignIn && <a href="forgot" className="forgot-password">Forgot password ?</a>}
|
||||
{isSignIn && (
|
||||
<a href="forgot" className="forgot-password">
|
||||
Forgot password ?
|
||||
</a>
|
||||
)}
|
||||
|
||||
{!isSignIn && (
|
||||
<div className="policy-checkbox">
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const changePasswordApi = async (
|
||||
resetToken: string,
|
||||
newPassword: string,
|
||||
confirmPassword: string
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/V1/Auth/reset-password/${resetToken}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ newPassword, confirmPassword }),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
echo.error("Failed to create password");
|
||||
if (error instanceof Error) {
|
||||
return { error: error.message };
|
||||
} else {
|
||||
return { error: "An unknown error occurred" };
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const checkEmailApi = async (Email: string) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/V1/Auth/forgetPassword`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ Email }),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
echo.error("Failed to create password");
|
||||
if (error instanceof Error) {
|
||||
return { error: error.message };
|
||||
} else {
|
||||
return { error: "An unknown error occurred" };
|
||||
}
|
||||
}
|
||||
};
|
||||
28
app/src/services/factoryBuilder/signInSignUp/verifyOtpApi.ts
Normal file
28
app/src/services/factoryBuilder/signInSignUp/verifyOtpApi.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const verifyOtpApi = async (Email: string, Otp: number) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/V1/Auth/validate-otp`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
Email,
|
||||
otp: Number(Otp),
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
echo.error("Failed to create password");
|
||||
if (error instanceof Error) {
|
||||
return { error: error.message };
|
||||
} else {
|
||||
return { error: "An unknown error occurred" };
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -647,12 +647,12 @@ export const useContextActionStore = create<any>((set: any) => ({
|
||||
|
||||
// Define the store's state and actions type
|
||||
interface DecalStore {
|
||||
selectedSubCategory: string;
|
||||
setSelectedSubCategory: (subCategory: string) => void;
|
||||
selectedSubCategory: string | null;
|
||||
setSelectedSubCategory: (subCategory: string | null) => void;
|
||||
}
|
||||
|
||||
// Create the Zustand store with types
|
||||
export const useDecalStore = create<DecalStore>((set) => ({
|
||||
selectedSubCategory: '',
|
||||
setSelectedSubCategory: (subCategory: string) => set({ selectedSubCategory: subCategory }),
|
||||
selectedSubCategory: null,
|
||||
setSelectedSubCategory: (subCategory: string | null) => set({ selectedSubCategory: subCategory }),
|
||||
}));
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { Object3D } from 'three';
|
||||
import { create } from 'zustand';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
|
||||
interface AssetsStore {
|
||||
assets: Assets;
|
||||
movedObjects: Object3D[];
|
||||
rotatedObjects: Object3D[];
|
||||
copiedObjects: Object3D[];
|
||||
pastedObjects: Object3D[];
|
||||
duplicatedObjects: Object3D[];
|
||||
|
||||
// Asset CRUD operations
|
||||
addAsset: (asset: Asset) => void;
|
||||
@@ -12,6 +18,12 @@ interface AssetsStore {
|
||||
resetAsset: (modelUuid: string) => void;
|
||||
setAssets: (assets: Assets) => void;
|
||||
|
||||
setMovedObjects: (objects: Object3D[]) => Object3D[];
|
||||
setRotatedObjects: (objects: Object3D[]) => Object3D[];
|
||||
setCopiedObjects: (objects: Object3D[]) => Object3D[];
|
||||
setPastedObjects: (objects: Object3D[]) => Object3D[];
|
||||
setDuplicatedObjects: (objects: Object3D[]) => Object3D[];
|
||||
|
||||
// Asset properties
|
||||
setName: (modelUuid: string, newName: string) => void;
|
||||
setPosition: (modelUuid: string, position: [number, number, number]) => void;
|
||||
@@ -44,6 +56,11 @@ export const createAssetStore = () => {
|
||||
return create<AssetsStore>()(
|
||||
immer((set, get) => ({
|
||||
assets: [],
|
||||
movedObjects: [],
|
||||
rotatedObjects: [],
|
||||
copiedObjects: [],
|
||||
pastedObjects: [],
|
||||
duplicatedObjects: [],
|
||||
|
||||
// Asset CRUD operations
|
||||
addAsset: (asset) => {
|
||||
@@ -94,6 +111,41 @@ export const createAssetStore = () => {
|
||||
});
|
||||
},
|
||||
|
||||
setMovedObjects: (objects) => {
|
||||
set((state) => {
|
||||
state.movedObjects = objects;
|
||||
});
|
||||
return objects;
|
||||
},
|
||||
|
||||
setRotatedObjects: (objects) => {
|
||||
set((state) => {
|
||||
state.rotatedObjects = objects;
|
||||
});
|
||||
return objects;
|
||||
},
|
||||
|
||||
setCopiedObjects: (objects) => {
|
||||
set((state) => {
|
||||
state.copiedObjects = objects;
|
||||
});
|
||||
return objects;
|
||||
},
|
||||
|
||||
setPastedObjects: (objects) => {
|
||||
set((state) => {
|
||||
state.pastedObjects = objects;
|
||||
});
|
||||
return objects;
|
||||
},
|
||||
|
||||
setDuplicatedObjects: (objects) => {
|
||||
set((state) => {
|
||||
state.duplicatedObjects = objects;
|
||||
});
|
||||
return objects;
|
||||
},
|
||||
|
||||
// Asset properties
|
||||
setName: (modelUuid, newName) => {
|
||||
set((state) => {
|
||||
|
||||
@@ -15,7 +15,7 @@ interface WallAssetStore {
|
||||
setOpacity: (uuid: string, opacity: number) => void;
|
||||
|
||||
getWallAssetById: (uuid: string) => WallAsset | undefined;
|
||||
getAssetsByWall: (wallUuid: string) => WallAsset[];
|
||||
getWallAssetsByWall: (wallUuid: string) => WallAsset[];
|
||||
}
|
||||
|
||||
export const createWallAssetStore = () => {
|
||||
@@ -93,7 +93,7 @@ export const createWallAssetStore = () => {
|
||||
return get().wallAssets.find(a => a.modelUuid === uuid);
|
||||
},
|
||||
|
||||
getAssetsByWall: (wallUuid) => {
|
||||
getWallAssetsByWall: (wallUuid) => {
|
||||
return get().wallAssets.filter(a => a.wallUuid === wallUuid);
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -25,6 +25,7 @@ interface WallStore {
|
||||
getWallByPoints: (points: Point[]) => Wall | undefined;
|
||||
getWallPointById: (uuid: string) => Point | undefined;
|
||||
getConnectedPoints: (uuid: string) => Point[];
|
||||
getConnectedWallsByWallId: (wallUuid: string, skipSelf: boolean) => Wall[];
|
||||
}
|
||||
|
||||
export const createWallStore = () => {
|
||||
@@ -178,7 +179,7 @@ export const createWallStore = () => {
|
||||
|
||||
getWallsByPointId: (uuid) => {
|
||||
return get().walls.filter((a) => {
|
||||
return a.points.some((p) => p.pointUuid === uuid);
|
||||
return JSON.parse(JSON.stringify(a.points.some((p) => p.pointUuid === uuid)));
|
||||
})
|
||||
},
|
||||
|
||||
@@ -211,6 +212,19 @@ export const createWallStore = () => {
|
||||
}
|
||||
return connected;
|
||||
},
|
||||
|
||||
getConnectedWallsByWallId: (wallUuid, skipSelf) => {
|
||||
const wall = get().walls.find(w => w.wallUuid === wallUuid);
|
||||
if (!wall) return [];
|
||||
|
||||
const pointUuids = wall.points.map(p => p.pointUuid);
|
||||
|
||||
return get().walls.filter(w => {
|
||||
if (skipSelf && w.wallUuid === wallUuid) return false;
|
||||
return w.points.some(p => pointUuids.includes(p.pointUuid));
|
||||
});
|
||||
},
|
||||
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -203,8 +203,8 @@ export const useSelectedAnimation = create<SelectedAnimationState>()(
|
||||
);
|
||||
|
||||
interface IsDraggingState {
|
||||
isDragging: "start" | "end" | "assembly" | null;
|
||||
setIsDragging: (state: "start" | "end" | "assembly" | null) => void;
|
||||
isDragging: "start" | "end" | "manufacture" | null;
|
||||
setIsDragging: (state: "start" | "end" | "manufacture" | null) => void;
|
||||
}
|
||||
|
||||
export const useIsDragging = create<IsDraggingState>()(
|
||||
@@ -219,8 +219,8 @@ export const useIsDragging = create<IsDraggingState>()(
|
||||
);
|
||||
|
||||
interface IsRotatingState {
|
||||
isRotating: "start" | "end" | "assembly" | null;
|
||||
setIsRotating: (state: "start" | "end" | "assembly" | null) => void;
|
||||
isRotating: "start" | "end" | "manufacture" | null;
|
||||
setIsRotating: (state: "start" | "end" | "manufacture" | null) => void;
|
||||
}
|
||||
|
||||
export const useIsRotating = create<IsRotatingState>()(
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
left: 50%;
|
||||
z-index: 2;
|
||||
transform: translate(-50%, 0);
|
||||
width: 70vw;
|
||||
max-width: calc(100vw - (2 * 328px));
|
||||
width: 100%;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.hide {
|
||||
|
||||
@@ -469,10 +469,6 @@
|
||||
width: 304px;
|
||||
|
||||
.decal-transformation-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.transformation-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -501,16 +497,17 @@
|
||||
}
|
||||
|
||||
input {
|
||||
min-width: 43px;
|
||||
min-width: 58px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.layers {
|
||||
.layers-list {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
.layer-move-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -518,6 +515,11 @@
|
||||
padding: 4px 16px;
|
||||
width: 50px;
|
||||
border-radius: 100px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
outline: 1px solid var(--accent-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -585,6 +587,7 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background: var(--background-color);
|
||||
outline: 1px solid #aaaaaa29;
|
||||
@@ -599,6 +602,7 @@
|
||||
|
||||
.kebab-icon {
|
||||
display: flex;
|
||||
|
||||
svg {
|
||||
transform: rotate(90deg) scale(0.8);
|
||||
}
|
||||
@@ -1433,6 +1437,11 @@
|
||||
padding: 12px;
|
||||
border-radius: #{$border-radius-large};
|
||||
|
||||
outline: 1px solid var(--border-color);
|
||||
outline-offset: -1px;
|
||||
border-radius: 12px;
|
||||
background: var(--background-color);
|
||||
|
||||
.compare-simulations-header {
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
@@ -1581,6 +1590,7 @@
|
||||
.analysis-main-container,
|
||||
.asset-properties-container,
|
||||
.zone-properties-container,
|
||||
.decal-transformation-container,
|
||||
.aisle-properties-container {
|
||||
.header {
|
||||
@include flex-space-between;
|
||||
@@ -1596,8 +1606,10 @@
|
||||
.animations-lists {
|
||||
max-height: 210px;
|
||||
overflow: auto;
|
||||
|
||||
.animations-list-wrapper {
|
||||
padding: 0 4px;
|
||||
|
||||
.animations-list {
|
||||
margin: 2px 0;
|
||||
padding: 4px 12px;
|
||||
@@ -1787,6 +1799,11 @@
|
||||
padding: 14px;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.input-toggle-container {
|
||||
padding: 4px 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
@@ -2044,11 +2061,9 @@
|
||||
&: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%);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2185,11 +2200,9 @@
|
||||
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;
|
||||
@@ -2445,4 +2458,4 @@
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
app/src/types/simulationTypes.d.ts
vendored
18
app/src/types/simulationTypes.d.ts
vendored
@@ -96,14 +96,24 @@ interface StorageAction {
|
||||
interface HumanAction {
|
||||
actionUuid: string;
|
||||
actionName: string;
|
||||
actionType: "worker" | "assembly" | "operator";
|
||||
actionType: "worker" | "manufacturer" | "operator" | "assembler";
|
||||
processTime: number;
|
||||
swapMaterial?: string;
|
||||
manufacturePoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
assemblyPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
pickUpPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
dropPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
loadCount: number;
|
||||
assemblyCount: number;
|
||||
assemblyCondition: {
|
||||
conditionType: "material";
|
||||
materialType: string;
|
||||
source?: {
|
||||
sourceUuid: string;
|
||||
sourceActionUuid: string;
|
||||
}[];
|
||||
}
|
||||
loadCount: number;
|
||||
manufactureCount: number;
|
||||
loadCapacity: number;
|
||||
triggers: TriggerSchema[];
|
||||
}
|
||||
@@ -324,11 +334,11 @@ interface CraneStatus extends CraneEventSchema {
|
||||
type HumanEventState = {
|
||||
humanId: string;
|
||||
actionQueue: {
|
||||
actionType: 'worker' | 'assembly' | 'operator';
|
||||
actionType: 'worker' | 'manufacturer' | 'operator';
|
||||
actionUuid: string;
|
||||
actionName: string;
|
||||
maxLoadCount: number;
|
||||
maxAssemblyCount: number;
|
||||
maxManufactureCount: number;
|
||||
count?: number;
|
||||
isMonitored: boolean;
|
||||
isCompleted: boolean;
|
||||
|
||||
39
app/src/utils/compressImage.ts
Normal file
39
app/src/utils/compressImage.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
export async function compressImage(
|
||||
dataUrl: string,
|
||||
maxWidth = 400,
|
||||
maxHeight = 400
|
||||
): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = dataUrl;
|
||||
|
||||
img.onload = () => {
|
||||
const { width, height } = img;
|
||||
|
||||
// Calculate aspect ratio preserving resize
|
||||
let newWidth = width;
|
||||
let newHeight = height;
|
||||
|
||||
if (width > height) {
|
||||
if (width > maxWidth) {
|
||||
newWidth = maxWidth;
|
||||
newHeight = (height * maxWidth) / width;
|
||||
}
|
||||
} else {
|
||||
if (height > maxHeight) {
|
||||
newHeight = maxHeight;
|
||||
newWidth = (width * maxHeight) / height;
|
||||
}
|
||||
}
|
||||
|
||||
const offCanvas = document.createElement("canvas");
|
||||
const ctx = offCanvas.getContext("2d");
|
||||
|
||||
offCanvas.width = newWidth;
|
||||
offCanvas.height = newHeight;
|
||||
|
||||
ctx?.drawImage(img, 0, 0, newWidth, newHeight);
|
||||
resolve(offCanvas.toDataURL("image/png"));
|
||||
};
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user