v2-ui #81

Merged
Vishnu merged 7 commits from v2-ui into main 2025-05-07 03:29:47 +00:00
18 changed files with 475 additions and 233 deletions
Showing only changes of commit 83f9c660b1 - Show all commits

View File

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

View File

@ -31,10 +31,10 @@ const SideBarLeft: React.FC = () => {
};
return (
<div className="sidebar-left-wrapper">
<div className={`sidebar-left-wrapper ${toggleUI ? "open" : "closed"}`}>
<Header />
{toggleUI && (
<div className="sidebar-left-container">
<div className={`sidebar-left-container `}>
{activeModule === "visualization" ? (
<>
<ToggleHeader
@ -79,3 +79,5 @@ const SideBarLeft: React.FC = () => {
};
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]);
return (
<div className="sidebar-right-wrapper">
<div className={`sidebar-right-wrapper ${toggleUI ? "open" : "closed"}`}>
<Header />
{toggleUI && (
<div className="sidebar-actions-container">

View File

@ -1,21 +1,70 @@
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
const SkeletonUI: React.FC<SkeletonUIProps> = ({ type }) => {
console.log("type: ", type);
// Function to render skeleton content based on 'type'
const renderSkeleton = () => {
switch (type) {
case "assetLibrary":
return (
<div className="skeleton-wrapper">
<>
{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-content">
<div className="skeleton skeleton-card"></div>
<div className="skeleton skeleton-card"></div>
</div>
</div>
);
}
};
return <div className="skeleton-wrapper">{renderSkeleton()}</div>;
};
export default SkeletonUI;

View File

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

View File

@ -8,56 +8,58 @@ import {
} from "../../icons/analysis";
import SemiCircleProgress from "./SemiCircleProgress";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
import SkeletonUI from "../../templates/SkeletonUI";
const ROISummary = ({
roiSummaryData = {
productName: "Product name",
roiPercentage: 133,
paybackPeriod: 50.3,
totalCost: "₹ 1,20,000",
revenueGenerated: "₹ 2,80,000",
netProfit: "₹ 1,60,000",
totalCost: "1,20,000",
revenueGenerated: "2,80,000",
netProfit: "1,60,000",
netLoss: null,
costBreakdown: [
{
item: "Raw Material A",
unitCost: "10/unit",
laborCost: "0",
totalCost: "1000",
sellingPrice: "1500",
unitCost: "10/unit",
laborCost: "0",
totalCost: "1000",
sellingPrice: "1500",
},
{
item: "Labor",
unitCost: "10/unit",
laborCost: "500",
totalCost: "500",
unitCost: "10/unit",
laborCost: "500",
totalCost: "500",
sellingPrice: "N/A",
},
{
item: "Product 1",
unitCost: "10/unit",
laborCost: "200",
totalCost: "200",
sellingPrice: "2000",
unitCost: "10/unit",
laborCost: "200",
totalCost: "200",
sellingPrice: "2000",
},
{
item: "Machine",
unitCost: "-",
laborCost: "-",
totalCost: "20,000",
totalCost: "20,000",
sellingPrice: "N/A",
},
{
item: "Total",
unitCost: "-",
laborCost: "-",
totalCost: "1,20,000",
totalCost: "1,20,000",
sellingPrice: "-",
},
{
item: "Net Profit",
unitCost: "-",
laborCost: "-",
totalCost: "1,60,000",
totalCost: "1,60,000",
sellingPrice: "-",
},
],
@ -70,6 +72,7 @@ const ROISummary = ({
setIsTableOpen(!isTableOpen);
};
const isLoading = false;
return (
<div className="roiSummary-container analysis-card">
<div className="roiSummary-wrapper analysis-card-wrapper">
@ -82,6 +85,8 @@ const ROISummary = ({
<ROISummaryIcon />
</div>
</div>
{!isLoading ? (
<>
<div className="product-info">
<ROISummaryProductName />
<div className="product-label">Product :</div>
@ -91,8 +96,8 @@ const ROISummary = ({
<SonarCrownIcon />
<div className="icon"></div>
<div className="info">
<span>+{roiSummaryData.roiPercentage}%</span> ROI with payback in
just <span>{roiSummaryData.paybackPeriod}</span> months
<span>+{roiSummaryData.roiPercentage}%</span> ROI with payback
in just <span>{roiSummaryData.paybackPeriod}</span> months
</div>
</div>
<div className="roi-details">
@ -107,20 +112,33 @@ const ROISummary = ({
<div className="metric-wrapper">
<div className="metric-item">
<span className="metric-label">Total Cost Incurred</span>
<span className="metric-value">{roiSummaryData.totalCost}</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">
<span className="metric-label">
<span></span> Net Profit
</span>
<span className="metric-value">{roiSummaryData.netProfit}</span>
<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>
@ -185,9 +203,9 @@ const ROISummary = ({
<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
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>
@ -197,6 +215,10 @@ const ROISummary = ({
<div className="btn">Get ROI Boost Tips</div>
</button>
</div>
</>
) : (
<SkeletonUI type={"default"} />
)}
</div>
</div>
);

View File

@ -2,6 +2,7 @@ import {
ProductionCapacityIcon,
ThroughputSummaryIcon,
} from "../../icons/analysis";
import SkeletonUI from "../../templates/SkeletonUI";
const ProductionCapacity = ({
progressPercent = 50,
@ -15,6 +16,7 @@ const ProductionCapacity = ({
const partialFillPercent =
((progressPercent / 100) * totalBars - barsToFill) * 100;
const isLoading = false;
return (
<div className="throughtputSummary-container analysis-card">
<div className="throughtputSummary-wrapper analysis-card-wrapper">
@ -29,7 +31,8 @@ const ProductionCapacity = ({
<ThroughputSummaryIcon />
</div>
</div>
{isLoading ? (
<>
<div className="process-container">
<div className="throughput-value">
<span className="value">{throughputValue}</span> Units/hour
@ -62,6 +65,10 @@ const ProductionCapacity = ({
<span className="value">{machineUtilization}</span>
</div>
</div>
</>
) : (
<SkeletonUI type={"default"} />
)}
</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";
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]
);
// 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 (
<LoggerContext.Provider value={loggerMethods}>
{children}

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,9 @@
@use "../../abstracts/mixins.scss" as *;
.marketplace-wrapper {
// transform: scale(0.65);
/* Start at 90% width */
height: 100vh;
width: 100vw;
z-index: #{$z-index-marketplace};
@ -11,6 +14,7 @@
top: 0;
padding: 10px;
padding-top: 100px;
// animation: growWidth 0.4s ease-in-out 0.5s forwards;
.marketplace-container {
position: relative;
@ -31,6 +35,58 @@
flex-direction: column;
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 {
width: 100%;
display: flex;
@ -84,6 +140,13 @@
.stars {
display: flex;
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 {
width: 100%;
height: 100%;

View File

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

View File

@ -378,12 +378,30 @@
text-overflow: ellipsis;
}
.metric-label.loss {
background: #6D4343;
border: 1px solid #FF301D
}
.metric-value {
text-align: center;
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 {
display: flex;
gap: 6px;

View File

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

View File

@ -1393,3 +1393,35 @@
}
}
}
.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 {
max-width: 600px;
// max-width: 600px;
margin: 0 auto;
width: 100%;