From 1018c4250049df4cf049cd8da52250e360a7a763 Mon Sep 17 00:00:00 2001 From: Poovizhi Date: Sat, 23 Aug 2025 14:29:25 +0530 Subject: [PATCH 1/2] updated forget password functionality with backend --- app/src/components/Dashboard/SidePannel.tsx | 31 +-- .../components/forgotPassword/EmailInput.tsx | 53 +++-- .../components/forgotPassword/OTPInput.tsx | 107 +++++++++- .../forgotPassword/OTP_Verification.tsx | 114 +++++----- .../forgotPassword/PasswordSetup.tsx | 137 ++++++------ app/src/pages/ForgotPassword.tsx | 196 +++++++++++------- app/src/pages/UserAuth.tsx | 46 ++-- .../signInSignUp/changePasswordApi.ts | 29 +++ .../signInSignUp/checkEmailApi.ts | 25 +++ .../signInSignUp/verifyOtpApi.ts | 28 +++ 10 files changed, 519 insertions(+), 247 deletions(-) create mode 100644 app/src/services/factoryBuilder/signInSignUp/changePasswordApi.ts create mode 100644 app/src/services/factoryBuilder/signInSignUp/checkEmailApi.ts create mode 100644 app/src/services/factoryBuilder/signInSignUp/verifyOtpApi.ts diff --git a/app/src/components/Dashboard/SidePannel.tsx b/app/src/components/Dashboard/SidePannel.tsx index 89ce5ea..0fa30d6 100644 --- a/app/src/components/Dashboard/SidePannel.tsx +++ b/app/src/components/Dashboard/SidePannel.tsx @@ -38,11 +38,13 @@ const SidePannel: React.FC = ({ 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 +61,12 @@ const SidePannel: React.FC = ({ 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}`); + setLoadingProgress(1); + navigate(`/projects/${data.data.projectId}`); } projectSocket.off("v1-project:response:add", handleResponse); // Clean up }; @@ -88,7 +90,8 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => {
{userName - ? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase() + ? userName.charAt(0).toUpperCase() + + userName.slice(1).toLowerCase() : "Anonymous"}
@@ -162,10 +165,14 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { Settings -
{ - localStorage.clear(); - navigate("/"); - }}> +
{ + localStorage.clear(); + navigate("/"); + }} + > Log out
@@ -179,4 +186,4 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { ); }; -export default SidePannel; \ No newline at end of file +export default SidePannel; diff --git a/app/src/components/forgotPassword/EmailInput.tsx b/app/src/components/forgotPassword/EmailInput.tsx index d3ca158..4df4d19 100644 --- a/app/src/components/forgotPassword/EmailInput.tsx +++ b/app/src/components/forgotPassword/EmailInput.tsx @@ -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) => void; } const EmailInput: React.FC = ({ email, setEmail, onSubmit }) => { - return ( -
-

Forgot password

-

- Enter your email for the verification process, we will send a 4-digit code to your email. -

-
{ e.preventDefault(); onSubmit(); }}> - setEmail(e.target.value)} - required - /> - -
-
- ); + return ( +
+

Forgot password

+

+ Enter your email for the verification process, we will send a 4-digit + code to your email. +

+
{ + e.preventDefault(); + onSubmit(e); + }} + > + setEmail(e.target.value)} + required + /> + +
+
+ ); }; export default EmailInput; diff --git a/app/src/components/forgotPassword/OTPInput.tsx b/app/src/components/forgotPassword/OTPInput.tsx index e14b45e..3c6fef8 100644 --- a/app/src/components/forgotPassword/OTPInput.tsx +++ b/app/src/components/forgotPassword/OTPInput.tsx @@ -1,10 +1,95 @@ -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(Array(length).fill('')); +// const OTPInput: React.FC<{ +// length?: number; +// onComplete: (otp: string) => void; +// code: string; +// }> = ({ length = 4, onComplete, code }) => { +// const [otpValues, setOtpValues] = useState(Array(length).fill("")); +// const inputsRef = useRef<(HTMLInputElement | null)[]>([]); +// useEffect(() => { +// if (code) { +// console.log("code: ", code); + +// const codeString = String(code); // convert number → string +// setOtpValues(codeString.split("")); +// onComplete(codeString); +// } +// }, [code, length]); +// // Auto focus first input on mount +// useEffect(() => { +// inputsRef.current[0]?.focus(); +// }, []); + +// const handleChange = (value: string, index: number) => { +// if (/^[0-9]?$/.test(value)) { +// const newOtp = [...otpValues]; +// newOtp[index] = value; +// setOtpValues(newOtp); + +// if (value && index < length - 1) { +// inputsRef.current[index + 1]?.focus(); +// } + +// if (newOtp.every((digit) => digit !== "")) { +// console.log('newOtp.join(""): ', newOtp.join("")); +// onComplete(newOtp.join("")); +// } +// } +// }; + +// const handleKeyDown = ( +// e: React.KeyboardEvent, +// index: number +// ) => { +// if (e.key === "Backspace" && !otpValues[index] && index > 0) { +// inputsRef.current[index - 1]?.focus(); +// } +// }; + +// return ( +//
+// {otpValues.map((value, index) => ( +// handleChange(e.target.value, index)} +// onKeyDown={(e) => handleKeyDown(e, index)} +// ref={(el) => (inputsRef.current[index] = el)} +// /> +// ))} +//
+// ); +// }; + +// export default OTPInput; +import React, { useState, useRef, useEffect } from "react"; + +const OTPInput: React.FC<{ + length?: number; + onComplete: (otp: string) => void; + code: string | number; +}> = ({ length = 4, onComplete, code }) => { + const [otpValues, setOtpValues] = useState(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 +104,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, index: number) => { - if (e.key === 'Backspace' && !otpValues[index] && index > 0) { + const handleKeyDown = ( + e: React.KeyboardEvent, + index: number + ) => { + if (e.key === "Backspace" && !otpValues[index] && index > 0) { inputsRef.current[index - 1]?.focus(); } }; @@ -39,7 +128,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)} diff --git a/app/src/components/forgotPassword/OTP_Verification.tsx b/app/src/components/forgotPassword/OTP_Verification.tsx index 8af7836..450f210 100644 --- a/app/src/components/forgotPassword/OTP_Verification.tsx +++ b/app/src/components/forgotPassword/OTP_Verification.tsx @@ -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) => void; + resendCode: () => void; } -const OTPVerification: React.FC = ({ email, timer, setCode, onSubmit, resendCode }) => { - const [otp, setOtp] = useState(''); +const OTPVerification: React.FC = ({ + 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) => { + e.preventDefault(); - return ( -
-

Verification

-

- Enter the 4-digit code sent to {email}. -

-
- { setOtp(code); setCode(code); }} /> -
- {timer > 0 - ? `${String(Math.floor(timer / 60)).padStart(2, '0')}:${String(timer % 60).padStart(2, '0')}` - : ''} -
- - -
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, Resend -
+ if (otp.length === 4) { + onSubmit(e); + } else { + alert("Please enter the 4-digit code"); + } + }; + + return ( +
+

Verification

+

+ Enter the 4-digit code sent to {email}. +

+
+ { + setOtp(code); + setCode(codes); + }} + code={code} + /> +
+ {timer > 0 + ? `${String(Math.floor(timer / 60)).padStart(2, "0")}:${String( + timer % 60 + ).padStart(2, "0")}` + : ""}
- ); + + +
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, Resend +
+
+ ); }; export default OTPVerification; diff --git a/app/src/components/forgotPassword/PasswordSetup.tsx b/app/src/components/forgotPassword/PasswordSetup.tsx index 4c6af1c..f8f5658 100644 --- a/app/src/components/forgotPassword/PasswordSetup.tsx +++ b/app/src/components/forgotPassword/PasswordSetup.tsx @@ -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) => void; } const PasswordSetup: React.FC = ({ - 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 ( -
-

New Password

-

Set the new password for your account so you can login and access all features.

-
{ - e.preventDefault(); - if (newPassword !== confirmPassword) { - alert('Passwords do not match'); - return; - } - onSubmit(); - }} - > -
- setNewPassword(e.target.value)} - required - /> - -
- -
- setConfirmPassword(e.target.value)} - required - /> - -
- - -
+ return ( +
+

New Password

+

+ Set the new password for your account so you can login and access all + features. +

+
{ + e.preventDefault(); + if (newPassword !== confirmPassword) { + alert("Passwords do not match"); + return; + } + onSubmit(e); + }} + > +
+ setNewPassword(e.target.value)} + required + /> +
- ); + +
+ setConfirmPassword(e.target.value)} + required + /> + +
+ + +
+
+ ); }; export default PasswordSetup; diff --git a/app/src/pages/ForgotPassword.tsx b/app/src/pages/ForgotPassword.tsx index 8681996..c2d8374 100644 --- a/app/src/pages/ForgotPassword.tsx +++ b/app/src/pages/ForgotPassword.tsx @@ -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 ( -
-
-
- -
+ 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) => { + setTimer(30); + try { + const emailResponse = await checkEmailApi(email); - {step === 1 && ( - <> - - Login - + if (emailResponse.message == "OTP sent Successfully") { + setStep(2); + setCode(emailResponse.OTP); + } + } catch {} + }; - )} + const handleOTPSubmit = async (e: FormEvent) => { + 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 && ( - <> - setStep(3)} - resendCode={resendCode} - /> - Login - + const handlePasswordSubmit = async (e: FormEvent) => { + try { + const passwordResponse = await changePasswordApi( + resetToken, + newPassword, + confirmPassword + ); + if (passwordResponse.message === "Password reset successfull!!") { + setStep(4); + } + } catch {} + }; - )} - - - {step === 3 && ( - <> - setStep(4)} - /> - Login - - - )} - - - {step === 4 && } - - -
+ return ( +
+
+
+
- ); + + {step === 1 && ( + <> + + + Login + + + )} + + {step === 2 && ( + <> + + + Login + + + )} + + {step === 3 && ( + <> + + + Login + + + )} + + {step === 4 && } +
+
+ ); }; export default ForgotPassword; diff --git a/app/src/pages/UserAuth.tsx b/app/src/pages/UserAuth.tsx index 8c45b77..06352a0 100644 --- a/app/src/pages/UserAuth.tsx +++ b/app/src/pages/UserAuth.tsx @@ -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 = () => {
- {isSignIn && Forgot password ?} + {isSignIn && ( + + Forgot password ? + + )} {!isSignIn && (
diff --git a/app/src/services/factoryBuilder/signInSignUp/changePasswordApi.ts b/app/src/services/factoryBuilder/signInSignUp/changePasswordApi.ts new file mode 100644 index 0000000..bc42635 --- /dev/null +++ b/app/src/services/factoryBuilder/signInSignUp/changePasswordApi.ts @@ -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" }; + } + } +}; diff --git a/app/src/services/factoryBuilder/signInSignUp/checkEmailApi.ts b/app/src/services/factoryBuilder/signInSignUp/checkEmailApi.ts new file mode 100644 index 0000000..6d8138a --- /dev/null +++ b/app/src/services/factoryBuilder/signInSignUp/checkEmailApi.ts @@ -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" }; + } + } +}; diff --git a/app/src/services/factoryBuilder/signInSignUp/verifyOtpApi.ts b/app/src/services/factoryBuilder/signInSignUp/verifyOtpApi.ts new file mode 100644 index 0000000..9b289e5 --- /dev/null +++ b/app/src/services/factoryBuilder/signInSignUp/verifyOtpApi.ts @@ -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" }; + } + } +}; From e2c5b43c2e05734fe55311bc52eaf58dd589ed9b Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 23 Aug 2025 15:43:19 +0530 Subject: [PATCH 2/2] Refactor SideBarRight component for improved readability and functionality; add DecalProperties component; remove DecalTransformation; update AssetProperties animations rendering; enhance InputRange and RegularDropDown components; adjust styles for better layout; fix state management in decal store. --- app/src/components/Dashboard/SidePannel.tsx | 1 - .../components/forgotPassword/OTPInput.tsx | 68 --- .../components/icons/ExportCommonIcons.tsx | 36 +- .../components/layout/sidebarLeft/Assets.tsx | 519 ++++++++-------- .../layout/sidebarRight/SideBarRight.tsx | 578 ++++++++++-------- .../sidebarRight/customInput/Vector3Input.tsx | 8 +- .../decals/DecalTransformation.tsx | 59 -- .../properties/AssetProperties.tsx | 52 +- .../properties/DecalProperties.tsx | 53 ++ app/src/components/ui/inputs/InputRange.tsx | 56 +- .../components/ui/inputs/RegularDropDown.tsx | 14 +- app/src/components/ui/log/LogList.tsx | 5 +- app/src/store/builder/store.ts | 8 +- .../components/simulation/simulation.scss | 3 +- app/src/styles/layout/sidebar.scss | 20 +- 15 files changed, 741 insertions(+), 739 deletions(-) delete mode 100644 app/src/components/layout/sidebarRight/decals/DecalTransformation.tsx create mode 100644 app/src/components/layout/sidebarRight/properties/DecalProperties.tsx diff --git a/app/src/components/Dashboard/SidePannel.tsx b/app/src/components/Dashboard/SidePannel.tsx index 6e6e461..db7d203 100644 --- a/app/src/components/Dashboard/SidePannel.tsx +++ b/app/src/components/Dashboard/SidePannel.tsx @@ -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>; diff --git a/app/src/components/forgotPassword/OTPInput.tsx b/app/src/components/forgotPassword/OTPInput.tsx index 3c6fef8..09d4537 100644 --- a/app/src/components/forgotPassword/OTPInput.tsx +++ b/app/src/components/forgotPassword/OTPInput.tsx @@ -1,71 +1,3 @@ -// import React, { useState, useRef, useEffect } from "react"; - -// const OTPInput: React.FC<{ -// length?: number; -// onComplete: (otp: string) => void; -// code: string; -// }> = ({ length = 4, onComplete, code }) => { -// const [otpValues, setOtpValues] = useState(Array(length).fill("")); -// const inputsRef = useRef<(HTMLInputElement | null)[]>([]); -// useEffect(() => { -// if (code) { -// console.log("code: ", code); - -// const codeString = String(code); // convert number → string -// setOtpValues(codeString.split("")); -// onComplete(codeString); -// } -// }, [code, length]); -// // Auto focus first input on mount -// useEffect(() => { -// inputsRef.current[0]?.focus(); -// }, []); - -// const handleChange = (value: string, index: number) => { -// if (/^[0-9]?$/.test(value)) { -// const newOtp = [...otpValues]; -// newOtp[index] = value; -// setOtpValues(newOtp); - -// if (value && index < length - 1) { -// inputsRef.current[index + 1]?.focus(); -// } - -// if (newOtp.every((digit) => digit !== "")) { -// console.log('newOtp.join(""): ', newOtp.join("")); -// onComplete(newOtp.join("")); -// } -// } -// }; - -// const handleKeyDown = ( -// e: React.KeyboardEvent, -// index: number -// ) => { -// if (e.key === "Backspace" && !otpValues[index] && index > 0) { -// inputsRef.current[index - 1]?.focus(); -// } -// }; - -// return ( -//
-// {otpValues.map((value, index) => ( -// handleChange(e.target.value, index)} -// onKeyDown={(e) => handleKeyDown(e, index)} -// ref={(el) => (inputsRef.current[index] = el)} -// /> -// ))} -//
-// ); -// }; - -// export default OTPInput; import React, { useState, useRef, useEffect } from "react"; const OTPInput: React.FC<{ diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index 00f7f22..72f9ca0 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -1394,7 +1394,7 @@ export const AlertIcon = () => { @@ -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" /> ); @@ -1419,17 +1419,17 @@ export const NavigationIcon = () => { ); @@ -1447,8 +1447,8 @@ export const HangTagIcon = () => { @@ -1471,20 +1471,20 @@ export const DecalInfoIcon = () => { ); @@ -1505,13 +1505,13 @@ export const LayeringBottomIcon = () => { width="10.9233" height="9.75071" stroke="var(--text-color)" - stroke-width="0.714277" + strokeWidth="0.714277" /> { width="10.9233" height="9.75071" stroke="var(--text-color)" - stroke-width="0.714277" + strokeWidth="0.714277" /> { @@ -1689,7 +1689,7 @@ export const TargetIcon = () => { diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index bc3a445..c08ef78 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -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(""); - const [selectedCategory, setSelectedCategory] = useState(null); - const [categoryAssets, setCategoryAssets] = useState([]); - const [filtereredAssets, setFiltereredAssets] = useState([]); - const [categoryList, setCategoryList] = useState([]); - const [isLoading, setisLoading] = useState(false); // Loading state for assets + const { setSelectedItem } = useSelectedItem(); + const [searchValue, setSearchValue] = useState(""); + const [selectedCategory, setSelectedCategory] = useState(null); + const [categoryAssets, setCategoryAssets] = useState([]); + const [filtereredAssets, setFiltereredAssets] = useState([]); + const [categoryList, setCategoryList] = useState([]); + const [isLoading, setisLoading] = useState(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: }, + { name: "Navigation", icon: }, + { name: "Branding", icon: }, + { name: "Informational", icon: }, + ]; + + const { selectedSubCategory, setSelectedSubCategory } = useDecalStore(); + return ( +
+ +
+
+ {(() => { + if (isLoading) { + return ; // Show skeleton when loading } - }; - filteredAssets(); - }, [categoryAssets]); + if (searchValue) { + return ( +
+
+
+

Results for {searchValue}

+
+
+ {categoryAssets?.map((asset: any, index: number) => ( +
+ {asset.filename} { + 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 }, - ]); - }, []); +
+ {asset.filename + .split("_") + .map( + (word: any) => + word.charAt(0).toUpperCase() + word.slice(1) + ) + .join(" ")} +
+
+ ))} +
+
+
+ ); + } - 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 ( +
+

+ {selectedCategory} + +

+ {selectedCategory === "Decals" && ( + <> +
+ {activeSubcategories.map((cat, index) => ( +
setSelectedSubCategory(cat.name)} + > +
{cat.icon}
+
{cat.name}
+
+ ))} +
+ + )} +
+ {categoryAssets?.map((asset: any, index: number) => ( +
+ {asset.filename} { + setSelectedItem({ + name: asset.filename, + id: asset.AssetID, + type: + asset.type === "undefined" + ? undefined + : asset.type, + category: asset.category, + subType: asset.subType, + }); + }} + /> +
+ {asset.filename + .split("_") + .map( + (word: any) => + word.charAt(0).toUpperCase() + word.slice(1) + ) + .join(" ")} +
+
+ ))} + {categoryAssets.length === 0 && ( +
+ 🚧 The asset shelf is empty. We're working on filling it + up! +
+ )} +
+
+ ); + } - const activeSubcategories = [ - { name: "Safety", icon: }, - { name: "Navigation", icon: }, - { name: "Branding", icon: }, - { name: "Informational", icon: } - ] - - - const { selectedSubCategory, setSelectedSubCategory } = useDecalStore(); - return ( -
- -
-
- {(() => { - if (isLoading) { - return ; // Show skeleton when loading - } - if (searchValue) { - return ( -
-
-
-

Results for {searchValue}

-
-
- {categoryAssets?.map((asset: any, index: number) => ( -
- {asset.filename} { - setSelectedItem({ - name: asset.filename, - id: asset.AssetID, - type: asset.type === "undefined" ? undefined : asset.type - }); - }} - /> - -
- {asset.filename - .split("_") - .map( - (word: any) => - word.charAt(0).toUpperCase() + word.slice(1) - ) - .join(" ")} -
-
- ))} -
-
-
- ); - } - - if (selectedCategory) { - return ( -
-

- {selectedCategory} - -

- - {selectedCategory === "Decals" && - <> -
- {activeSubcategories.map((cat, index) => ( -
setSelectedSubCategory(cat.name)}> -
{cat.icon}
-
{cat.name}
-
- ))} -
- - } -
- {categoryAssets?.map((asset: any, index: number) => ( -
- {asset.filename} { - setSelectedItem({ - name: asset.filename, - id: asset.AssetID, - type: asset.type === "undefined" ? undefined : asset.type, - category: asset.category, - subType: asset.subType - }); - }} - /> -
- {asset.filename.split("_").map((word: any) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ")} -
-
- ))} - {categoryAssets.length === 0 && ( -
- 🚧 The asset shelf is empty. We're working on filling it up! -
- )} -
-
- ); - } - - return ( -
-

Categories

-
- {Array.from( - new Set(categoryList.map((asset) => asset.category)) - ).map((category, index) => { - const categoryInfo = categoryList.find( - (asset) => asset.category === category - ); - return ( -
fetchCategoryAssets(category)} - > - {category} -
{category}
-
- ); - })} -
-
- ); - })()} -
-
-
- ); + return ( +
+

Categories

+
+ {Array.from( + new Set(categoryList.map((asset) => asset.category)) + ).map((category, index) => { + const categoryInfo = categoryList.find( + (asset) => asset.category === category + ); + return ( +
fetchCategoryAssets(category)} + > + {category} +
{category}
+
+ ); + })} +
+
+ ); + })()} +
+
+
+ ); }; export default Assets; diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index bb4e137..0041aaf 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -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("none"); + const [displayComponent, setDisplayComponent] = + useState("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 ; + case "globalProperties": + return ; + case "aisleProperties": + return ; + case "wallProperties": + return ; + case "floorProperties": + return ; + case "assetProperties": + return ; + case "selectedWallProperties": + return ; + case "selectedFloorProperties": + return ; + case "zoneProperties": + return ; + case "simulations": + return ; + case "mechanics": + return ; + case "analysis": + return ; + case "visualization": + return ; + case "selectedDecalProperties": + return ; + case "resourceManagement": + return ; + 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 ; - case "globalProperties": - return ; - case "aisleProperties": - return ; - case "wallProperties": - return ; - case "floorProperties": - return ; - case "assetProperties": - return ; - case "selectedWallProperties": - return ; - case "selectedFloorProperties": - return ; - case "zoneProperties": - return ; - case "simulations": - return ; - case "mechanics": - return ; - case "analysis": - return ; - case "visualization": - return ; - case "decals": - return ; - case "resourceManagement": - return ; - default: - return null; - } - }; - - return ( -
-
- {toggleUIRight && ( + return ( +
+
+ {toggleUIRight && ( + <> + {(!isVersionSaved || activeModule !== "simulation") && ( +
+ {activeModule !== "simulation" && ( <> - {(!isVersionSaved || activeModule !== "simulation") && ( -
- - {activeModule !== "simulation" && ( - <> - - - )} - - {activeModule === "simulation" && ( - <> - - - - - )} - - {(activeModule === "builder" || activeModule === "simulation") && ( - - )} - -
- )} - - {displayComponent !== "none" && ( -
-
- {renderComponent()} - {/* */} -
-
- )} + - )} -
- ); + )} + + {activeModule === "simulation" && ( + <> + + + + + )} + + {(activeModule === "builder" || + activeModule === "simulation") && ( + + )} +
+ )} + + {displayComponent !== "none" && ( +
+
+ {renderComponent()} + {/* */} +
+
+ )} + + )} +
+ ); }; -export default SideBarRight; \ No newline at end of file +export default SideBarRight; diff --git a/app/src/components/layout/sidebarRight/customInput/Vector3Input.tsx b/app/src/components/layout/sidebarRight/customInput/Vector3Input.tsx index 05fee8c..cc473c2 100644 --- a/app/src/components/layout/sidebarRight/customInput/Vector3Input.tsx +++ b/app/src/components/layout/sidebarRight/customInput/Vector3Input.tsx @@ -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 = ({ 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 = ({ handleChange(i, e.target.value)} + value={value?.[i] !== undefined ? value[i].toFixed(1) : ""} + onChange={(e) => handleChange(i, e.target.value)} placeholder={placeholder} disabled={disabled} /> diff --git a/app/src/components/layout/sidebarRight/decals/DecalTransformation.tsx b/app/src/components/layout/sidebarRight/decals/DecalTransformation.tsx deleted file mode 100644 index 39a30d7..0000000 --- a/app/src/components/layout/sidebarRight/decals/DecalTransformation.tsx +++ /dev/null @@ -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 ( -
- {["position", "rotation", "scale"].map((transformation) => ( -
-
{transformation}
-
- -
- -
- -
-
-
-
- ))} - -
-
opacity
-
- -
- -
-
- -
-
Layering
- -
-
- -
-
- -
-
-
- -
- -
replace
-
-
- ); -}; - -export default DecalTransformation; - diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index 7d50298..4f76584 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -103,32 +103,32 @@ const AssetProperties: React.FC = () => {
Animations
- {assets.map((asset) => ( - <> - {asset.modelUuid === selectedFloorItem.uuid && - asset.animations && - asset.animations.length > 0 && - asset.animations.map((animation, index) => ( -
-
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()} -
-
- ))} - - ))} + {assets.map((asset) => { + if (asset.modelUuid !== selectedFloorItem.uuid || !asset.animations) + return null; + + return asset.animations.map((animation, index) => ( +
+
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()} +
+
+ )); + })}
); diff --git a/app/src/components/layout/sidebarRight/properties/DecalProperties.tsx b/app/src/components/layout/sidebarRight/properties/DecalProperties.tsx new file mode 100644 index 0000000..4163ea5 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/DecalProperties.tsx @@ -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 ( +
+
Decal Propertis
+
+ {}} + value={10} + /> + console.log(value)} + header="Scale" + value={[0, 0, 0] as [number, number, number]} + /> +
+ +
+ console.log(value)} + key={"6"} + /> + +
+
Layering
+ +
+ + +
+
+
+
+ ); +}; + +export default DecalProperties; diff --git a/app/src/components/ui/inputs/InputRange.tsx b/app/src/components/ui/inputs/InputRange.tsx index 1a2f6f5..372edaf 100644 --- a/app/src/components/ui/inputs/InputRange.tsx +++ b/app/src/components/ui/inputs/InputRange.tsx @@ -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 = ({ onChange, min, max, + step = 1, // default step disabled, value, onPointerUp, }) => { - const [rangeValue, setRangeValue] = useState(value ? value : 5); + const [rangeValue, setRangeValue] = useState(value ?? 5); function handleChange(e: React.ChangeEvent) { - 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) { - 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) { - 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) { + const newValue = parseFloat(e.currentTarget.value); + if (onPointerUp) onPointerUp(newValue); } return (
-