Merge pull request 'completed some features in market place' (#18) from simulation-agv into main
Reviewed-on: http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev/pulls/18
This commit is contained in:
commit
0cacf0964f
|
@ -1,11 +1,15 @@
|
|||
import React from "react";
|
||||
import React, { Suspense, useEffect } from "react";
|
||||
import assetImage from "../../assets/image/image.png";
|
||||
import { FiileedStarsIconSmall } from "../../components/icons/marketPlaceIcons";
|
||||
import { Canvas, useThree } 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: string;
|
||||
uploadedOn: number;
|
||||
price: number;
|
||||
rating: number;
|
||||
views: number;
|
||||
|
@ -17,6 +21,14 @@ interface AssetPreviewProps {
|
|||
setSelectedCard: React.Dispatch<React.SetStateAction<SelectedCard | null>>; // Type for setter function
|
||||
}
|
||||
|
||||
function Ui() {
|
||||
return (
|
||||
<Text color="#6f42c1" anchorX="center" anchorY="middle" scale={0.3}>
|
||||
Loading your model...
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
const AssetPreview: React.FC<AssetPreviewProps> = ({
|
||||
selectedCard,
|
||||
setSelectedCard,
|
||||
|
@ -27,8 +39,6 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
|
|||
Math.min(5, isNaN(selectedCard.rating) ? 0 : selectedCard.rating)
|
||||
);
|
||||
|
||||
console.log("selectedCard: ", selectedCard);
|
||||
|
||||
// Ensure that the rating is a valid positive integer for array length
|
||||
const starsArray = Array.from({ length: rating }, (_, index) => index);
|
||||
|
||||
|
@ -36,8 +46,38 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
|
|||
<div className="assetPreview-wrapper">
|
||||
<div className="assetPreview">
|
||||
<div className="image-preview">
|
||||
<img src={assetImage} alt="" />
|
||||
{/* <img src={assetImage} alt="" /> */}
|
||||
{/* Add canvas here */}
|
||||
<div className="canvas-container" style={{ height: "100%" }}>
|
||||
<Canvas
|
||||
flat
|
||||
shadows
|
||||
color="#FFFFFF"
|
||||
camera={{ fov: 75 }}
|
||||
gl={{
|
||||
preserveDrawingBuffer: true,
|
||||
}}
|
||||
onCreated={({ scene }) => {
|
||||
scene.background = new THREE.Color(0xffffff);
|
||||
}}
|
||||
>
|
||||
<Suspense fallback={<Ui />}>
|
||||
{selectedCard.assetName && (
|
||||
<GltfLoader fromServer={selectedCard.assetName} />
|
||||
)}
|
||||
<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">
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from "../../components/icons/marketPlaceIcons";
|
||||
|
||||
import assetImage from "../../assets/image/image.png";
|
||||
import { getAssetDownload } from "../../services/marketplace/getAssetDownload";
|
||||
|
||||
interface CardProps {
|
||||
assetName: string;
|
||||
|
@ -40,9 +41,9 @@ const Card: React.FC<CardProps> = ({
|
|||
|
||||
return (
|
||||
<div className="card-container">
|
||||
<div className="icon">
|
||||
{/* <a href={getAssetDownload(assetName)} download className="icon">
|
||||
<DownloadIcon />
|
||||
</div>
|
||||
</a> */}
|
||||
<div className="image-container">
|
||||
<img src={image} alt={assetName} />
|
||||
</div>
|
||||
|
|
|
@ -16,109 +16,11 @@ interface ModelData {
|
|||
uploadDate: number;
|
||||
_id: string;
|
||||
}
|
||||
interface ModelsProps {
|
||||
models: ModelData[];
|
||||
}
|
||||
|
||||
const CardsContainer: React.FC = () => {
|
||||
const [models, setModels] = useState<ModelData[]>([]);
|
||||
|
||||
const array = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Asset 1",
|
||||
uploadedOn: "12 Jan 23",
|
||||
price: 36500,
|
||||
rating: 4.5,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Asset 2",
|
||||
uploadedOn: "14 Jan 23",
|
||||
price: 45000,
|
||||
rating: 4.0,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Asset 3",
|
||||
uploadedOn: "15 Jan 23",
|
||||
price: 52000,
|
||||
rating: 4.8,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Asset 4",
|
||||
uploadedOn: "18 Jan 23",
|
||||
price: 37000,
|
||||
rating: 3.9,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Asset 5",
|
||||
uploadedOn: "20 Jan 23",
|
||||
price: 60000,
|
||||
rating: 5.0,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Asset 6",
|
||||
uploadedOn: "22 Jan 23",
|
||||
price: 46000,
|
||||
rating: 4.2,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Asset 7",
|
||||
uploadedOn: "25 Jan 23",
|
||||
price: 38000,
|
||||
rating: 4.3,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Asset 8",
|
||||
uploadedOn: "27 Jan 23",
|
||||
price: 41000,
|
||||
rating: 4.1,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Asset 9",
|
||||
uploadedOn: "30 Jan 23",
|
||||
price: 55000,
|
||||
rating: 4.6,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Asset 10",
|
||||
uploadedOn: "2 Feb 23",
|
||||
price: 49000,
|
||||
rating: 4.4,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Asset 11",
|
||||
uploadedOn: "5 Feb 23",
|
||||
price: 62000,
|
||||
rating: 5.0,
|
||||
views: 500,
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Asset 12",
|
||||
uploadedOn: "7 Feb 23",
|
||||
price: 53000,
|
||||
rating: 4.7,
|
||||
views: 500,
|
||||
},
|
||||
];
|
||||
|
||||
const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
|
||||
const [selectedCard, setSelectedCard] = useState<{
|
||||
assetName: string;
|
||||
uploadedOn: string;
|
||||
|
@ -136,33 +38,11 @@ const CardsContainer: React.FC = () => {
|
|||
}) => {
|
||||
setSelectedCard(cardData);
|
||||
};
|
||||
const getAllAssets = async () => {
|
||||
try {
|
||||
const assetsData = await fetchAssets();
|
||||
const reversedData = [...assetsData]?.reverse().slice(0, 8);
|
||||
setModels(reversedData);
|
||||
} catch (error) {
|
||||
} finally {
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
getAllAssets();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="cards-container-container">
|
||||
<div className="header">Products You May Like</div>
|
||||
<div className="cards-wrapper-container">
|
||||
{/* {array.map((asset) => (
|
||||
<Card
|
||||
key={asset.id}
|
||||
assetName={asset.name}
|
||||
uploadedOn={asset.uploadedOn}
|
||||
price={asset.price}
|
||||
rating={asset.rating}
|
||||
views={asset.views}
|
||||
onSelectCard={handleCardSelect}
|
||||
/>
|
||||
))} */}
|
||||
{models.length > 0 &&
|
||||
models.map((assetDetail) => (
|
||||
<Card
|
||||
|
@ -171,7 +51,7 @@ const CardsContainer: React.FC = () => {
|
|||
uploadedOn={assetDetail.uploadDate.toString()}
|
||||
price={36500}
|
||||
rating={4.5}
|
||||
views={500}
|
||||
views={800}
|
||||
onSelectCard={handleCardSelect}
|
||||
image={assetDetail.thumbnail}
|
||||
/>
|
||||
|
|
|
@ -1,19 +1,69 @@
|
|||
import React, { useState } from "react";
|
||||
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";
|
||||
|
||||
const FilterSearch: React.FC = () => {
|
||||
import { getSortedAssets } from "../../services/marketplace/getSortedAssets";
|
||||
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;
|
||||
}
|
||||
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
|
||||
console.log("filteredModels: ", filteredModels);
|
||||
|
||||
const handleSelect = (option: string) => {
|
||||
setActiveOption(option);
|
||||
console.log("option: ", 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);
|
||||
setModels(ascending);
|
||||
} else if (activeOption == "Alphabet descending") {
|
||||
let descending = models
|
||||
?.slice()
|
||||
.sort((a, b) => b.filename.localeCompare(a.filename))
|
||||
.map((val) => val);
|
||||
setModels(descending);
|
||||
}
|
||||
}, [activeOption]);
|
||||
const handleSearch = (val: string) => {
|
||||
const filteredModel = filteredModels?.filter((model) =>
|
||||
model.filename.toLowerCase().includes(val.toLowerCase())
|
||||
);
|
||||
|
||||
setModels(filteredModel);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="filter-search-container">
|
||||
<Search onChange={() => {}} />
|
||||
<Search onChange={handleSearch} />
|
||||
<RegularDropDown
|
||||
header={activeOption}
|
||||
options={["Alphabet ascending", "Alphabet descending", ""]}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
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";
|
||||
import { fetchGltfUrl } from "../../services/marketplace/fetchGltfUrl";
|
||||
|
||||
interface GltfLoaderProps {
|
||||
glbdata?: boolean;
|
||||
fromServer?: string;
|
||||
setAnimations?: (animations?: string[]) => void;
|
||||
selectedAnimation?: string;
|
||||
setSelectedAnimation?: (animation: string) => void;
|
||||
}
|
||||
|
||||
// const getGLTFUrl = (url: string) => url; // Placeholder for your actual function
|
||||
|
||||
const GltfLoader: React.FC<GltfLoaderProps> = ({
|
||||
glbdata,
|
||||
fromServer,
|
||||
setAnimations,
|
||||
selectedAnimation,
|
||||
}) => {
|
||||
const modelUrl: any = fromServer ? fetchGltfUrl(fromServer) : glbdata;
|
||||
const { scene, animations } = useGLTF(modelUrl ?? "") 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;
|
|
@ -1,14 +1,46 @@
|
|||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import FilterSearch from "./FilterSearch";
|
||||
import CardsContainer from "./CardsContainer";
|
||||
|
||||
import { fetchAssets } from "../../services/marketplace/fetchAssets";
|
||||
import { getAssetImages } from "../../services/factoryBuilder/assest/assets/getAssetImages";
|
||||
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;
|
||||
}
|
||||
const MarketPlace = () => {
|
||||
const [models, setModels] = useState<ModelData[]>([]);
|
||||
const [filteredModels, setFilteredModels] = useState<ModelData[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const filteredAssets = async () => {
|
||||
try {
|
||||
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
|
||||
setModels(filt.items);
|
||||
setFilteredModels(filt.items);
|
||||
} catch {}
|
||||
};
|
||||
filteredAssets();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="marketplace-wrapper">
|
||||
<div className="marketplace-container">
|
||||
<div className="marketPlace">
|
||||
<FilterSearch />
|
||||
<CardsContainer />
|
||||
<FilterSearch
|
||||
models={models}
|
||||
setModels={setModels}
|
||||
filteredModels={filteredModels}
|
||||
/>
|
||||
<CardsContainer models={models} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
export const getAssetDetails = async (filename: string) => {
|
||||
try {
|
||||
const response = await fetch(`${BackEnd_url}/api/v1/assetDetails`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ filename }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch asset details");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
// console.error("Error fetching category:", error.message);
|
||||
throw new Error(error.message);
|
||||
}
|
||||
};
|
|
@ -6,6 +6,8 @@ export const fetchAssets = async () => {
|
|||
throw new Error("Network response was not ok");
|
||||
}
|
||||
const result = await response.json();
|
||||
const last10Assets = result.slice(-10);
|
||||
console.log('last10Assets: ', last10Assets);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.log("error: ", error);
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
export const fetchGltfUrl = (filename: string) => {
|
||||
if (filename) {
|
||||
return `${BackEnd_url}/api/v1/getAssetFile/${filename}`;
|
||||
}
|
||||
return null; // or handle the case when filename is not provided
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
export const getAssetDownload = (filename: any) => {
|
||||
return `${BackEnd_url}/api/v1/getAssetFile/${filename}.gltf`;
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
export const getSortedAssets = async (category: any, orders: any) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${BackEnd_url}/api/v1/categoryWise/${category}?sortBy=${orders}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return result; // Return the result to be used later
|
||||
} catch (error: any) {
|
||||
console.error("Error fetching category:", error.message);
|
||||
throw new Error(error.message);
|
||||
}
|
||||
};
|
|
@ -126,6 +126,8 @@
|
|||
display: flex;
|
||||
max-height: 180px;
|
||||
justify-content: center;
|
||||
border-radius: #{$border-radius-medium};
|
||||
overflow: hidden;
|
||||
img{
|
||||
height: inherit;
|
||||
width: 100%;
|
||||
|
@ -211,7 +213,7 @@
|
|||
background-color: var(--background-color);
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue