This commit is contained in:
Vishnu 2025-05-07 08:58:07 +05:30
commit 83f9c660b1
18 changed files with 475 additions and 233 deletions

View File

@ -18,6 +18,7 @@ import safety from "../../../assets/image/categories/safety.png";
import feneration from "../../../assets/image/categories/feneration.png"; import feneration from "../../../assets/image/categories/feneration.png";
import archThumbnail from "../../../assets/image/localAssets/arch.png"; import archThumbnail from "../../../assets/image/localAssets/arch.png";
import windowThumbnail from "../../../assets/image/localAssets/window.png"; import windowThumbnail from "../../../assets/image/localAssets/window.png";
import SkeletonUI from "../../templates/SkeletonUI";
// ------------------------------------- // -------------------------------------
interface AssetProp { interface AssetProp {
@ -46,6 +47,7 @@ const Assets: React.FC = () => {
const [categoryAssets, setCategoryAssets] = useState<AssetProp[]>([]); const [categoryAssets, setCategoryAssets] = useState<AssetProp[]>([]);
const [filtereredAssets, setFiltereredAssets] = useState<AssetProp[]>([]); const [filtereredAssets, setFiltereredAssets] = useState<AssetProp[]>([]);
const [categoryList, setCategoryList] = useState<CategoryListProp[]>([]); const [categoryList, setCategoryList] = useState<CategoryListProp[]>([]);
const [isLoading, setisLoading] = useState<boolean>(false); // Loading state for assets
const handleSearchChange = (value: string) => { const handleSearchChange = (value: string) => {
const searchTerm = value.toLowerCase(); const searchTerm = value.toLowerCase();
@ -73,6 +75,7 @@ const Assets: React.FC = () => {
setCategoryAssets(filteredModels); setCategoryAssets(filteredModels);
}; };
useEffect(() => { useEffect(() => {
const filteredAssets = async () => { const filteredAssets = async () => {
try { try {
@ -112,7 +115,9 @@ const Assets: React.FC = () => {
{ category: "Office", categoryImage: office }, { category: "Office", categoryImage: office },
]); ]);
}, []); }, []);
const fetchCategoryAssets = async (asset: any) => { const fetchCategoryAssets = async (asset: any) => {
setisLoading(true);
setSelectedCategory(asset); setSelectedCategory(asset);
if (asset === "Feneration") { if (asset === "Feneration") {
const localAssets: AssetProp[] = [ const localAssets: AssetProp[] = [
@ -140,13 +145,15 @@ const Assets: React.FC = () => {
]; ];
setCategoryAssets(localAssets); setCategoryAssets(localAssets);
setFiltereredAssets(localAssets); setFiltereredAssets(localAssets);
setisLoading(false);
} else { } else {
try { try {
const res = await getCategoryAsset(asset); const res = await getCategoryAsset(asset);
setCategoryAssets(res); setCategoryAssets(res);
setFiltereredAssets(res); setFiltereredAssets(res);
setisLoading(false); // End loading
} catch (error) { } catch (error) {
console.log(error); setisLoading(false);
} }
} }
}; };
@ -155,7 +162,9 @@ const Assets: React.FC = () => {
<Search onChange={handleSearchChange} /> <Search onChange={handleSearchChange} />
<div className="assets-list-section"> <div className="assets-list-section">
<section> <section>
{searchValue ? ( {isLoading ? (
<SkeletonUI type="asset" /> // Show skeleton when loading
) : searchValue ? (
<div className="assets-result"> <div className="assets-result">
<div className="assets-wrapper"> <div className="assets-wrapper">
<div className="searched-content"> <div className="searched-content">

View File

@ -31,10 +31,10 @@ const SideBarLeft: React.FC = () => {
}; };
return ( return (
<div className="sidebar-left-wrapper"> <div className={`sidebar-left-wrapper ${toggleUI ? "open" : "closed"}`}>
<Header /> <Header />
{toggleUI && ( {toggleUI && (
<div className="sidebar-left-container"> <div className={`sidebar-left-container `}>
{activeModule === "visualization" ? ( {activeModule === "visualization" ? (
<> <>
<ToggleHeader <ToggleHeader
@ -79,3 +79,5 @@ const SideBarLeft: React.FC = () => {
}; };
export default SideBarLeft; export default SideBarLeft;
// sidebar-left-container opemn close sidebar-left-container smoothly

View File

@ -55,7 +55,7 @@ const SideBarRight: React.FC = () => {
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]); }, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
return ( return (
<div className="sidebar-right-wrapper"> <div className={`sidebar-right-wrapper ${toggleUI ? "open" : "closed"}`}>
<Header /> <Header />
{toggleUI && ( {toggleUI && (
<div className="sidebar-actions-container"> <div className="sidebar-actions-container">

View File

@ -1,21 +1,70 @@
import React from "react"; import React from "react";
// Define the prop types
interface SkeletonUIProps {
type: "asset" | "assetLibrary" | "assetWidget" | "default"; // You can expand this with other types as needed
}
const SkeletonUI = () => { // Define the SkeletonUI component
return ( const SkeletonUI: React.FC<SkeletonUIProps> = ({ type }) => {
<div className="skeleton-wrapper"> console.log("type: ", type);
<div className="skeleton-header">
<div className="skeleton skeleton-title"></div>
<div className="skeleton skeleton-subtitle"></div>
</div>
<div className="skeleton-content"> // Function to render skeleton content based on 'type'
<div className="skeleton skeleton-card"></div> const renderSkeleton = () => {
<div className="skeleton skeleton-card"></div> switch (type) {
</div> case "assetLibrary":
return (
</div> <>
); {Array(5)
.fill(null) // Create an array of 5 empty items
.map((_, index) => (
<div key={index} className="skeleton-content">
<div className="skeleton asset-image"></div>
<div className="skeleton asset-details"></div>
<div className="skeleton organization"></div>
<div className="skeleton asset-review"></div>
<div className="skeleton button"></div>
</div>
))}
</>
);
case "assetWidget":
return (
<div className="skeleton-content">
<div className="skeleton skeleton-widget"></div>
<div className="skeleton skeleton-widget"></div>
</div>
);
case "asset":
return (
<>
<div className="skeleton-content">
<div className="skeleton asset-name"></div>
<div className="skeleton asset"></div>
</div>
<div className="skeleton-content">
<div className="skeleton asset-name"></div>
<div className="skeleton asset"></div>
</div>
</>
);
default:
return (
<div className="skeleton-content">
<div className="skeleton-header">
<div className="skeleton skeleton-title"></div>
<div className="skeleton skeleton-subtitle"></div>
</div>
<div className="skeleton skeleton-card"></div>
<div className="skeleton skeleton-card"></div>
</div>
);
}
};
return <div className="skeleton-wrapper">{renderSkeleton()}</div>;
}; };
export default SkeletonUI; export default SkeletonUI;

View File

@ -73,7 +73,7 @@ const ThroughputSummary = () => {
}, },
}; };
const isLoading = true; const isLoading = false;
return ( return (
<div className="production analysis-card"> <div className="production analysis-card">
@ -90,7 +90,7 @@ const ThroughputSummary = () => {
</div> </div>
</div> </div>
{isLoading ? ( {!isLoading ? (
<> <>
<div className="process-container"> <div className="process-container">
<div className="throughput-value"> <div className="throughput-value">
@ -157,7 +157,7 @@ const ThroughputSummary = () => {
</div> </div>
</> </>
) : ( ) : (
<SkeletonUI /> <SkeletonUI type={"default"} />
)} )}
</div> </div>
</div> </div>

View File

@ -8,56 +8,58 @@ import {
} from "../../icons/analysis"; } from "../../icons/analysis";
import SemiCircleProgress from "./SemiCircleProgress"; import SemiCircleProgress from "./SemiCircleProgress";
import { ArrowIcon } from "../../icons/ExportCommonIcons"; import { ArrowIcon } from "../../icons/ExportCommonIcons";
import SkeletonUI from "../../templates/SkeletonUI";
const ROISummary = ({ const ROISummary = ({
roiSummaryData = { roiSummaryData = {
productName: "Product name", productName: "Product name",
roiPercentage: 133, roiPercentage: 133,
paybackPeriod: 50.3, paybackPeriod: 50.3,
totalCost: "₹ 1,20,000", totalCost: "1,20,000",
revenueGenerated: "₹ 2,80,000", revenueGenerated: "2,80,000",
netProfit: "₹ 1,60,000", netProfit: "1,60,000",
netLoss: null,
costBreakdown: [ costBreakdown: [
{ {
item: "Raw Material A", item: "Raw Material A",
unitCost: "10/unit", unitCost: "10/unit",
laborCost: "0", laborCost: "0",
totalCost: "1000", totalCost: "1000",
sellingPrice: "1500", sellingPrice: "1500",
}, },
{ {
item: "Labor", item: "Labor",
unitCost: "10/unit", unitCost: "10/unit",
laborCost: "500", laborCost: "500",
totalCost: "500", totalCost: "500",
sellingPrice: "N/A", sellingPrice: "N/A",
}, },
{ {
item: "Product 1", item: "Product 1",
unitCost: "10/unit", unitCost: "10/unit",
laborCost: "200", laborCost: "200",
totalCost: "200", totalCost: "200",
sellingPrice: "2000", sellingPrice: "2000",
}, },
{ {
item: "Machine", item: "Machine",
unitCost: "-", unitCost: "-",
laborCost: "-", laborCost: "-",
totalCost: "20,000", totalCost: "20,000",
sellingPrice: "N/A", sellingPrice: "N/A",
}, },
{ {
item: "Total", item: "Total",
unitCost: "-", unitCost: "-",
laborCost: "-", laborCost: "-",
totalCost: "1,20,000", totalCost: "1,20,000",
sellingPrice: "-", sellingPrice: "-",
}, },
{ {
item: "Net Profit", item: "Net Profit",
unitCost: "-", unitCost: "-",
laborCost: "-", laborCost: "-",
totalCost: "1,60,000", totalCost: "1,60,000",
sellingPrice: "-", sellingPrice: "-",
}, },
], ],
@ -70,6 +72,7 @@ const ROISummary = ({
setIsTableOpen(!isTableOpen); setIsTableOpen(!isTableOpen);
}; };
const isLoading = false;
return ( return (
<div className="roiSummary-container analysis-card"> <div className="roiSummary-container analysis-card">
<div className="roiSummary-wrapper analysis-card-wrapper"> <div className="roiSummary-wrapper analysis-card-wrapper">
@ -82,121 +85,140 @@ const ROISummary = ({
<ROISummaryIcon /> <ROISummaryIcon />
</div> </div>
</div> </div>
<div className="product-info"> {!isLoading ? (
<ROISummaryProductName /> <>
<div className="product-label">Product :</div> <div className="product-info">
<div className="product-name">{roiSummaryData.productName}</div> <ROISummaryProductName />
</div> <div className="product-label">Product :</div>
<div className="playBack"> <div className="product-name">{roiSummaryData.productName}</div>
<SonarCrownIcon />
<div className="icon"></div>
<div className="info">
<span>+{roiSummaryData.roiPercentage}%</span> ROI with payback in
just <span>{roiSummaryData.paybackPeriod}</span> months
</div>
</div>
<div className="roi-details">
<div className="progress-wrapper">
<SemiCircleProgress />
<div className="content">
you're on track to hit it by
<div className="key">July 2029</div>
</div> </div>
</div> <div className="playBack">
<div className="metrics"> <SonarCrownIcon />
<div className="metric-wrapper"> <div className="icon"></div>
<div className="metric-item"> <div className="info">
<span className="metric-label">Total Cost Incurred</span> <span>+{roiSummaryData.roiPercentage}%</span> ROI with payback
<span className="metric-value">{roiSummaryData.totalCost}</span> in just <span>{roiSummaryData.paybackPeriod}</span> months
</div> </div>
<div className="metric-item"> </div>
<span className="metric-label">Revenue Generated</span> <div className="roi-details">
<span className="metric-value"> <div className="progress-wrapper">
{roiSummaryData.revenueGenerated} <SemiCircleProgress />
<div className="content">
you're on track to hit it by
<div className="key">July 2029</div>
</div>
</div>
<div className="metrics">
<div className="metric-wrapper">
<div className="metric-item">
<span className="metric-label">Total Cost Incurred</span>
<span className="metric-value">
<span></span>
{roiSummaryData.totalCost}
</span>
</div>
<div className="metric-item">
<span className="metric-label">Revenue Generated</span>
<span className="metric-value">
<span></span>
{roiSummaryData.revenueGenerated}
</span>
</div>
</div>
<div
className={`metric-item net-profit ${
roiSummaryData.netLoss ?? "loss"
}`}
>
<div className="metric-label">
<span></span>
Net Profit
</div>
<div className="metric-value">
<span></span>
{roiSummaryData.netProfit}
</div>
</div>
</div>
</div>
<div className="cost-breakdown">
<div className="breakdown-header" onClick={toggleTable}>
<div className="section-wrapper">
<CostBreakDownIcon />
<span className="section-title">Cost Breakdown</span>
</div>
<span className={`expand-icon ${isTableOpen ? "open" : ""}`}>
<ArrowIcon />
</span> </span>
</div> </div>
<div
className={`breakdown-table-wrapper ${
isTableOpen ? "open" : "closed"
}`}
style={{
transition: "max-height 0.3s ease-in-out",
overflow: "hidden",
}}
>
<table className="breakdown-table">
<thead>
<tr>
<th>Item</th>
<th>Unit Cost</th>
<th>Labor Cost</th>
<th>Total Cost</th>
<th>Selling Price</th>
</tr>
</thead>
<tbody>
{roiSummaryData.costBreakdown.map((row, index) => (
<tr
key={index}
className={
row.item === "Total"
? "total-row"
: row.item === "Net Profit"
? "net-profit-row"
: ""
}
>
<td>{row.item}</td>
<td>{row.unitCost}</td>
<td>{row.laborCost}</td>
<td>{row.totalCost}</td>
<td>{row.sellingPrice}</td>
</tr>
))}
</tbody>
</table>
</div>
</div> </div>
<div className="metric-item net-profit"> <div className="tips-section">
<span className="metric-label"> <div className="tip-header">
<span></span> Net Profit <span className="lightbulb-icon">
</span> <LightBulpIcon />
<span className="metric-value">{roiSummaryData.netProfit}</span> </span>
<span className="tip-title">How to improve ROI?</span>
</div>
<div className="tip-description">
Increase CNC utilization by{" "}
<span className="highlight">10%</span> to shave{" "}
<span className="highlight">0.5</span> months of payback period
<div className="placeHolder-wrapper">
<div className="placeHolder"></div>
<div className="placeHolder"></div>
</div>
</div>
<button className="get-tips-button">
<div className="btn">Get ROI Boost Tips</div>
</button>
</div> </div>
</div> </>
</div> ) : (
<div className="cost-breakdown"> <SkeletonUI type={"default"} />
<div className="breakdown-header" onClick={toggleTable}> )}
<div className="section-wrapper">
<CostBreakDownIcon />
<span className="section-title">Cost Breakdown</span>
</div>
<span className={`expand-icon ${isTableOpen ? "open" : ""}`}>
<ArrowIcon />
</span>
</div>
<div
className={`breakdown-table-wrapper ${
isTableOpen ? "open" : "closed"
}`}
style={{
transition: "max-height 0.3s ease-in-out",
overflow: "hidden",
}}
>
<table className="breakdown-table">
<thead>
<tr>
<th>Item</th>
<th>Unit Cost</th>
<th>Labor Cost</th>
<th>Total Cost</th>
<th>Selling Price</th>
</tr>
</thead>
<tbody>
{roiSummaryData.costBreakdown.map((row, index) => (
<tr
key={index}
className={
row.item === "Total"
? "total-row"
: row.item === "Net Profit"
? "net-profit-row"
: ""
}
>
<td>{row.item}</td>
<td>{row.unitCost}</td>
<td>{row.laborCost}</td>
<td>{row.totalCost}</td>
<td>{row.sellingPrice}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div className="tips-section">
<div className="tip-header">
<span className="lightbulb-icon">
<LightBulpIcon />
</span>
<span className="tip-title">How to improve ROI?</span>
</div>
<div className="tip-description">
Increase CNC utilization by <span className="highlight">10%</span>{" "}
to shave <span className="highlight">0.5</span> months of payback
period
<div className="placeHolder-wrapper">
<div className="placeHolder"></div>
<div className="placeHolder"></div>
</div>
</div>
<button className="get-tips-button">
<div className="btn">Get ROI Boost Tips</div>
</button>
</div>
</div> </div>
</div> </div>
); );

View File

@ -2,6 +2,7 @@ import {
ProductionCapacityIcon, ProductionCapacityIcon,
ThroughputSummaryIcon, ThroughputSummaryIcon,
} from "../../icons/analysis"; } from "../../icons/analysis";
import SkeletonUI from "../../templates/SkeletonUI";
const ProductionCapacity = ({ const ProductionCapacity = ({
progressPercent = 50, progressPercent = 50,
@ -15,6 +16,7 @@ const ProductionCapacity = ({
const partialFillPercent = const partialFillPercent =
((progressPercent / 100) * totalBars - barsToFill) * 100; ((progressPercent / 100) * totalBars - barsToFill) * 100;
const isLoading = false;
return ( return (
<div className="throughtputSummary-container analysis-card"> <div className="throughtputSummary-container analysis-card">
<div className="throughtputSummary-wrapper analysis-card-wrapper"> <div className="throughtputSummary-wrapper analysis-card-wrapper">
@ -29,39 +31,44 @@ const ProductionCapacity = ({
<ThroughputSummaryIcon /> <ThroughputSummaryIcon />
</div> </div>
</div> </div>
{isLoading ? (
<div className="process-container"> <>
<div className="throughput-value"> <div className="process-container">
<span className="value">{throughputValue}</span> Units/hour <div className="throughput-value">
</div> <span className="value">{throughputValue}</span> Units/hour
{/* Dynamic Progress Bar */}
<div className="progress-bar-wrapper">
{[...Array(totalBars)].map((_, i) => (
<div className="progress-bar" key={i}>
{i < barsToFill ? (
<div className="bar-fill full" />
) : i === barsToFill ? (
<div
className="bar-fill partial"
style={{ width: `${partialFillPercent}%` }}
/>
) : null}
</div> </div>
))}
</div>
</div>
<div className="metrics-section"> {/* Dynamic Progress Bar */}
<div className="metric"> <div className="progress-bar-wrapper">
<span className="label">Avg. Process Time</span> {[...Array(totalBars)].map((_, i) => (
<span className="value">{avgProcessTime}</span> <div className="progress-bar" key={i}>
</div> {i < barsToFill ? (
<div className="metric"> <div className="bar-fill full" />
<span className="label">Machine Utilization</span> ) : i === barsToFill ? (
<span className="value">{machineUtilization}</span> <div
</div> className="bar-fill partial"
</div> style={{ width: `${partialFillPercent}%` }}
/>
) : null}
</div>
))}
</div>
</div>
<div className="metrics-section">
<div className="metric">
<span className="label">Avg. Process Time</span>
<span className="value">{avgProcessTime}</span>
</div>
<div className="metric">
<span className="label">Machine Utilization</span>
<span className="value">{machineUtilization}</span>
</div>
</div>
</>
) : (
<SkeletonUI type={"default"} />
)}
</div> </div>
</div> </div>
); );

View File

@ -1,4 +1,5 @@
import React, { createContext, useContext, useState, useCallback, useMemo } from "react"; // LoggerProvider.tsx
import React, { createContext, useContext, useState, useCallback, useMemo, useEffect } from "react";
import { MathUtils } from "three"; import { MathUtils } from "three";
export type LogType = "log" | "info" | "warning" | "error" | "success"; export type LogType = "log" | "info" | "warning" | "error" | "success";
@ -60,6 +61,18 @@ export const LoggerProvider: React.FC<{ children: React.ReactNode }> = ({
[logs, setLogs, isLogListVisible, setIsLogListVisible, addLog] [logs, setLogs, isLogListVisible, setIsLogListVisible, addLog]
); );
// Attach logger globally to window object
useEffect(() => {
(window as any).echo = {
log: loggerMethods.log,
info: loggerMethods.info,
warn: loggerMethods.warn,
error: loggerMethods.error,
success: loggerMethods.success,
clear: loggerMethods.clear,
};
}, [loggerMethods]);
return ( return (
<LoggerContext.Provider value={loggerMethods}> <LoggerContext.Provider value={loggerMethods}>
{children} {children}

View File

@ -1,10 +1,8 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
// import RegularDropDown from "./ui/inputs/RegularDropDown";
import Search from "../../components/ui/inputs/Search"; import Search from "../../components/ui/inputs/Search";
import { StarsIcon } from "../../components/icons/marketPlaceIcons"; import { StarsIcon } from "../../components/icons/marketPlaceIcons";
import RegularDropDown from "../../components/ui/inputs/RegularDropDown"; import RegularDropDown from "../../components/ui/inputs/RegularDropDown";
import { getSortedAssets } from "../../services/marketplace/getSortedAssets";
interface ModelData { interface ModelData {
CreatedBy: string; CreatedBy: string;
animated: string | null; animated: string | null;
@ -19,47 +17,46 @@ interface ModelData {
_id: string; _id: string;
price: number; price: number;
} }
interface ModelsProps { interface ModelsProps {
models: ModelData[]; models: ModelData[];
setModels: React.Dispatch<React.SetStateAction<ModelData[]>>; setModels: React.Dispatch<React.SetStateAction<ModelData[]>>;
filteredModels: ModelData[]; filteredModels: ModelData[];
} }
const FilterSearch: React.FC<ModelsProps> = ({ const FilterSearch: React.FC<ModelsProps> = ({
models, models,
setModels, setModels,
filteredModels, filteredModels,
}) => { }) => {
const [activeOption, setActiveOption] = useState("Sort by"); // State for active option const [activeOption, setActiveOption] = useState("Sort by");
const [rating, setRating] = useState(0);
const handleSelect = (option: string) => { const handleSelect = (option: string) => {
setActiveOption(option); setActiveOption(option);
// Alphabet ascending
// Alphabet descending
// All
}; };
useEffect(() => { useEffect(() => {
if (activeOption == "Alphabet ascending") { if (activeOption === "Alphabet ascending") {
let ascending = models const ascending = [...models].sort((a, b) => a.filename.localeCompare(b.filename));
?.slice()
.sort((a, b) => a.filename.localeCompare(b.filename))
.map((val) => val);
setModels(ascending); setModels(ascending);
} else if (activeOption == "Alphabet descending") { } else if (activeOption === "Alphabet descending") {
let descending = models const descending = [...models].sort((a, b) => b.filename.localeCompare(a.filename));
?.slice()
.sort((a, b) => b.filename.localeCompare(a.filename))
.map((val) => val);
setModels(descending); setModels(descending);
} }
}, [activeOption]); }, [activeOption]);
const handleSearch = (val: string) => { const handleSearch = (val: string) => {
const filteredModel = filteredModels?.filter((model) => const filteredModel = filteredModels.filter((model) =>
model.filename.toLowerCase().includes(val.toLowerCase()) model.filename.toLowerCase().includes(val.toLowerCase())
); );
setModels(filteredModel); setModels(filteredModel);
}; };
const handleStarClick = (index: number) => {
setRating(index + 1);
};
return ( return (
<div className="filter-search-container"> <div className="filter-search-container">
<Search onChange={handleSearch} /> <Search onChange={handleSearch} />
@ -71,14 +68,19 @@ const FilterSearch: React.FC<ModelsProps> = ({
/> />
<div className="button">Free</div> <div className="button">Free</div>
<div className="button">Animated</div> <div className="button">Animated</div>
<div className="rating-container"> <div className="rating-container">
<div className="label">Rating</div> <div className="label">Rating</div>
<div className="stars"> <div className="stars">
<StarsIcon /> {[0, 1, 2, 3, 4].map((i) => (
<StarsIcon /> <div
<StarsIcon /> key={i}
<StarsIcon /> onClick={() => handleStarClick(i)}
<StarsIcon /> className={`star-wrapper ${i < rating ? "filled" : "empty"}`}
>
<StarsIcon />
</div>
))}
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,6 +3,7 @@ import FilterSearch from "./FilterSearch";
import CardsContainer from "./CardsContainer"; import CardsContainer from "./CardsContainer";
import { fetchAssets } from "../../services/marketplace/fetchAssets"; import { fetchAssets } from "../../services/marketplace/fetchAssets";
import { getAssetImages } from "../../services/factoryBuilder/assest/assets/getAssetImages"; import { getAssetImages } from "../../services/factoryBuilder/assest/assets/getAssetImages";
import SkeletonUI from "../../components/templates/SkeletonUI";
interface ModelData { interface ModelData {
CreatedBy: string; CreatedBy: string;
animated: string | null; animated: string | null;
@ -20,14 +21,19 @@ interface ModelData {
const MarketPlace = () => { const MarketPlace = () => {
const [models, setModels] = useState<ModelData[]>([]); const [models, setModels] = useState<ModelData[]>([]);
const [filteredModels, setFilteredModels] = useState<ModelData[]>([]); const [filteredModels, setFilteredModels] = useState<ModelData[]>([]);
const [isLoading, setisLoading] = useState<boolean>(false); // Loading state
useEffect(() => { useEffect(() => {
const filteredAssets = async () => { const filteredAssets = async () => {
setisLoading(true);
try { try {
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6"); const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
setModels(filt.items); setModels(filt.items);
setFilteredModels(filt.items); setFilteredModels(filt.items);
} catch {} setisLoading(false);
} catch {
setisLoading(false);
}
}; };
filteredAssets(); filteredAssets();
}, []); }, []);
@ -36,12 +42,18 @@ const MarketPlace = () => {
<div className="marketplace-wrapper"> <div className="marketplace-wrapper">
<div className="marketplace-container"> <div className="marketplace-container">
<div className="marketPlace"> <div className="marketPlace">
<FilterSearch {isLoading ? (
models={models} <SkeletonUI type="assetLibrary" /> // Show loading spinner while fetching
setModels={setModels} ) : (
filteredModels={filteredModels} <>
/> <FilterSearch
<CardsContainer models={models} /> models={models}
setModels={setModels}
filteredModels={filteredModels}
/>
<CardsContainer models={models} />
</>
)}{" "}
</div> </div>
</div> </div>
</div> </div>

View File

@ -99,7 +99,7 @@ export const DraggableWidget = ({
const deleteSelectedChart = async () => { const deleteSelectedChart = async () => {
try { try {
console.log("delete"); console.log("delete");
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
let deleteWidget = { let deleteWidget = {
@ -109,9 +109,9 @@ export const DraggableWidget = ({
}; };
if (visualizationSocket) { if (visualizationSocket) {
setSelectedChartId(null) setSelectedChartId(null);
visualizationSocket.emit("v2:viz-widget:delete", deleteWidget); visualizationSocket.emit("v2:viz-widget:delete", deleteWidget);
console.log("delete widget",selectedChartId); console.log("delete widget", selectedChartId);
} }
const updatedWidgets = selectedZone.widgets.filter( const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id (w: Widget) => w.id !== widget.id
@ -176,7 +176,7 @@ export const DraggableWidget = ({
const duplicatedWidget: Widget = { const duplicatedWidget: Widget = {
...widget, ...widget,
title: name === '' ? widget.title : name, title: name === "" ? widget.title : name,
Data: { Data: {
duration: duration, duration: duration,
measurements: { ...measurements }, measurements: { ...measurements },
@ -189,7 +189,7 @@ export const DraggableWidget = ({
zoneId: selectedZone.zoneId, zoneId: selectedZone.zoneId,
widget: duplicatedWidget, widget: duplicatedWidget,
}; };
if (visualizationSocket) { if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:add", duplicateWidget); visualizationSocket.emit("v2:viz-widget:add", duplicateWidget);
} }
@ -309,9 +309,9 @@ export const DraggableWidget = ({
: undefined, : undefined,
}} }}
ref={chartWidget} ref={chartWidget}
onClick={() => {setSelectedChartId(widget) onClick={() => {
console.log('click'); setSelectedChartId(widget);
console.log("click");
}} }}
> >
{/* Kebab Icon */} {/* Kebab Icon */}
@ -333,10 +333,13 @@ export const DraggableWidget = ({
</div> </div>
<div className="label">Duplicate</div> <div className="label">Duplicate</div>
</div> </div>
<div className="edit btn" onClick={(e)=>{ <div
e.stopPropagation() className="edit btn"
deleteSelectedChart(); onClick={(e) => {
}}> e.stopPropagation();
deleteSelectedChart();
}}
>
<div className="icon"> <div className="icon">
<DeleteIcon /> <DeleteIcon />
</div> </div>

View File

@ -86,7 +86,7 @@ const Project: React.FC = () => {
{!selectedUser && ( {!selectedUser && (
<> <>
<KeyPressListener /> <KeyPressListener />
{loadingProgress > 0 && <LoadingPage progress={loadingProgress} />} {/* {loadingProgress > 0 && <LoadingPage progress={loadingProgress} />} */}
{!isPlaying && ( {!isPlaying && (
<> <>
{toggleThreeD && <ModuleToggle />} {toggleThreeD && <ModuleToggle />}
@ -122,7 +122,7 @@ const Project: React.FC = () => {
} }
onDragOver={(event) => event.preventDefault()} onDragOver={(event) => event.preventDefault()}
> >
<Scene /> {/* <Scene /> */}
</div> </div>
{selectedUser && <FollowPerson />} {selectedUser && <FollowPerson />}
{isLogListVisible && ( {isLogListVisible && (
@ -130,7 +130,7 @@ const Project: React.FC = () => {
<LogList /> <LogList />
</RenderOverlay> </RenderOverlay>
)} )}
<Footer /> {activeModule != "market" && <Footer />}
</div> </div>
); );
}; };

View File

@ -2,6 +2,9 @@
@use "../../abstracts/mixins.scss" as *; @use "../../abstracts/mixins.scss" as *;
.marketplace-wrapper { .marketplace-wrapper {
// transform: scale(0.65);
/* Start at 90% width */
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
z-index: #{$z-index-marketplace}; z-index: #{$z-index-marketplace};
@ -11,6 +14,7 @@
top: 0; top: 0;
padding: 10px; padding: 10px;
padding-top: 100px; padding-top: 100px;
// animation: growWidth 0.4s ease-in-out 0.5s forwards;
.marketplace-container { .marketplace-container {
position: relative; position: relative;
@ -31,6 +35,58 @@
flex-direction: column; flex-direction: column;
gap: 24px; gap: 24px;
.skeleton-wrapper {
display: flex;
flex-wrap: wrap;
gap: 18px;
margin: 0;
width: 100%;
.skeleton-content {
width: calc(25% - 14px) !important;
height: auto !important;
border-radius: #{$border-radius-xlarge};
padding: 12px;
box-shadow: 0px 2px 10.5px 0px #0000000d;
background: var(--background-color-solid-gradient);
border: 1px solid var(--border-color);
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
gap: 6px;
.asset-image {
width: 100%;
height: 100%;
}
.asset-details {
width: 100%;
height: 33px;
}
.organization {
width: 40%;
height: 15px;
}
.asset-review {
width: 100%;
height: 20px;
}
.button {
width: 100%;
height: 35px;
border-radius: 20px;
}
}
}
.filter-search-container { .filter-search-container {
width: 100%; width: 100%;
display: flex; display: flex;
@ -84,6 +140,13 @@
.stars { .stars {
display: flex; display: flex;
align-items: center; align-items: center;
.star-wrapper.filled {
svg {
fill: #F3A50C;
}
}
} }
} }
} }
@ -226,6 +289,17 @@
} }
} }
@keyframes growWidth {
from {
transform: scale(0.65);
}
to {
transform: scale(1);
}
}
.assetPreview-wrapper { .assetPreview-wrapper {
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -90,7 +90,7 @@
.dropdown-menu { .dropdown-menu {
position: absolute; position: absolute;
top: 0; top: 0;
left: 103%; left: 100%;
background: var(--background-color-solid); background: var(--background-color-solid);
min-width: 220px; min-width: 220px;
border-radius: #{$border-radius-medium}; border-radius: #{$border-radius-medium};
@ -141,7 +141,7 @@
.submenu { .submenu {
position: absolute; position: absolute;
left: 102%; left: 100%;
top: 0; top: 0;
background: var(--background-color-solid); background: var(--background-color-solid);
min-width: 200px; min-width: 200px;

View File

@ -378,12 +378,30 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.metric-label.loss {
background: #6D4343;
border: 1px solid #FF301D
}
.metric-value { .metric-value {
text-align: center; text-align: center;
line-height: 20px; line-height: 20px;
} }
} }
.metric-item.loss {
background: #6D4343;
border: 1px solid #FF301D;
.metric-label {
span {
color: #FF311E;
display: inline-block;
transform: rotate(180deg);
}
}
}
.metric-wrapper { .metric-wrapper {
display: flex; display: flex;
gap: 6px; gap: 6px;

View File

@ -456,7 +456,7 @@
padding: 18px 10px; padding: 18px 10px;
position: relative; position: relative;
z-index: 1; z-index: 1;
top: -32px; // top: -32px;
} }
.barOverflow { .barOverflow {
@ -482,4 +482,3 @@
transition: transform 0.5s ease; transition: transform 0.5s ease;
} }
// progress should be progress {progress}

View File

@ -1392,4 +1392,36 @@
} }
} }
} }
} }
.skeleton-wrapper {
display: flex;
.skeleton-content {}
.asset-name {
width: 40%;
height: 10px;
}
.asset {
width: 100%;
height: 100%;
}
}
.sidebar-left-wrapper,
.sidebar-right-wrapper {
height: calc(50vh + 150px);
overflow-y: hidden;
transition: height 0.4s ease-in-out;
}
.sidebar-left-wrapper.closed,
.sidebar-right-wrapper.closed {
height: 52px;
}

View File

@ -1,5 +1,5 @@
.skeleton-wrapper { .skeleton-wrapper {
max-width: 600px; // max-width: 600px;
margin: 0 auto; margin: 0 auto;
width: 100%; width: 100%;