diff --git a/app/src/components/Dashboard/SidePannel.tsx b/app/src/components/Dashboard/SidePannel.tsx index f38f0bc..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>; @@ -38,11 +37,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,7 +60,7 @@ 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") { @@ -88,7 +89,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 +164,14 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { Settings -
{ - localStorage.clear(); - navigate("/"); - }}> +
{ + localStorage.clear(); + navigate("/"); + }} + > Log out
@@ -179,4 +185,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..09d4537 100644 --- a/app/src/components/forgotPassword/OTPInput.tsx +++ b/app/src/components/forgotPassword/OTPInput.tsx @@ -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(Array(length).fill('')); +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 +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, 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 +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)} 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/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 (
-