first commit

This commit is contained in:
2025-06-10 15:28:23 +05:30
commit e22a2dc275
699 changed files with 100382 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
import React, { Suspense } from "react";
import { FilledStarsIconSmall } from "../../components/icons/marketPlaceIcons";
import { Canvas } from "@react-three/fiber";
import { ContactShadows, OrbitControls, Text } from "@react-three/drei";
import GltfLoader from "./GltfLoader";
import * as THREE from "three";
// Define the shape of the selected card
interface SelectedCard {
assetName: string;
uploadedOn: number;
price: number;
rating: number;
views: number;
description: string;
AssetID: string;
}
// Define the props type for AssetPreview
interface AssetPreviewProps {
selectedCard: SelectedCard;
modelUrl: string;
setSelectedCard: React.Dispatch<React.SetStateAction<SelectedCard | null>>; // Type for setter function
}
const savedTheme: string | null = localStorage.getItem("theme");
function Ui() {
return (
<Text
color={savedTheme === "dark" ? "#d2baff" : "#6f42c1"}
anchorX="center"
anchorY="middle"
scale={0.3}
>
Loading preview...
</Text>
);
}
const AssetPreview: React.FC<AssetPreviewProps> = ({
selectedCard,
setSelectedCard,
modelUrl,
}) => {
return (
<div className="assetPreview-wrapper">
<div className="assetPreview">
<div className="image-preview">
{/* <img src={assetImage} alt="" /> */}
{/* Add canvas here */}
<div className="canvas-container" style={{ height: "100%" }}>
<Canvas
flat
shadows
camera={{ fov: 75 }}
gl={{
preserveDrawingBuffer: true,
}}
onCreated={({ scene }) => {
scene.background = new THREE.Color(
savedTheme === "dark" ? 0x19191d : 0xfcfdfd
);
}}
>
<Suspense fallback={<Ui />}>
{selectedCard.assetName && modelUrl && (
<GltfLoader
fromServer={modelUrl}
assetId={selectedCard?.AssetID}
/>
)}
<OrbitControls minPolarAngle={0} maxPolarAngle={Math.PI / 2} />
<ContactShadows
renderOrder={2}
frames={1}
resolution={1024}
scale={120}
blur={2}
opacity={0.4}
far={100}
/>
</Suspense>
</Canvas>
</div>
</div>
<div className="asset-details-preview">
<div className="organization">
<div className="image">H</div>
<div className="organization-details">
<div className="organization-name">HERX FACTORY</div>
<div className="follow">Follow +</div>
</div>
</div>
{/* asset details */}
<div className="asset-details">
<div className="asset-name">{selectedCard.assetName}</div>
<div className="asset-description">
{`${selectedCard.description}`}
</div>
<div className="asset-review">
<div className="asset-rating">
<FilledStarsIconSmall />
{selectedCard.rating}
</div>
<div className="asset-view">{selectedCard.views} views</div>
</div>
<div className="asset-price"> {selectedCard.price}</div>
</div>
{/* buttons */}
<div className="button-container">
<div className="button">Add to cart</div>
<div className="button">Buy now</div>
</div>
{/* close button */}
<button
id="asset-back-buttton"
className="closeButton"
onClick={() => setSelectedCard(null)}
>
{`<-back`}
</button>
</div>
</div>
</div>
);
};
export default AssetPreview;

View File

@@ -0,0 +1,115 @@
import React from "react";
import {
CommentsIcon,
EyeIconBig,
FilledStarsIconSmall,
StarsIconSmall,
VerifiedIcon,
} from "../../components/icons/marketPlaceIcons";
interface CardProps {
assetName: string;
uploadedOn: number;
price: number;
rating: number;
views: number;
image: string;
description: string;
AssetID: string;
modelUrl: string;
onSelectCard: (cardData: {
assetName: string;
uploadedOn: number;
price: number;
rating: number;
views: number;
description: string;
AssetID: string;
}) => void;
}
const Card: React.FC<CardProps> = ({
assetName,
uploadedOn,
price,
rating,
views,
image,
description,
AssetID,
onSelectCard,
}) => {
const handleCardSelect = () => {
console.log("assetName: ", assetName);
console.log("AssetID: ", AssetID);
onSelectCard({
assetName,
uploadedOn,
price,
rating,
views,
description,
AssetID,
});
};
return (
<div className="card-container">
{/* <a href={getAssetDownload(assetName)} download className="icon">
<DownloadIcon />
</a> */}
<div className="image-container">
<img src={image} alt={assetName} />
</div>
<div className="assets-container">
<div className="name-container">
<div className="assets-name">{assetName.split("_").join(" ")}</div>
<div className="assets-date">
Uploaded on -{" "}
{new Date(uploadedOn).toLocaleDateString("en-GB", {
day: "2-digit",
month: "short",
year: "2-digit",
})}
</div>
</div>
<div className="details">
<div className="content">
<EyeIconBig />
{views}
</div>
<div className="content">
<CommentsIcon />
32
</div>
</div>
</div>
<div className="vendor-icon">
HEXR FACTORY <VerifiedIcon />
</div>
<div className="stars-container">
<div className="stars-wrapper">
{[...Array(5)].map((_, index) => (
<React.Fragment key={index}>
{index < 3 ? <FilledStarsIconSmall /> : <StarsIconSmall />}
</React.Fragment>
))}
</div>
<div className="units">
{price}/<span>unit</span>
</div>
</div>
<button
id={`${AssetID}-asset-buy`}
className="buy-now-button"
onClick={handleCardSelect}
>
Buy now
</button>
</div>
);
};
export default Card;

View File

@@ -0,0 +1,88 @@
import React, { useState } from "react";
import Card from "./Card";
import AssetPreview from "./AssetPreview";
import { fetchGltfUrl } from "../../services/marketplace/fetchGltfUrl";
interface ModelData {
CreatedBy: string;
animated: string | null;
category: string;
description: string;
filename: string;
isArchieve: boolean;
modelfileID: string;
tags: string;
thumbnail: string;
uploadDate: number;
_id: string;
price: number;
AssetID: string;
}
interface ModelsProps {
models: ModelData[];
}
const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
const [modelUrl, setModelUrl] = useState<string>("");
const [selectedCard, setSelectedCard] = useState<{
assetName: string;
uploadedOn: number;
price: number;
rating: number;
views: number;
description: string;
AssetID: string;
} | null>(null);
const handleCardSelect = async (cardData: {
assetName: string;
uploadedOn: number;
price: number;
rating: number;
views: number;
description: string;
AssetID: string;
}) => {
setSelectedCard(cardData);
const res = await fetchGltfUrl(cardData.assetName, cardData.AssetID);
// console.log("res: ", res);
setModelUrl(res.url);
};
return (
<div className="cards-container-wrapper">
<div className="cards-container-container">
<div className="header">Products You May Like</div>
<div className="cards-wrapper-container">
{models.length > 0 &&
models.map((assetDetail) => (
<React.Fragment key={assetDetail._id}>
<Card
assetName={assetDetail?.filename}
uploadedOn={assetDetail.uploadDate}
price={assetDetail?.price}
rating={4.5}
views={800}
onSelectCard={handleCardSelect}
AssetID={assetDetail.AssetID}
image={assetDetail.thumbnail}
modelUrl={assetDetail.modelfileID}
description={assetDetail.description}
/>
</React.Fragment>
))}
{/* <RenderOverlay> */}
{selectedCard && (
<AssetPreview
selectedCard={selectedCard}
setSelectedCard={setSelectedCard}
modelUrl={modelUrl}
/>
)}
{/* </RenderOverlay> */}
</div>
</div>
</div>
);
};
export default CardsContainer;

View File

@@ -0,0 +1,96 @@
import React, { useEffect, useState } from "react";
import Search from "../../components/ui/inputs/Search";
import { StarsIcon } from "../../components/icons/marketPlaceIcons";
import RegularDropDown from "../../components/ui/inputs/RegularDropDown";
interface ModelData {
CreatedBy: string;
animated: string | null;
category: string;
description: string;
filename: string;
isArchieve: boolean;
modelfileID: string;
tags: string;
thumbnail: string;
uploadDate: number;
_id: string;
price: number;
AssetID: string;
}
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");
const [rating, setRating] = useState(0);
const handleSelect = (option: string) => {
setActiveOption(option);
};
useEffect(() => {
if (activeOption === "Alphabet ascending") {
const ascending = [...models].sort((a, b) =>
a.filename.localeCompare(b.filename)
);
setModels(ascending);
} else if (activeOption === "Alphabet descending") {
const descending = [...models].sort((a, b) =>
b.filename.localeCompare(a.filename)
);
setModels(descending);
}
}, [activeOption, models, setModels]);
const handleSearch = (val: string) => {
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} />
<RegularDropDown
header={activeOption}
options={["Alphabet ascending", "Alphabet descending", ""]}
onSelect={handleSelect}
search={false}
/>
<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) => (
<button
id={`${i + 1}-star-button`}
key={i}
onClick={() => handleStarClick(i)}
className={`star-wrapper ${i < rating ? "filled" : "empty"}`}
>
<StarsIcon />
</button>
))}
</div>
</div>
</div>
);
};
export default FilterSearch;

View File

@@ -0,0 +1,77 @@
import { useRef, useEffect } from "react";
import { useFrame } from "@react-three/fiber";
import { Stage, useGLTF } from "@react-three/drei";
import { AnimationMixer, AnimationAction, Object3D } from "three";
import * as THREE from "three";
interface GltfLoaderProps {
glbdata?: boolean;
fromServer?: string;
setAnimations?: (animations?: string[]) => void;
selectedAnimation?: string;
setSelectedAnimation?: (animation: string) => void;
assetId: string
}
const GltfLoader: React.FC<GltfLoaderProps> = ({
glbdata,
fromServer,
setAnimations,
selectedAnimation,
assetId
}) => {
const { scene, animations } = useGLTF(fromServer ?? "") as {
scene: Object3D;
animations: THREE.AnimationClip[];
};
const mixer = useRef<AnimationMixer | null>(
scene ? new AnimationMixer(scene) : null
);
const actions = useRef<Record<string, AnimationAction>>({});
useEffect(() => {
if (animations.length > 0 && mixer.current && setAnimations) {
const animationNames = animations.map((animation) => animation.name);
setAnimations(animationNames);
animations.forEach((animation) => {
const action = mixer.current!.clipAction(animation);
actions.current[animation.name] = action;
});
} else {
setAnimations && setAnimations([]);
}
}, [animations, setAnimations]);
useEffect(() => {
if (actions.current && selectedAnimation) {
const action = actions.current[selectedAnimation];
if (action) {
action.reset().fadeIn(0.5).play();
return () => {
action.fadeOut(0.5);
};
}
}
}, [selectedAnimation]);
useFrame((_, delta) => {
if (mixer.current) {
mixer.current.update(delta);
}
});
return (
<Stage
adjustCamera={false}
intensity={0.5}
shadows="contact"
environment="city"
>
<primitive object={scene} scale={1} />
</Stage>
);
};
export default GltfLoader;

View File

@@ -0,0 +1,66 @@
import React, { useEffect, useState } from "react";
import FilterSearch from "./FilterSearch";
import CardsContainer from "./CardsContainer";
import { getAssetImages } from "../../services/factoryBuilder/assest/assets/getAssetImages";
import SkeletonUI from "../../components/templates/SkeletonUI";
interface ModelData {
CreatedBy: string;
animated: string | null;
category: string;
description: string;
filename: string;
isArchieve: boolean;
modelfileID: string;
tags: string;
thumbnail: string;
uploadDate: number;
_id: string;
price: number;
AssetID: string;
}
const MarketPlace = () => {
const [models, setModels] = useState<ModelData[]>([]);
const [filteredModels, setFilteredModels] = useState<ModelData[]>([]);
const [isLoading, setisLoading] = useState<boolean>(false); // Loading state
useEffect(() => {
echo.log("opend market place");
}, []);
useEffect(() => {
const filteredAssets = async () => {
setisLoading(true);
try {
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
setModels(filt.items);
setFilteredModels(filt.items);
setisLoading(false);
} catch {
echo.error("Failed to filter asset");
setisLoading(false);
}
};
filteredAssets();
}, []);
return (
<div className="marketplace-wrapper">
<div className="marketplace-container">
<div className="marketPlace">
<FilterSearch
models={models}
setModels={setModels}
filteredModels={filteredModels}
/>
{isLoading ? (
<SkeletonUI type="assetLibrary" /> // Show loading spinner while fetching
) : (
<CardsContainer models={models} />
)}
</div>
</div>
</div>
);
};
export default MarketPlace;