diff --git a/package.json b/package.json index 15f8115..576afc4 100644 --- a/package.json +++ b/package.json @@ -33,13 +33,17 @@ "react-helmet": "^6.1.0", "react-icons": "^4.10.1", "react-if": "^4.1.5", + "react-material-ui-carousel": "^3.4.2", "react-pro-sidebar": "^1.1.0-alpha.1", "react-redux": "^8.1.1", "react-responsive-carousel": "^3.2.23", "react-router-dom": "^6.17.0", "react-scripts": "5.0.1", + "react-slick": "^0.29.0", "react-spinners": "^0.13.8", "react-stripe-checkout": "^2.6.3", + "react-swipeable-views": "^0.14.0", + "slick-carousel": "^1.8.1", "socket.io-client": "^4.7.2", "stripe": "^12.14.0", "styled-components": "^6.0.5", @@ -75,6 +79,7 @@ "eslint": "^8.46.0", "eslint-config-prettier": "^8.9.0", "eslint-plugin-prettier": "^5.0.0", + "http-proxy-middleware": "^2.0.6", "prettier": "^3.0.0" } } diff --git a/src/App.js b/src/App.js index 2f74223..e56bc47 100644 --- a/src/App.js +++ b/src/App.js @@ -1,18 +1,12 @@ // External Imports -import React, { useEffect, useRef, useState } from 'react'; -import { - BrowserRouter as Router, - Route, - Routes, - useLocation, -} from 'react-router-dom'; +import React, { useContext, useEffect, useRef, useState } from 'react'; +import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import { Helmet } from 'react-helmet'; -import styled, { createGlobalStyle } from 'styled-components'; // Component Imports import Header from './components/headings/header/Header'; import Footer from './components/headings/footer/Footer'; -import PrivateRoute from './components/Auth/PrivateRoute'; +import PrivateRoute from './components/reusable/PrivateRoute'; // Page Imports import SplashPage from './pages/SplashPage'; @@ -22,182 +16,54 @@ import CartPage from './pages/CartPage'; import ProfilePage from './pages/ProfilePage'; import CollectionPage from './pages/CollectionPage'; import DeckBuilderPage from './pages/DeckBuilderPage'; -import ThreeJsCube from './pages/ThreeJsCube'; -import CardDeckAnimation from './pages/CardDeckAnimation'; +import ThreeJsCube from './assets/animations/ThreeJsCube'; +import CardDeckAnimation from './assets/animations/CardDeckAnimation'; // Context Hooks Imports -import { useCombinedContext } from './context/CombinedProvider'; import { useUserContext } from './context/UserContext/UserContext'; import { useCollectionStore } from './context/CollectionContext/CollectionContext'; import { useUtilityContext } from './context/UtilityContext/UtilityContext'; +import { AppContainer } from './pages/pageStyles/StyledComponents'; +import { useCardImages } from './context/CardImagesContext/CardImagesContext'; +import { useCookies } from 'react-cookie'; +const App = () => { + const [cookies] = useCookies(['user']); -// Styled Components -const AppContainer = styled.div` - display: flex; - flex-direction: column; - height: 100vh; -`; - -const GlobalStyle = createGlobalStyle` - /* Your global styles here */ -`; + const user = cookies.user; + const [currentPage, setCurrentPage] = useState(''); + // const { setContext } = useAppContext(); // Assuming useAppContext provides setContext + const { fetchAllCollectionsForUser, selectedCollection } = + useCollectionStore(); + const { isLoading, setIsLoading } = useUtilityContext(); -// Custom Hook for Cron Job -const useCronJob = (lastCronJobTriggerTime, setLastCronJobTriggerTime) => { - const { - cronData, - handleSendAllCardsInCollections, - listOfMonitoredCards, - // handleRetreiveListOfMonitoredCards, - // retrievedListOfMonitoredCards, - allCollectionsUpdated, - } = useCombinedContext(); - const { allCollections } = useCollectionStore(); - const [priceHistory, setPriceHistory] = useState([]); + // const { getRandomCardImages } = useCardImages(); // Add this line - const { handlePricesActivateCron, cardsWithChangedPrice } = - useCombinedContext(); - const { user } = useUserContext(); - const [priceFlux, setPriceFlux] = useState(null); - const userId = user?.userID; - useEffect(() => { - setLastCronJobTriggerTime(new Date().getTime()); - }, [setLastCronJobTriggerTime]); - // --------------------------------------------------------------- // useEffect(() => { - // handlePricesActivateCron( - // userId, - // listOfMonitoredCards, - // allCollections, - // cardsWithChangedPrice - // ); - // }, [ - // userId, - // listOfMonitoredCards, - // allCollections, - // cardsWithChangedPrice, - // priceFlux, - // ]); - - useEffect(() => { - const handleTriggerCronJob = () => { - const currentTime = new Date().getTime(); - const timeDifference = currentTime - lastCronJobTriggerTime; - const previousTotalPrice = allCollections?.totalPrice; - if (!priceHistory.includes(previousTotalPrice)) { - priceHistory.push(previousTotalPrice); - } - const minutes = Math.floor(timeDifference / 60000); // 1 minute = 60000 milliseconds - const seconds = Math.floor((timeDifference % 60000) / 1000); // remainder in milliseconds converted to seconds - - // Output the remaining time in minutes and seconds - console.log( - `REMAINING TIME: ${minutes} minute(s) and ${seconds} second(s)` - ); - - for (const collection of allCollections) { - if ( - collection?.cards && - collection?.cards?.length > 0 && - collection.totalPrice !== previousTotalPrice // Implement your logic here - ) { - setPriceFlux(new Date().getTime()); // Trigger a re-render - console.log('PRICE FLUX:', priceFlux); - } - } - // if (collection.totalPrice !== previousTotalPrice) { - // // Update dependencies of useEffect - // setPriceFlux(new Date().getTime()); // Trigger a re-render - // } - if (currentTime - lastCronJobTriggerTime >= 60000) { - setLastCronJobTriggerTime(currentTime); - if (userId && listOfMonitoredCards) { - console.log('RETRIEVING LIST OF MONITORED CARDS (paused)'); - handleSendAllCardsInCollections( - userId, - listOfMonitoredCards - // handleRetrieveListOfMonitoredCards() - ); - console.log('Triggered the cron job.'); - } - } - }; - - const interval = setInterval(handleTriggerCronJob, 60000); - return () => clearInterval(interval); - }, [ - cronData, - lastCronJobTriggerTime, - allCollectionsUpdated, - // handleRetrieveListOfMonitoredCards, - handleSendAllCardsInCollections, - listOfMonitoredCards, - userId, - ]); -}; - -// Main Component -const App = () => { - const { user } = useUserContext(); - const { isLoading, setIsContextLoading } = useUtilityContext(); - const { fetchAllCollectionsForUser, allCollections } = useCollectionStore(); - const [lastCronJobTriggerTime, setLastCronJobTriggerTime] = useState(null); - - useCronJob(lastCronJobTriggerTime, setLastCronJobTriggerTime); + // getRandomCardImages(10); // Fetch 10 random images on app start + // }, []); // Add this useEffect useEffect(() => { if (user) { - console.log('Private routes now available'); + fetchAllCollectionsForUser(user.id) + .then(() => { + setIsLoading(false); + }) + .catch((error) => { + console.error('Error fetching collections:', error); + setIsLoading(false); + }); } + }, [user, fetchAllCollectionsForUser, setIsLoading, selectedCollection]); - return () => { - console.log('Private routes no longer available'); - }; - }, [user]); - + // Handle initial loading state useEffect(() => { if (!isLoading) { - setIsContextLoading(false); + setIsLoading(false); } - }, [isLoading, setIsContextLoading]); - - useEffect(() => { - const timer = setTimeout(() => { - setIsContextLoading(false); - }, 5500); - return () => clearTimeout(timer); - }, [setIsContextLoading]); - - useEffect(() => { - let isMounted = true; // Add a flag to track if the component is mounted - const fetchCollections = async () => { - if (user && isMounted) { - try { - // const response = fet - await fetchAllCollectionsForUser(user.userID); - if (isMounted) { - console.log('Fetched collections because none were present.'); - // Update state only if the component is still mounted - console.log('RESPONSE:', isMounted); - // setOfficialCollectionDatasets(response.data); - } - } catch (err) { - console.error('Failed to fetch collections:', err); - } - } - }; - - fetchCollections(); - - // Cleanup function to cancel any ongoing tasks when the component unmounts - return () => { - isMounted = false; - }; - }, [user, fetchAllCollectionsForUser, allCollections]); + }, [isLoading, setIsLoading]); return ( <> - { // Initialize the scene, camera, and renderer here diff --git a/src/pages/pageStyles/Cube.jsx b/src/assets/animations/Cube.jsx similarity index 100% rename from src/pages/pageStyles/Cube.jsx rename to src/assets/animations/Cube.jsx diff --git a/src/assets/animations/LoadingCardAnimation.jsx b/src/assets/animations/LoadingCardAnimation.jsx new file mode 100644 index 0000000..12a4f02 --- /dev/null +++ b/src/assets/animations/LoadingCardAnimation.jsx @@ -0,0 +1,228 @@ +import React, { useEffect, useState } from 'react'; +import * as THREE from 'three'; +import { useCardImages } from '../../context/CardImagesContext/CardImagesContext'; +import placeholderImage from '../images/placeholder.jpeg'; // Adjust the path as necessary +function LoadingCardAnimation() { + const { cards, isLoading, error } = useCardImages(); + const [randomCardImage, setRandomCardImage] = useState(); + + useEffect(() => { + if (cards && cards.length > 0) { + const randomCard = cards[Math.floor(Math.random() * cards.length)]; + if (randomCard.card_images && randomCard.card_images.length > 0) { + // Constructing the local file name + const name = randomCard.name.replace(/[/\\?%*:|"<>]/g, ''); + console.log('name', name); + const folder = 'images'; + console.log('folder', folder); + const folder2 = 'cards'; + const folder3 = `${ + randomCard?.type === 'Spell Card' || randomCard?.type === 'Trap Card' + ? 'spell_trap' + : 'monster' + }`; + console.log('folder2', folder2); + const extension = 'jpg'; // Assuming all images are jpg + console.log('naextensionme', extension); + const fullName = `${name}_${randomCard.race}_${randomCard.type}${ + randomCard.level ? '_lvl' + randomCard.level : '' + }${ + randomCard.attribute ? '_' + randomCard.attribute : '' + }.${extension}`; + console.log('fullName', fullName); + const filePath = `../${folder}/${folder2}/${folder3}/${fullName}`; + console.log('filePath', filePath); + console.log('placeholderImage', placeholderImage); + setRandomCardImage(filePath); + } + } + console.log('cards', cards); + console.log('randomCardImage', randomCardImage); + }, [cards]); + + useEffect(() => { + if (!randomCardImage) return; + + const scene = new THREE.Scene(); + const camera = new THREE.PerspectiveCamera( + 75, + window.innerWidth / window.innerHeight, + 0.1, + 1000 + ); + const renderer = new THREE.WebGLRenderer({ alpha: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + + const loadingIconContainer = document.getElementById('loadingIcon'); + if (!loadingIconContainer) return; + + loadingIconContainer.appendChild(renderer.domElement); + + // Load the placeholder texture + const placeholderTexture = new THREE.TextureLoader().load(placeholderImage); + + // Load the card image texture + new THREE.TextureLoader().load( + randomCardImage, + (texture) => { + const cardMaterialFront = new THREE.MeshBasicMaterial({ map: texture }); + const cardMaterialBack = new THREE.MeshBasicMaterial({ + map: placeholderTexture, + }); + + const cardGeometry = new THREE.BoxGeometry(1, 1.4, 0.01); + const card = new THREE.Mesh(cardGeometry, [ + cardMaterialBack, // Left side + cardMaterialBack, // Right side + cardMaterialBack, // Top side + cardMaterialBack, // Bottom side + cardMaterialFront, // Front side + cardMaterialBack, // Back side + ]); + scene.add(card); + camera.position.z = 5; + + const animate = () => { + requestAnimationFrame(animate); + card.rotation.y += 0.05; + renderer.render(scene, camera); + }; + + animate(); + }, + undefined, // onProgress callback not supported + (err) => { + console.error('An error occurred while loading the texture:', err); + } + ); + + return () => { + if (loadingIconContainer.contains(renderer.domElement)) { + loadingIconContainer.removeChild(renderer.domElement); + } + }; + }, [randomCardImage]); + + if (isLoading) return
Loading...
; + if (error) return
Error: {error}
; + + return ( +
+ Three.js animation should appear here +
+ ); +} + +export default LoadingCardAnimation; + +// import * as THREE from 'three'; +// import { useCollectionStore } from '../../context/CollectionContext/CollectionContext'; +// import placeholderImage from '../images/placeholder.jpeg'; +// import { useCardImages } from '../../context/CardImagesContext/CardImagesContext'; + +// function LoadingCardAnimation() { +// const { selectedCollection, allCollections } = useCollectionStore(); +// const [randomCardImage, setRandomCardImage] = useState(null); +// const { images, isLoading, error, downloadCardImages, getRandomCardImages } = +// useCardImages(); + +// if (isLoading) { +// console.log('Loading...'); +// return
Loading...
; +// } + +// if (error) { +// console.error(error); +// return
Error: {error}
; +// } + +// if (images && images.length > 0) { +// return ( +//
+// {images.map((image, index) => ( +// card +// ))} +//
+// ); +// } + +// useEffect(() => { +// const selectRandomCardImage = (collection) => { +// const filteredCards = collection.cards.filter( +// (card) => card.card_images && card.card_images.length > 0 +// ); +// if (filteredCards.length > 0) { +// const randomCard = +// filteredCards[Math.floor(Math.random() * filteredCards.length)]; +// return randomCard.card_images[0].image_url; +// } +// return placeholderImage; +// }; + +// const collection = +// selectedCollection || +// (allCollections.length > 0 +// ? allCollections[Math.floor(Math.random() * allCollections.length)] +// : null); +// if (collection) { +// setRandomCardImage(selectRandomCardImage(collection)); +// } else { +// setRandomCardImage(placeholderImage); +// } +// }, [selectedCollection, allCollections]); + +// useEffect(() => { +// if (!randomCardImage) return; + +// const loadingIconContainer = document.getElementById('loadingIcon'); +// if (!loadingIconContainer) { +// console.error('Loading icon container not found'); +// return; +// } + +// const scene = new THREE.Scene(); +// const camera = new THREE.PerspectiveCamera(75, 400 / 400, 0.1, 1000); +// const renderer = new THREE.WebGLRenderer({ alpha: true }); +// renderer.setSize(400, 400); + +// loadingIconContainer.appendChild(renderer.domElement); + +// const placeholderTexture = new THREE.TextureLoader().load(placeholderImage); +// const randomCardTexture = new THREE.TextureLoader().load(randomCardImage); +// const cardMaterialFront = new THREE.MeshBasicMaterial({ +// map: placeholderTexture, +// }); +// const cardMaterialBack = new THREE.MeshBasicMaterial({ +// map: randomCardTexture, +// }); + +// const cardGeometry = new THREE.BoxGeometry(1, 1.4, 0.01); +// const card = new THREE.Mesh(cardGeometry, [ +// cardMaterialBack, +// cardMaterialBack, +// cardMaterialBack, +// cardMaterialBack, +// cardMaterialFront, +// cardMaterialBack, +// ]); +// scene.add(card); + +// camera.position.z = 5; + +// const animate = function () { +// requestAnimationFrame(animate); +// card.rotation.y += 0.05; +// renderer.render(scene, camera); +// }; + +// animate(); + +// return () => { +// loadingIconContainer.removeChild(renderer.domElement); +// }; +// }, [randomCardImage]); + +// return
; +// } + +// export default LoadingCardAnimation; diff --git a/src/pages/ThreeJsCube.js b/src/assets/animations/ThreeJsCube.js similarity index 100% rename from src/pages/ThreeJsCube.js rename to src/assets/animations/ThreeJsCube.js diff --git a/src/assets/example.json b/src/assets/example.json deleted file mode 100644 index 88ce308..0000000 --- a/src/assets/example.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "chartData": { - "_id": 64163367, - "name": "\"A\" Cell Incubator", - "userId": "Spell Card", - "collectionId": "spell", - "chartId": "Each time an A-Counter(s) is removed from play by a card effect, place 1 A-Counter on this card. When this card is destroyed, distribute the A-Counters on this card among face-up monsters.", - "datasets": [ - { - "label": "Set x", - "totalquantity": 37, - "data": { - "points": [ - "card name": { - "x": "x/x/2023", - "y": 3, - "price change": "prev y - y", - "price change %": "price change / prev y", - "total quantity": "totalquantity", - "total price": "totalquantity * y" - }, - - } - ], - }, - "backgroundColor": "rgba(255, 99, 132, 0.2)", - "borderColor": "rgb(255, 99, 132)", - "borderWidth": 1 - } - ] - } -} diff --git a/src/assets/styles/CSStoRemove.css b/src/assets/styles/CSStoRemove.css deleted file mode 100644 index 5dacdbe..0000000 --- a/src/assets/styles/CSStoRemove.css +++ /dev/null @@ -1,769 +0,0 @@ -code { - font-family: - source-code-pro, - Menlo, - Monaco, - Consolas, - Courier New, - monospace; -} -#header { - background-color: #4caf50; - color: #fff; - padding: 10px; -} - -#header:after { - clear: both; - content: ''; - display: table; -} - -#navbar { - float: left; - margin: 0; - padding: 0; -} - -#navbar li { - display: inline-block; - margin-left: 70px; -} - -#navbar li a { - color: #fff; - display: block; - padding: 14px 16px; - text-align: center; - text-decoration: none; -} - -#navbar li a:hover { - background-color: #111; -} -#home .header-content { - color: #fff; - position: relative; - text-align: center; - top: 50%; - -webkit-transform: translateY(-50%); - transform: translateY(-50%); -} - -#home .header-content h1 { - font-size: 3.5rem; - margin: 0; -} - -#home .header-content p { - font-size: 1.5rem; - margin-bottom: 1.25rem; -} - -#about .about-heading { - text-align: center; -} - -#about .about-heading h2 { - font-size: 3rem; -} - -#about .about-content { - align-items: center; - display: flex; - justify-content: space-between; -} - -#about .about-content .left-column { - flex-basis: 50%; - padding-right: 1rem; -} - -#about .about-content .right-column { - flex-basis: 50%; - padding-left: 1rem; -} - -#services .service-item { - margin-bottom: 2rem; - text-align: center; -} - -#services .service-item h2 { - font-size: 2.5rem; -} - -#services .service-item p { - font-size: 1rem; -} -#portfolio .portfolio-item .how-title { - color: #fff; - font-size: 16px; - font-weight: 700; -} - -#portfolio .portfolio-item .skill { - align-items: center; - display: flex; - margin-top: 5px; -} - -#portfolio .portfolio-item .skill p { - font-size: 14px; - font-weight: 600; - margin: 0; -} - -#portfolio .portfolio-item .skill-bar { - background-color: #fff; - height: 6px; - margin: 0 5px; - position: relative; - width: 100%; -} - -#portfolio .portfolio-item .skill-bar:before { - background-color: #00baff; - content: ''; - height: 100%; - left: 0; - position: absolute; - top: 0; - transition: width 0.3s ease-out; -} - -#portfolio .portfolio-item .skill1 .skill-count1:after, -#portfolio .portfolio-item .skill2 .skill-count2:after, -#portfolio .portfolio-item .skill3 .skill-count3:after { - content: '%'; -} - -#portfolio .portfolio-item .skill-count1, -#portfolio .portfolio-item .skill-count2, -#portfolio .portfolio-item .skill-count3 { - color: #00baff; - font-size: 14px; - font-weight: 600; -} - -#portfolio .portfolio-item .skill1 .skill-bar:before { - width: 95%; -} - -#portfolio .portfolio-item .skill2 .skill-bar:before { - width: 85%; -} - -#portfolio .portfolio-item .skill3 .skill-bar:before { - width: 75%; -} - -#portfolio .portfolio-item .skill .skill-count { - color: #18d26e; - font-size: 24px; - font-weight: 700; - letter-spacing: 1px; - position: absolute; - right: -40px; - top: -35px; -} - -#portfolio .portfolio-item .skill1 .skill-count1 { - content: '95%'; -} - -#portfolio .portfolio-item .skill2 .skill-count2 { - content: '85%'; -} - -#portfolio .portfolio-item .skill3 .skill-count3 { - content: '75%'; -} -.project-choices { - display: flex; - flex-wrap: wrap; - justify-content: space-between; -} -.skill-bar { - background-color: #f5f5f5; - border-radius: 50px; - height: 15px; - position: relative; -} - -.skill-count1, -.skill-count2, -.skill-count3 { - font-size: 12px; - font-weight: 600; - position: absolute; - right: 0; - top: -25px; -} - -.skill1 .skill-bar:before { - background-color: #007bff; - width: 95%; -} - -.skill1 .skill-bar:before, -.skill2 .skill-bar:before { - border-radius: 50px; - bottom: 0; - content: ''; - left: 0; - position: absolute; - top: 0; -} - -.skill2 .skill-bar:before { - background-color: #dc3545; - width: 85%; -} - -.skill3 .skill-bar:before { - background-color: #ffc107; - border-radius: 50px; - bottom: 0; - content: ''; - left: 0; - position: absolute; - top: 0; - width: 75%; -} -#portfolio .portfolio-item .wrapper { - background-color: rgba(0, 0, 0, 0.7); - color: #fff; - margin-bottom: auto; - margin-top: auto; - position: absolute; - width: auto; -} -.modal-dialog { - margin: 1.75rem auto; - max-width: 800px; -} - -.modal-body { - padding: 20px 30px; -} - -.modal-content .close { - background-color: #f5f5f5; - padding: 1rem; - right: 0; - top: 0; - z-index: 1; -} - -.modal-content .close:hover { - color: #f6a50b; -} - -.modal-dialog { - max-width: 80%; -} - -.modal-header { - background-color: #f6a50b; - color: #fff; -} - -.modal-header .modal-title { - font-size: 24px; - font-weight: 600; -} - -.modal-body .project-date { - font-size: 14px; - font-weight: 700; - margin-top: 15px; -} - -.modal-body .project-description { - margin-top: 15px; -} - -.modal-body .project-description p { - margin-bottom: 5px; -} - -.modal-body .project-title-settings { - font-size: 20px; - font-weight: 600; - line-height: 24px; - margin-bottom: 0; - margin-top: 30px; -} - -.modal-body p { - font-size: 16px; - line-height: 24px; - margin: 0; -} - -.modal-body .project-description { - font-size: 16px; - line-height: 24px; - margin-bottom: 0; - margin-top: 30px; -} - -.modal-body ul { - margin-bottom: 0; - margin-top: 30px; - padding-left: 20px; -} - -.modal-body ul li { - font-size: 16px; - line-height: 24px; -} - -.modal-content { - border: none; - border-radius: 0; -} - -.modal-header { - border-bottom: none; -} - -.modal-footer { - border-top: none; -} - -.modal-content .close { - color: #222; - font-size: 28px; - font-weight: 400; - opacity: 1; - position: absolute; - right: 15px; - top: 15px; -} - -.modal-content .close:hover { - color: #4c4c4c; - opacity: 1; -} - -.modal-content .close:focus { - outline: none; -} - -.modal-dialog { - margin: 0; - max-width: none; - width: 100%; -} - -@media (min-width: 576px) { - .modal-dialog { - max-width: 750px; - } -} - -@media (min-width: 768px) { - .modal-dialog { - max-width: 970px; - } -} - -@media (min-width: 992px) { - .modal-dialog { - max-width: 1170px; - } - - .modal-content { - min-height: 600px; - } - - .modal-body { - padding: 60px 80px; - } - - .modal-body .project-title-settings { - font-size: 30px; - line-height: 36px; - margin-top: 40px; - } - - .modal-body .project-date { - font-size: 18px; - font-weight: 500; - margin-top: 10px; - } - - .modal-body .project-description { - font-size: 20px; - font-weight: 500; - line-height: 26px; - margin-top: 20px; - } - - .modal-body .project-link { - margin-top: 30px; - } - - .modal-body .project-link a { - background-color: #6c63ff; - border-color: #6c63ff; - border-radius: 5px; - color: #fff; - font-size: 20px; - font-weight: 600; - padding: 10px 25px; - text-decoration: none; - transition: all 0.3s ease; - } - - .modal-body .project-link a:hover { - background-color: #5b54d5; - border-color: #5b54d5; - transition: all 0.3s ease; - } -} -.title-styles { - font-family: Raleway, sans-serif; - font-size: 250%; -} -#about .background .transparentbox #portfolio .aboutCol { - align-items: center; - display: flex; - flex-grow: 1; - justify-content: center; - padding: 10px; - width: 50%; -} -.input-group > div { - border: 1px solid #ddd; - display: table-cell; - vertical-align: middle; -} - -.input-group-icon { - background: #eee; - color: #777; - padding: 0 12px; -} - -.input-group-area { - width: 100%; -} -.github-corner:hover .octo-arm { - -webkit-animation: octocat-wave 0.56s ease-in-out; - animation: octocat-wave 0.56s ease-in-out; -} - -@-webkit-keyframes octocat-wave { - 0%, - to { - -webkit-transform: rotate(0); - transform: rotate(0); - } - - 20%, - 60% { - -webkit-transform: rotate(-25deg); - transform: rotate(-25deg); - } - - 40%, - 80% { - -webkit-transform: rotate(10deg); - transform: rotate(10deg); - } -} - -@keyframes octocat-wave { - 0%, - to { - -webkit-transform: rotate(0); - transform: rotate(0); - } - - 20%, - 60% { - -webkit-transform: rotate(-25deg); - transform: rotate(-25deg); - } - - 40%, - 80% { - -webkit-transform: rotate(10deg); - transform: rotate(10deg); - } -} - -@media (max-width: 500px) { - .github-corner:hover .octo-arm { - -webkit-animation: none; - animation: none; - } - - .github-corner .octo-arm { - -webkit-animation: octocat-wave 0.56s ease-in-out; - animation: octocat-wave 0.56s ease-in-out; - } -} -.slider-image { - border: 5px solid #d7caaa; -} - -.slider-tab { - background-color: #d7caaa; - height: 25px; -} - -.slider-iconfiy { - margin-top: 10px; -} - -.styles_typicalWrapper__1_Uvh:after { - cursor: none !important; - display: none; -} -.polaroid span { - background: #fff; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); - display: inline-block; - margin: 55px 75px 30px; - padding: 15px 15px 30px; - position: relative; - text-align: center; - text-decoration: none; - transition: all 0.2s linear; - z-index: 0; -} - -.link-href { - color: #a3a3a3; -} - -.wave { - font-size: 160%; -} - -.font-trebuchet { - font-family: - Trebuchet MS, - Lucida Sans Unicode, - Lucida Grande, - Lucida Sans, - Arial, - sans-serif; -} - -#preview { - background-color: #ebeaf5; - margin-bottom: 15%; - padding: 15px; - position: relative; - width: 500px; -} -.modal-inside .modal-content { - background: #fff; -} - -.bars { - float: left; - padding: 0; - text-align: left; - width: 95%; -} - -.bars .skills { - list-style: none; - margin-top: 36px; -} - -.bars li { - background: #ccc; - border-radius: 3px; - height: 42px; - margin-bottom: 60px; - position: relative; -} - -.bars li em { - color: #313131; - font: - 15px opensans-bold, - sans-serif; - font-weight: 400; - letter-spacing: 2px; - position: relative; - text-transform: uppercase; - top: -36px; -} - -.bar-expand { - background: #313131; - border-radius: 3px 0 0 3px; - display: inline-block; - height: 42px; - left: 0; - line-height: 42px; - margin: 0; - padding-right: 24px; - position: absolute; - top: 0; -} - -.modal-close { - cursor: pointer; - padding: 10px 15px; - text-align: right; -} - -.close-icon { - color: #000; - font-weight: lighter !important; -} - -.modal-description { - font-size: 12px; - margin-bottom: 20px; - padding: 5px 5px 0; - text-align: justify; -} - -.awssld__next, -.awssld__prev { - outline: none !important; -} - -.loader-bar-color { - color: #000 !important; -} -.portfolio .portfolio-item .portfolio-item-caption { - background-color: rgba(51, 51, 51, 0.9); - opacity: 0; - transition: all 0.5s ease; -} - -.portfolio .portfolio-item .portfolio-item-caption:hover { - opacity: 1; -} - -.portfolio - .portfolio-item - .portfolio-item-caption - .portfolio-item-caption-content { - font-size: 1.5rem; -} - -@media (min-width: 576px) { - .portfolio .closeButtonResponsive { - display: block; - } - - .portfolio .portfolio-item { - margin-bottom: 30px; - } -} -@-webkit-keyframes cd-bounce-1 { - 0% { - opacity: 0; - -webkit-transform: scale(0.5); - } - - 60% { - opacity: 1; - -webkit-transform: scale(1.2); - } - - to { - -webkit-transform: scale(1); - } -} - -@keyframes cd-bounce-1 { - 0% { - opacity: 0; - -webkit-transform: scale(0.5); - transform: scale(0.5); - } - - 60% { - opacity: 1; - -webkit-transform: scale(1.2); - transform: scale(1.2); - } - - to { - -webkit-transform: scale(1); - transform: scale(1); - } -} - -@-webkit-keyframes cd-bounce-2 { - 0% { - opacity: 0; - -webkit-transform: translateX(-100px); - } - - 60% { - opacity: 1; - -webkit-transform: translateX(20px); - } - - to { - -webkit-transform: translateX(0); - } -} - -@keyframes cd-bounce-2 { - 0% { - opacity: 0; - -webkit-transform: translateX(-100px); - transform: translateX(-100px); - } - - 60% { - opacity: 1; - -webkit-transform: translateX(20px); - transform: translateX(20px); - } - - to { - -webkit-transform: translateX(0); - transform: translateX(0); - } -} - -@-webkit-keyframes cd-bounce-2-inverse { - 0% { - opacity: 0; - -webkit-transform: translateX(100px); - } - - 60% { - opacity: 1; - -webkit-transform: translateX(-20px); - } - - to { - -webkit-transform: translateX(0); - } -} - -@keyframes cd-bounce-2-inverse { - 0% { - opacity: 0; - -webkit-transform: translateX(100px); - transform: translateX(100px); - } - - 60% { - opacity: 1; - -webkit-transform: translateX(-20px); - transform: translateX(-20px); - } - - to { - -webkit-transform: translateX(0); - transform: translateX(0); - } -} diff --git a/src/assets/styles/ProductListing.css b/src/assets/styles/ProductListing.css deleted file mode 100644 index e6a0a59..0000000 --- a/src/assets/styles/ProductListing.css +++ /dev/null @@ -1,24 +0,0 @@ -/* src/styles/ProductListing.css */ -.product-listing { - padding: 20px; -} - -.product-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); - gap: 20px; -} - -.product-card { - border: 1px solid #ccc; - border-radius: 8px; - padding: 16px; -} - -.product-card img { - width: 100%; - height: 200px; - object-fit: cover; - border-radius: 8px; - margin-bottom: 10px; -} diff --git a/src/assets/styles/chakraStylesReset.css b/src/assets/styles/chakraStylesReset.css deleted file mode 100644 index 80fe103..0000000 --- a/src/assets/styles/chakraStylesReset.css +++ /dev/null @@ -1 +0,0 @@ -@import '@chakra-ui/css-reset'; diff --git a/src/assets/styles/reset.css b/src/assets/styles/reset.css deleted file mode 100644 index a3f7681..0000000 --- a/src/assets/styles/reset.css +++ /dev/null @@ -1,129 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ - v2.0 | 20110126 - License: none (public domain) -*/ - -html, -body, -div, -span, -applet, -object, -iframe, -h1, -h2, -h3, -h4, -h5, -h6, -p, -blockquote, -pre, -a, -abbr, -acronym, -address, -big, -cite, -code, -del, -dfn, -em, -img, -ins, -kbd, -q, -s, -samp, -small, -strike, -strong, -sub, -sup, -tt, -var, -b, -u, -i, -center, -dl, -dt, -dd, -ol, -ul, -li, -fieldset, -form, -label, -legend, -table, -caption, -tbody, -tfoot, -thead, -tr, -th, -td, -article, -aside, -canvas, -details, -embed, -figure, -figcaption, -footer, -header, -hgroup, -menu, -nav, -output, -ruby, -section, -summary, -time, -mark, -audio, -video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -menu, -nav, -section { - display: block; -} -body { - line-height: 1; -} -ol, -ul { - list-style: none; -} -blockquote, -q { - quotes: none; -} -blockquote:before, -blockquote:after, -q:before, -q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/src/assets/styles/themes.js b/src/assets/styles/themes.js deleted file mode 100644 index da1f593..0000000 --- a/src/assets/styles/themes.js +++ /dev/null @@ -1,29 +0,0 @@ -import { createTheme } from '@mui/material/styles'; - -const theme = createTheme({ - typography: { - fontFamily: "'Bebas Neue', sans-serif", - }, - breakpoints: { - values: { - xs: 0, - sm: 600, - md: 950, // Set md breakpoint to 950px - lg: 1280, - xl: 1920, - }, - }, - palette: { - background: { - default: '#fafafa', // You can set your default background color here - }, - primary: { - main: '#1976d2', // Add the main property with the desired color value - contrastText: '#ffffff', // You can set your contrast text color here - spacing: 4, // You can set your spacing scale here - }, - }, - spacing: 4, // You can set your spacing scale here -}); - -export default theme; diff --git a/src/themeSettings.jsx b/src/assets/themeSettings.jsx similarity index 62% rename from src/themeSettings.jsx rename to src/assets/themeSettings.jsx index 9814311..23cf87f 100644 --- a/src/themeSettings.jsx +++ b/src/assets/themeSettings.jsx @@ -1,4 +1,3 @@ -import { grey } from '@mui/material/colors'; import { tokens } from './tokens'; export const themeSettings = (mode) => { @@ -9,9 +8,24 @@ export const themeSettings = (mode) => { palette: { mode: mode, primary: { - main: mode === 'dark' ? colors.primary[500] : colors.primary[100], + // main: mode === 'dark' ? colors.primary[500] : colors.primary[100], + main: colors.greenAccent[500], + light: colors.greenAccent[200], + dark: colors.greenAccent[600], + default: colors.grey[700], + // contrastText: '#fff', contrastText: mode === 'dark' ? '#ffffff' : '#000000', + + // main: colors.greenAccent[500], }, + + chartColors: [ + colors.greenAccent[500], + colors.greenAccent[400], + colors.greenAccent[300], + colors.greenAccent[200], + colors.greenAccent[100], + ], primaryLight: { main: colors.primary[300], }, @@ -23,25 +37,39 @@ export const themeSettings = (mode) => { black: colors.grey[900], }, secondary: { - main: colors.greenAccent[500], + // main: colors.greenAccent[500], + main: colors.greenAccent[200], + light: colors.greenAccent[100], + dark: colors.greenAccent[400], + contrastText: '#000', }, neutral: { dark: colors.grey[700], - main: colors.grey[500], + main: colors.primary[300], light: colors.grey[100], }, background: { - default: mode === 'dark' ? '#121212' : '#ffffff', - paper: mode === 'dark' ? colors.grey[300] : '#ffffff', + main: '#333', + dark: '#222', + secondary: '#444', + tertiary: '#555', + quaternary: '#666', + quinternary: '#999', + default: mode === 'dark' ? '#777' : '#ffffff', + paper: colors.greenAccent[100], + // paper: mode === 'dark' ? colors.grey[300] : '#ffffff', }, error: { main: colors.redAccent[500], + dark: colors.redAccent[700], }, warning: { main: colors.redAccent[500], }, success: { + light: colors.greenAccent[100], main: colors.greenAccent[500], + dark: colors.greenAccent[200], }, info: { main: colors.blueAccent[500], @@ -55,6 +83,41 @@ export const themeSettings = (mode) => { hover: mode === 'dark' ? colors.grey[800] : colors.grey[200], }, }, + // chart: { + // palette: { + // primary: { + // main: colors.greenAccent[500], + // light: colors.greenAccent[300], + // dark: colors.greenAccent[700], + // contrastText: '#fff', + // }, + // secondary: { + // main: colors.greenAccent[200], + // light: colors.greenAccent[100], + // dark: colors.greenAccent[400], + // contrastText: '#000', + // }, + // background: { + // paper: colors.greenAccent[100], + // default: colors.greenAccent[200], + // }, + // text: { + // primary: colors.greenAccent[800], + // secondary: colors.greenAccent[600], + // }, + // chartColors: [ + // colors.greenAccent[500], + // colors.greenAccent[400], + // colors.greenAccent[300], + // colors.greenAccent[200], + // colors.greenAccent[100], + // ], + // }, + // typography: { + // // Define typography styles if needed + // }, + // }, + spacing: (factor) => `${0.25 * factor}rem`, shape: { borderRadius: 4, diff --git a/src/tokens.jsx b/src/assets/tokens.jsx similarity index 100% rename from src/tokens.jsx rename to src/assets/tokens.jsx diff --git a/src/components/buttons/actionButtons/CardActionButtons.jsx b/src/components/buttons/actionButtons/CardActionButtons.jsx index 0afd4bc..ea451a0 100644 --- a/src/components/buttons/actionButtons/CardActionButtons.jsx +++ b/src/components/buttons/actionButtons/CardActionButtons.jsx @@ -1,97 +1,171 @@ import React, { useCallback } from 'react'; -import { Button, Grid } from '@mui/material'; +import { Box, Button, Grid } from '@mui/material'; import { useStyles } from '../buttonStyles'; import useAppContext from '../../../context/hooks/useAppContext'; +import { useMode } from '../../../context/hooks/colormode'; +import { useCollectionStore } from '../../../context/CollectionContext/CollectionContext'; +import Logger from '../../grids/collectionGrids/Logger'; +import { useDeckStore } from '../../../context/DeckContext/DeckContext'; +import { useCartStore } from '../../../context/CartContext/CartContext'; +const cardOtherLogger = new Logger([ + 'Action', + 'Card Name', + 'Quantity', + 'Total Price', +]); +const CardActionButtons = ({ card, context, closeModal }) => { + const { theme } = useMode(); // Using the theme hook -const CardActionButtons = ({ card, quantity, context, closeModal }) => { const classes = useStyles(); - const [contextProps, isContextAvailable] = useAppContext(context); - - if (!isContextAvailable) { - console.error(`The component isn't wrapped with the ${context}Provider`); - return null; // Consider rendering an error boundary or a user-friendly error message instead. - } + const { contextProps, isContextAvailable } = useAppContext(context); // Changed to object destructuring + const { addOneToCollection, removeOneFromCollection } = useCollectionStore(); + const { addToDeck, removeFromDeck } = useDeckStore(); + const { addToCart, removeFromCart } = useCartStore(); + // const { totalQuantity } = context[contextProps?.totalQuantity]; + // const { totalQuantity } = useCollectionStore(); + // if (!isContextAvailable || !contextProps) { + // console.error(`The component isn't wrapped with the ${context}Provider`); + // return Context not available; + // } + const styles = { + box: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + padding: theme.spacing(2), // Using theme spacing + backgroundColor: theme.palette.background.paper, // Using theme background color + }, + button: { + margin: theme.spacing(1), // Using theme spacing + color: theme.palette.background.primary, // Using theme text color + backgroundColor: theme.palette.success.main, // Using theme background color + }, + gridItem: { + textAlign: 'center', + }, + }; const ADD = 'add'; const REMOVE_ONE = 'removeOne'; const REMOVE_ALL = 'removeAll'; + // const performAction = useCallback( + // (action, card) => { + // console.log( + // `action --> ${action}`, + // `payload --> ${card}`, + // `context --> ${context}` + // ); + // try { + // if (context === 'Collection') { + // if (action === ADD) { + // addOneToCollection(card, card.id); + // cardOtherLogger.logCardAction('Add Card', card); + // } else if (action === REMOVE_ONE) { + // removeOneFromCollection(card, card.id); + // cardOtherLogger.logCardAction('Remove Card', card); + // } + // } + // // Additional context handling can be implemented here + // } catch (error) { + // console.error( + // `Error performing action '${action}' with payload`, + // card, + // error + // ); + // } + // }, + // [context, addOneToCollection, removeOneFromCollection, cardOtherLogger] + // ); - // Action handler const performAction = useCallback( - (action, payload) => { - console.log(`action --> ${action}`, `payload --> ${payload}`); + (action, card) => { try { - switch (action) { - case ADD: - contextProps[context][`addOneTo${context}`](payload); + switch (context) { + case 'Collection': + if (action === 'add') { + addOneToCollection(card, card.id); + } else if (action === 'removeOne') { + removeOneFromCollection(card, card.id); + } break; - case REMOVE_ONE: - contextProps[context].removeOne(payload); + case 'Deck': + if (action === 'add') { + addToDeck(card, card.id); + } else if (action === 'removeOne') { + removeFromDeck(card, card.id); + } break; - case REMOVE_ALL: - contextProps[context].removeAll(payload); + case 'Cart': + if (action === 'add') { + addToCart(card, card.id); + } else if (action === 'removeOne') { + removeFromCart(card, card.id); + } break; default: - console.error(`Unhandled action type: ${action}`); + console.error(`Unhandled context: ${context}`); } + cardOtherLogger.logCardAction(`${action} Card`, card); } catch (error) { console.error( `Error performing action '${action}' with payload`, - payload, + card, error ); } }, - [context, contextProps] + [ + addOneToCollection, + removeOneFromCollection, + addToDeck, + removeFromDeck, + addToCart, + removeFromCart, + context, + ] ); const handleAddClick = () => { + console.log('handleAddClick', card); performAction(ADD, card); - closeModal(); + closeModal?.(); + }; + + const handleRemoveOne = () => { + performAction(REMOVE_ONE, card); + closeModal?.(); }; - const handleClickOutside = () => { - if (quantity === 0) { - closeModal(); - } + const handleRemoveAll = () => { + performAction(REMOVE_ALL, card); + closeModal?.(); }; return ( -
- {card.quantity > 0 ? ( - <> - - - {`In ${context}: `} {card.quantity} - - - - - - - + - - ) : ( - - )} -
+ + + + ); }; diff --git a/src/components/buttons/actionButtons/GenericActionButtons.jsx b/src/components/buttons/actionButtons/GenericActionButtons.jsx index ff7a5dc..820ecc4 100644 --- a/src/components/buttons/actionButtons/GenericActionButtons.jsx +++ b/src/components/buttons/actionButtons/GenericActionButtons.jsx @@ -1,63 +1,118 @@ -import React, { useEffect } from 'react'; -import { makeStyles } from '@mui/styles'; -import GenericCardModal from '../../modals/GenericCardModal'; +import React, { useContext } from 'react'; import CardActionButtons from './CardActionButtons'; import useAppContext from '../../../context/hooks/useAppContext'; +import { ModalContext } from '../../../context/ModalContext/ModalContext'; +import { Box } from '@mui/material'; -const useStyles = makeStyles({ - root: { - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-evenly', - height: '100%', - }, -}); - -const GenericActionButtons = ({ - card, - context, - open, - openModal, - closeModal, - isModalOpen, - setModalOpen, -}) => { - const classes = useStyles(); - const [contextProps, isContextAvailable] = useAppContext(context); - - if (!isContextAvailable) { - console.error(`The component isn't wrapped with the ${context}Provider`); - return null; // Consider rendering an error boundary or user-friendly error message instead. +const GenericActionButtons = ({ card, context }) => { + const contextProps = useAppContext(); // Assuming useAppContext returns the context object + const { closeModal, isModalOpen, setModalOpen } = useContext(ModalContext); + + if (!contextProps) { + return ( + Provider not found for {context} + ); } - // Ensure contextProps is an object with the expected methods before using them - if ( - typeof contextProps !== 'object' || - typeof contextProps[context] !== 'object' - ) { - console.error(`Invalid contextProps provided for the context: ${context}`); - return null; // Consider rendering an error boundary or user-friendly error message instead. + let modifiedContextProps = contextProps[context]; + + if (!modifiedContextProps) { + return ( + + Invalid context provided: {context} + + ); } return ( -
+ setModalOpen(false)} - open={open} - /> - {/* */} -
+ /> + ); }; export default GenericActionButtons; + +// import React, { useContext, useEffect } from 'react'; +// import { makeStyles } from '@mui/styles'; +// import GenericCardModal from '../../modals/GenericCardModal'; +// import CardActionButtons from './CardActionButtons'; +// import useAppContext from '../../../context/hooks/useAppContext'; +// import { ModalContext } from '../../../context/ModalContext/ModalContext'; + +// const useStyles = makeStyles({ +// root: { +// display: 'flex', +// flexDirection: 'column', +// justifyContent: 'space-evenly', +// height: '100%', +// }, +// }); + +// const GenericActionButtons = ({ +// card, +// context, +// open, +// openModal, +// // closeModal, +// // isModalOpen, +// // setModalOpen, +// }) => { +// const classes = useStyles(); +// const [contextProps, isContextAvailable] = useAppContext(context); +// const { +// openModalWithCard, +// closeModal, +// isModalOpen, +// setModalOpen, +// modalContent, +// } = useContext(ModalContext); +// if (!isContextAvailable) { +// console.error(`The component isn't wrapped with the ${context}Provider`); +// return null; // Consider rendering an error boundary or user-friendly error message instead. +// } + +// // Ensure contextProps is an object with the expected methods before using them +// if ( +// typeof contextProps !== 'object' || +// typeof contextProps[context] !== 'object' +// ) { +// console.error(`Invalid contextProps provided for the context: ${context}`); +// return null; // Consider rendering an error boundary or user-friendly error message instead. +// } + +// return ( +//
+// setModalOpen(false)} +// open={open} +// /> +// {/* */} +//
+// ); +// }; + +// export default GenericActionButtons; diff --git a/src/components/cards/CardToolTip.jsx b/src/components/cards/CardToolTip.jsx index 47585b9..14f9b48 100644 --- a/src/components/cards/CardToolTip.jsx +++ b/src/components/cards/CardToolTip.jsx @@ -2,25 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { makeStyles } from '@mui/styles'; import { Tooltip } from '@mui/material'; - -const useStyles = makeStyles((theme) => ({ - tooltip: { - display: 'flex', - flexDirection: 'column', - flexGrow: 1, - overflow: 'hidden', - }, - tooltipTitle: { - marginBottom: theme.spacing(1), - }, - attributeSpan: { - display: 'block', - marginBottom: theme.spacing(0.5), - }, - descriptionSpan: { - marginTop: theme.spacing(1), - }, -})); +import { useStyles } from './cardStyles'; const formatKey = (key) => { return key diff --git a/src/components/cards/GenericCard.jsx b/src/components/cards/GenericCard.jsx index 9bbcae6..18b2292 100644 --- a/src/components/cards/GenericCard.jsx +++ b/src/components/cards/GenericCard.jsx @@ -1,108 +1,216 @@ -import React, { useEffect } from 'react'; -import { Card, CardContent, CardActions, Typography } from '@mui/material'; +import React, { useContext, useEffect } from 'react'; +import { + Card, + CardContent, + CardActions, + Typography, + styled, +} from '@mui/material'; import CardMediaSection from '../media/CardMediaSection'; import GenericActionButtons from '../buttons/actionButtons/GenericActionButtons'; import placeholderImage from '../../assets/images/placeholder.jpeg'; -import { useStyles } from './cardStyles'; import { useCollectionStore } from '../../context/CollectionContext/CollectionContext'; +import { ModalContext } from '../../context/ModalContext/ModalContext'; +import { PopoverContext } from '../../context/PopoverContext/PopoverContext'; + +const AspectRatioBox = styled('div')(({ theme }) => ({ + width: '100%', // Full width of the parent container + // paddingTop: '2%', // Aspect ratio of 16:9 + position: 'relative', +})); + +const StyledCard = styled(Card)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + maxWidth: '100%', // Ensure it doesn't exceed the parent width + width: 'auto', // Adjust if needed + maxHeight: '80vh', // Max height based on the viewport height + backgroundColor: theme.palette.background.paper, + borderRadius: theme.shape.borderRadius, + boxShadow: theme.shadows[5], + transition: 'transform 0.3s ease-in-out', + '&:hover': { + transform: 'scale(1.03)', + }, +})); const GenericCard = React.forwardRef((props, ref) => { - const { - card, - context, - isModalOpen, - setModalOpen, - hoveredCard, - setHoveredCard, - setIsPopoverOpen, - setClickedCard, - } = props; - const classes = useStyles(); + const { card, context, setClickedCard } = props; const { selectedCollection } = useCollectionStore(); - const requiresDoubleButtons = context === 'Deck' || context === 'Collection'; // Added this line - const checkCardInCollection = () => { - if (selectedCollection) { - const cardIds = selectedCollection?.cards?.map((card) => card.id); - return cardIds?.includes(card.id); - } - return false; - }; + const { openModalWithCard, setModalOpen } = useContext(ModalContext); + const { setHoveredCard, setIsPopoverOpen, hoveredCard } = + useContext(PopoverContext); const handleClick = () => { - const cardIsInCollection = checkCardInCollection(); - console.log('Modal opened with card:', card); - console.log('Card is in collection:', cardIsInCollection); - + openModalWithCard(card); setModalOpen(true); setIsPopoverOpen(false); }; - // Function to handle hover interactions with the card const handleInteraction = (hoverState) => { - if (!isModalOpen) { - setHoveredCard((prev) => (hoverState ? card : null)); + if (!hoverState) { + setHoveredCard(hoverState ? card : null); setIsPopoverOpen(hoverState); } }; - // Effect to close popover when modal is open or reactivate when modal closes useEffect(() => { - setIsPopoverOpen(isModalOpen ? false : hoveredCard === card); - if (isModalOpen) { - setHoveredCard(null); - } - }, [isModalOpen, hoveredCard, card, setIsPopoverOpen, setHoveredCard]); + setIsPopoverOpen(hoveredCard === card); + }, [hoveredCard, card, setIsPopoverOpen]); - // Get the card image URL, or use placeholder if not available const imgUrl = card?.card_images?.[0]?.image_url || placeholderImage; const price = `Price: ${card?.card_prices?.[0]?.tcgplayer_price || 'N/A'}`; - console.log(typeof handleInteraction); // Should log 'function' return ( - - - + + + + + + {card?.name} {price} - - {/* Conditionally render action buttons based on context */} - {requiresDoubleButtons ? ( - <> - {/* */} - - - ) : ( - - )} + + - + ); }); GenericCard.displayName = 'GenericCard'; export default GenericCard; + +// import React, { useContext, useEffect } from 'react'; +// import { Card, CardContent, CardActions, Typography } from '@mui/material'; +// import CardMediaSection from '../media/CardMediaSection'; +// import GenericActionButtons from '../buttons/actionButtons/GenericActionButtons'; +// import placeholderImage from '../../assets/images/placeholder.jpeg'; +// import { useStyles } from './cardStyles'; +// import { useCollectionStore } from '../../context/CollectionContext/CollectionContext'; +// import { ModalContext } from '../../context/ModalContext/ModalContext'; +// import { PopoverContext } from '../../context/PopoverContext/PopoverContext'; + +// const GenericCard = React.forwardRef((props, ref) => { +// const { +// card, +// context, +// // isModalOpen, +// // setModalOpen, +// // hoveredCard, +// // setHoveredCard, +// // setIsPopoverOpen, +// setClickedCard, +// } = props; +// const classes = useStyles(); +// const { selectedCollection } = useCollectionStore(); +// const { +// openModalWithCard, +// closeModal, +// isModalOpen, +// setModalOpen, +// modalContent, +// } = useContext(ModalContext); +// const { setHoveredCard, setIsPopoverOpen, hoveredCard } = +// useContext(PopoverContext); + +// const requiresDoubleButtons = context === 'Deck' || context === 'Collection'; // Added this line +// const checkCardInCollection = () => { +// if (selectedCollection) { +// const cardIds = selectedCollection?.cards?.map((card) => card.id); +// return cardIds?.includes(card.id); +// } +// return false; +// }; + +// const handleClick = () => { +// const cardIsInCollection = checkCardInCollection(); +// console.log('Modal opened with card:', card); +// openModalWithCard(card); +// console.log('Card is in collection:', cardIsInCollection); + +// setModalOpen(true); +// setIsPopoverOpen(false); +// }; +// // setIsPopoverOpen(false); + +// // Function to handle hover interactions with the card +// // const handleInteraction = (hoverState) => { +// // if (!isModalOpen) { +// // setHoveredCard((prev) => (hoverState ? card : null)); +// // setIsPopoverOpen(hoverState); +// // } +// // }; +// const handleInteraction = (hoverState) => { +// if (!isModalOpen) { +// setHoveredCard(hoverState ? card : null); +// setIsPopoverOpen(hoverState); +// } +// }; +// // Effect to close popover when modal is open or reactivate when modal closes +// useEffect(() => { +// setIsPopoverOpen(isModalOpen ? false : hoveredCard === card); +// if (isModalOpen) { +// setHoveredCard(null); +// } +// }, [isModalOpen, hoveredCard, card, setIsPopoverOpen, setHoveredCard]); + +// // Get the card image URL, or use placeholder if not available +// const imgUrl = card?.card_images?.[0]?.image_url || placeholderImage; +// const price = `Price: ${card?.card_prices?.[0]?.tcgplayer_price || 'N/A'}`; +// console.log(typeof handleInteraction); // Should log 'function' + +// return ( +// +// +// +// {card?.name} +// +// {price} +// +// +// +// {requiresDoubleButtons ? ( +// <> +// +// {/* TODO fix card to display buttons for both collections and decks */} +// +// ) : ( +// +// )} +// +// +// ); +// }); + +// GenericCard.displayName = 'GenericCard'; + +// export default GenericCard; diff --git a/src/components/cards/cardStyles.jsx b/src/components/cards/cardStyles.jsx index fb39acc..95af636 100644 --- a/src/components/cards/cardStyles.jsx +++ b/src/components/cards/cardStyles.jsx @@ -41,6 +41,22 @@ export const useStyles = makeStyles((theme) => ({ justifyContent: 'center', // centers the buttons width: '100%', }, + tooltip: { + display: 'flex', + flexDirection: 'column', + flexGrow: 1, + overflow: 'hidden', + }, + tooltipTitle: { + marginBottom: theme.spacing(1), + }, + attributeSpan: { + display: 'block', + marginBottom: theme.spacing(0.5), + }, + descriptionSpan: { + marginTop: theme.spacing(1), + }, })); // import { makeStyles } from '@mui/styles'; diff --git a/src/components/chart/LinearChart.js b/src/components/chart/LinearChart.js index 054239e..d4c4be3 100644 --- a/src/components/chart/LinearChart.js +++ b/src/components/chart/LinearChart.js @@ -1,102 +1,93 @@ import React, { useState, useMemo, useCallback } from 'react'; import { ResponsiveLine } from '@nivo/line'; -import { makeStyles, useTheme } from '@mui/styles'; -import Typography from '@mui/material/Typography'; -import Box from '@mui/material/Box'; -import Tooltip from '@mui/material/Tooltip'; +import { Typography, Box, Tooltip, useTheme } from '@mui/material'; +import { styled } from '@mui/material/styles'; import ChartErrorBoundary from './ChartErrorBoundary'; -// import CustomLogger from '../../context/CutstomLogger'; -import { - formatDateToString, - useEventHandlers, - getFilteredData, - getTickValues, - getAveragedData, - CustomTooltip, -} from './chartUtils'; +import { tokens } from '../../assets/tokens'; +import { useMode } from '../../context/hooks/colormode'; -const useStyles = makeStyles((theme) => ({ - chartContainer: { - position: 'relative', - width: '100%', - height: '100%', - }, - loadingContainer: { - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - height: '100%', - }, - xAxisLabel: { - position: 'absolute', - bottom: 0, - left: '50%', - transform: 'translateX(-50%)', - textAlign: 'center', - marginTop: theme.spacing(2), - fontSize: '1rem', - fontWeight: 'bold', - marginBottom: theme.spacing(4), - color: theme.palette.text.primary, - }, - yAxisLabel: { - position: 'absolute', - top: '50%', - left: 0, - transform: 'translateY(-50%) rotate(-90deg)', - textAlign: 'center', - marginTop: theme.spacing(2), - fontSize: '1rem', - fontWeight: 'bold', // Make the label text bold - marginLeft: theme.spacing(4), // Increase spacing from the chart - color: theme.palette.text.primary, - }, - customTooltip: { - borderRadius: theme.shape.borderRadius, - padding: theme.spacing(1), - backgroundColor: theme.palette.background.paper, - boxShadow: theme.shadows[3], - }, - customGridLine: { - stroke: theme.palette.text.secondary, - strokeWidth: 2, - strokeDasharray: 'none', - }, +const AxisLabel = styled(Typography)(({ theme, axis }) => ({ + position: 'absolute', + [axis === 'x' ? 'bottom' : 'top']: 0, + [axis === 'x' ? 'left' : 'right']: '50%', + transform: + axis === 'x' ? 'translateX(-50%)' : 'translateY(-50%) rotate(-90deg)', + textAlign: 'center', + margin: theme.spacing(2), + fontSize: '1rem', + fontWeight: 'bold', + color: theme.palette.text.primary, })); -// function logReadableChartInfo( -// // chartDimensions, -// dataForChart, -// datesTimesValues, -// filteredChartData, -// latestData -// ) { -// console.log('[7][DATA FOR CHART]:', JSON.stringify(dataForChart, null, 2)); -// console.log( -// '[8][DATES TIMES VALUES]:', -// JSON.stringify(datesTimesValues, null, 2) -// ); -// console.log( -// '[4][FILTERED CHART DATA]:', -// JSON.stringify(filteredChartData, null, 2) -// ); -// console.log('[5][LATEST DATA]:', JSON.stringify(latestData, null, 2)); -// } +const CustomTooltip = ({ point }) => { + const theme = useTheme(); + const { serieId, data: { label, xFormatted, yFormatted } = {} } = point; + return ( + + + + {`Card: ${label}`} + + + {`Time: ${new Date(xFormatted).toLocaleString()}`} + + + {`Value: $${parseFloat(yFormatted).toFixed(2)}`} + + + + ); +}; + +export const useEventHandlers = () => { + const [hoveredData, setHoveredData] = useState(null); + const handleMouseMove = useCallback((point) => { + setHoveredData(point ? { x: point.data.x, y: point.data.y } : null); + }, []); + const handleMouseLeave = useCallback(() => setHoveredData(null), []); + return { hoveredData, handleMouseMove, handleMouseLeave }; +}; const LinearChart = ({ filteredChartData, - datesTimesValues, nivoReadyData, - latestData, dimensions, - timeRanges, timeRange, }) => { - const theme = useTheme(); + const { theme } = useMode(); + // const colors = tokens(theme.palette.mode); + const greenAccent = { + 100: '#dbf5ee', + 200: '#b7ebde', + 300: '#94e2cd', + 400: '#70d8bd', + 500: '#4cceac', + 600: '#3da58a', + 700: '#2e7c67', + 800: '#1e5245', + 900: '#0f2922', + }; const [isZoomed, setIsZoomed] = useState(false); - const { hoveredData, handleMouseMove, handleMouseLeave } = useEventHandlers(); - const [format, setFormat] = useState('0,0'); - // Calculate tickValues and xFormat based on timeRange + const { handleMouseMove, handleMouseLeave } = useEventHandlers(); + + // const { tickValues, xFormat } = useMemo(() => { + // const timeFormats = { + // '2 hours': { format: '%H:%M', ticks: 'every 15 minutes' }, + // '24 hours': { format: '%H:%M', ticks: 'every 1 hour' }, + // '7 days': { format: '%b %d', ticks: 'every 1 day' }, + // '1 month': { format: '%b %d', ticks: 'every 3 days' }, + // default: { format: '%b %d', ticks: 'every 1 day' }, + // }; + // return timeFormats[timeRange] || timeFormats.default; + // }, [timeRange]); + const { tickValues, xFormat } = useMemo(() => { let format, ticks; switch (timeRange) { @@ -133,16 +124,18 @@ const LinearChart = ({ ); } + const chartProps = { + // theme: theme.chart, margin: { top: 50, right: 110, bottom: 50, left: 60 }, - // data: [{ id: 'Data', data: dataForChart }], data: nivoReadyData, animate: true, motionStiffness: 90, motionDamping: 15, + background: '#2c2121', + xScale: { type: 'time', - // format: '%Y-%m-%d %H:%M:%S', format: '%Y-%m-%dT%H:%M:%S.%LZ', useUTC: false, precision: 'second', @@ -150,15 +143,12 @@ const LinearChart = ({ xFormat: 'time:%Y-%m-%d %H:%M:%S', axisBottom: { tickRotation: 0, - legendOffset: -12, + legendOffset: -24, legend: 'Time', tickPadding: 10, - tickSize: 10, - // format: '%b %d', - // tickValues: 'every 2 days', + tickSize: 1, format: xFormat, tickValues: tickValues, - // tickValues: tickValues, }, yScale: { type: 'linear', min: 'auto', max: 'auto' }, @@ -171,14 +161,60 @@ const LinearChart = ({ tickPadding: 10, tickSize: 10, }, - pointSize: 6, - pointBorderWidth: 1, - pointColor: theme.palette.primary.main, - colors: theme.palette.chartColors, - theme: theme.chart, + pointSize: 8, + pointBorderWidth: 2, + + pointColor: theme.palette.success.light, + colors: theme.palette.primaryDark.main, lineWidth: 3, curve: 'monotoneX', useMesh: true, + theme: { + chart: { + axis: { + domain: { + line: { + stroke: greenAccent[800], + strokeWidth: 1, + }, + }, + ticks: { + line: { + stroke: greenAccent[700], + strokeWidth: 1, + }, + text: { + fill: greenAccent[900], + fontSize: 12, + }, + }, + }, + grid: { + line: { + stroke: greenAccent[200], + strokeWidth: 1, + }, + }, + legends: { + text: { + fill: greenAccent[800], + fontSize: 12, + }, + }, + tooltip: { + container: { + background: greenAccent[100], + color: greenAccent[800], + fontSize: 12, + borderRadius: 4, + boxShadow: '0 2px 4px rgba(0,0,0,0.25)', + }, + }, + points: { + borderColor: greenAccent[800], + }, + }, + }, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, onClick: () => setIsZoomed(!isZoomed), @@ -187,14 +223,16 @@ const LinearChart = ({ // const point = slice.points.find( // (p) => p.id === 'Data' && p.data.x === latestData.x // ); - // return point ? : null; - // }, + // xFormat, + // tickValues, }; return (
+ {/* Time + Value ($) */}
); diff --git a/src/components/chart/PortfolioChart.jsx b/src/components/chart/PortfolioChart.jsx index d120048..ccaa994 100644 --- a/src/components/chart/PortfolioChart.jsx +++ b/src/components/chart/PortfolioChart.jsx @@ -8,7 +8,7 @@ import { useCombinedContext } from '../../context/CombinedProvider'; import debounce from 'lodash/debounce'; import { convertDataForNivo2, - getFilteredData2, + getUniqueValidData, groupAndAverageData, } from './chartUtils'; @@ -28,48 +28,55 @@ const ChartPaper = styled(Paper)(({ theme }) => ({ margin: theme.spacing(2, 0), })); +function handleThresholdUpdate(lastUpdateTime, setLastUpdateTime) { + const currentTime = new Date().getTime(); + if (!lastUpdateTime || currentTime - lastUpdateTime >= 600000) { + // 10 minutes + setLastUpdateTime(currentTime); + return currentTime; + } + return lastUpdateTime; +} +const getFilteredData2 = (collection) => { + if (!collection) { + console.error('Invalid input: selectedCollection should not be null'); + return []; + } + return getUniqueValidData(collection.currentChartDataSets2 || []); +}; const PortfolioChart = () => { const theme = useTheme(); const { latestData, setLatestData, timeRange } = useChartContext(); const [lastUpdateTime, setLastUpdateTime] = useState(null); const chartContainerRef = useRef(null); - // const [nivoReadyData2, setNivoReadyData2] = useState(null); // Declare state for nivoReadyData2 - const [chartDimensions, setChartDimensions] = useState({ width: 0, height: 0, }); const { selectedCollection } = useCollectionStore(); - const filteredChartData2 = getFilteredData2(selectedCollection); - // let nivoReadyData2 = []; - // const rawData2 = useMemo( - // () => groupAndAverageData(filteredChartData2, threshold), - // [filteredChartData2, threshold] - // ); - - const handleThresholdUpdate = () => { - const currentTime = new Date().getTime(); - if (!lastUpdateTime || currentTime - lastUpdateTime >= 600000) { - // 10 minutes - setLastUpdateTime(currentTime); - return currentTime; - } - return lastUpdateTime; - }; - - const threshold = handleThresholdUpdate(); + const threshold = useMemo( + () => handleThresholdUpdate(lastUpdateTime, setLastUpdateTime), + [lastUpdateTime] + ); + const filteredChartData2 = useMemo( + () => getFilteredData2(selectedCollection), + [selectedCollection] + ); const rawData2 = useMemo( () => groupAndAverageData(filteredChartData2, threshold), [filteredChartData2, threshold] ); - const nivoReadyData2 = useMemo( () => convertDataForNivo2(rawData2), [rawData2] ); - const HEIGHT_TO_WIDTH_RATIO = 2 / 3; + console.log('Selected Collection:', selectedCollection); + console.log('Filtered Chart Data:', filteredChartData2); + console.log('Raw Data:', rawData2); + console.log('Nivo Ready Data:', nivoReadyData2); + const HEIGHT_TO_WIDTH_RATIO = 7 / 10; useEffect(() => { const handleResize = debounce(() => { @@ -98,19 +105,32 @@ const PortfolioChart = () => { alignItems: 'center', padding: theme.spacing(2), gap: theme.spacing(2), - backgroundColor: theme.palette.background.paper, - color: theme.palette.text.primary, + // backgroundColor: theme.palette.background.paper, + background: '#333', // Darker background for Paper + + color: '#fff', // White text color + border: '1px solid #555', + borderRadius: 2, + // color: theme.palette.text.primary, }} > - + - {filteredChartData2.length > 0 ? ( + {filteredChartData2?.length > 0 && rawData2?.length > 0 ? ( { @@ -49,16 +49,7 @@ export const getUniqueValidData = (currentChartData) => { y: entry.y, })); }; -export const getFilteredData2 = (selectedCollection) => { - const filteredChartData2 = useMemo(() => { - // const { selectedCollection } = useCollectionStore(); - const allXYValues2 = selectedCollection?.currentChartDataSets2; - // console.log('ALL XY VALUES:', allXYValues2); - return allXYValues2 ? getUniqueValidData(allXYValues2) : []; - }, [selectedCollection]); - - return filteredChartData2; -}; + // export const groupAndAverageData = (data, threshold = 600000) => { // // 10 minutes in milliseconds // if (!data || data.length === 0) return { dates: [], times: [], values: [] }; @@ -194,65 +185,65 @@ export const getTickValues = (timeRange) => { return mapping[timeRange] || 'every day'; // Default to 'every week' if no match }; -export const useMovingAverage = (data, numberOfPricePoints) => { - return useMemo(() => { - if (!Array.isArray(data)) { - return []; - } - console.log('[1][Data]----------> [', data + ']'); - console.log( - '[2][NUMBER OF POINTS]----------> [', - numberOfPricePoints + ']' - ); - - return data.map((row, index, total) => { - const start = Math.max(0, index - numberOfPricePoints); - const end = index; - const subset = total.slice(start, end + 1); - const sum = subset.reduce((a, b) => a + b.y, 0); - return { - // x: String(row.x), - x: row.x, - y: sum / subset.length || 0, - }; - }); - }, [data, numberOfPricePoints]); -}; +// export const useMovingAverage = (data, numberOfPricePoints) => { +// return useMemo(() => { +// if (!Array.isArray(data)) { +// return []; +// } +// console.log('[1][Data]----------> [', data + ']'); +// console.log( +// '[2][NUMBER OF POINTS]----------> [', +// numberOfPricePoints + ']' +// ); -export const convertDataForNivo = ({ dates, times, values }) => { - if ( - !dates || - !times || - !values || - dates.length !== times.length || - dates.length !== values.length - ) { - console.error('Invalid or mismatched data arrays provided'); - return []; - } +// return data.map((row, index, total) => { +// const start = Math.max(0, index - numberOfPricePoints); +// const end = index; +// const subset = total.slice(start, end + 1); +// const sum = subset.reduce((a, b) => a + b.y, 0); +// return { +// // x: String(row.x), +// x: row.x, +// y: sum / subset.length || 0, +// }; +// }); +// }, [data, numberOfPricePoints]); +// }; - // Assuming that dates, times, and values arrays are of equal length - const nivoData = dates.map((date, index) => { - const dateTime = new Date(`${date} ${times[index]}`); - // Nivo chart requires the date to be either a Date object or an ISO date string - return { - x: dateTime.toISOString(), - y: values[index], - }; - }); +// export const convertDataForNivo = ({ dates, times, values }) => { +// if ( +// !dates || +// !times || +// !values || +// dates.length !== times.length || +// dates.length !== values.length +// ) { +// console.error('Invalid or mismatched data arrays provided'); +// return []; +// } - // Wrapping the data for a single series, you can add more series similarly - return [ - { - id: 'Averaged Data', - data: nivoData, - }, - ]; -}; +// // Assuming that dates, times, and values arrays are of equal length +// const nivoData = dates.map((date, index) => { +// const dateTime = new Date(`${date} ${times[index]}`); +// // Nivo chart requires the date to be either a Date object or an ISO date string +// return { +// x: dateTime.toISOString(), +// y: values[index], +// }; +// }); + +// Wrapping the data for a single series, you can add more series similarly +// return [ +// { +// id: 'Averaged Data', +// data: nivoData, +// }, +// ]; +// }; export const convertDataForNivo2 = (rawData2) => { - if (!Array.isArray(rawData2) || rawData2.length === 0) { - console.error('Invalid or empty rawData provided'); + if (!Array.isArray(rawData2) || rawData2?.length === 0) { + console.error('Invalid or empty rawData provided', rawData2); return []; } @@ -268,50 +259,12 @@ export const convertDataForNivo2 = (rawData2) => { return [ { id: 'Averaged Data', - color: 'hsl(0, 100%, 50%)', + color: '#4cceac', data: nivoData, }, ]; }; -export const CustomTooltip = ({ point }) => { - const theme = useTheme(); - const { serieId, data } = point; - const { label, xFormatted, yFormatted } = data || {}; - const series = { - type: { - Collection: 'Collection', - Card: 'Card', - Deck: 'Deck', - }, - }; - return ( - - - {`Series: ${serieId}`} - - {series.type[label] === 'Card' && ( - - {`Card: ${label}`} - - )} - - {`Time: ${new Date(xFormatted).toLocaleString()}`} - - - {`Value: $${parseFloat(yFormatted).toFixed(2)}`} - - - ); -}; - const roundToNearestTenth = (value) => { return Math.round(value * 10) / 10; }; @@ -330,25 +283,6 @@ export const getFilteredData = (data, timeRange) => { .map((d) => ({ ...d, y: roundToNearestTenth(d.y) })); }; -export const useEventHandlers = () => { - const [hoveredData, setHoveredData] = useState(null); - - const handleMouseMove = useCallback((point) => { - if (point) { - setHoveredData({ - x: point.data.x, - y: point.data.y, - }); - } - }, []); - - const handleMouseLeave = useCallback(() => { - setHoveredData(null); - }, []); - - return { hoveredData, handleMouseMove, handleMouseLeave }; -}; - export const formatDateToString = (date) => { if (!(date instanceof Date) || isNaN(date.getTime())) { console.error('Invalid date:', date); diff --git a/src/AppWrapper.jsx b/src/components/cleanUp/AppWrapper.jsx similarity index 100% rename from src/AppWrapper.jsx rename to src/components/cleanUp/AppWrapper.jsx diff --git a/src/components/cleanUp/CollectionActionButtons.jsx b/src/components/cleanUp/CollectionActionButtons.jsx index 6290010..a5ddde6 100644 --- a/src/components/cleanUp/CollectionActionButtons.jsx +++ b/src/components/cleanUp/CollectionActionButtons.jsx @@ -3,7 +3,7 @@ import { makeStyles } from '@mui/styles'; import { useCollectionStore } from '../../context/CollectionContext/CollectionContext'; // Assuming you have a separate context for Collections import CardActionButtons from './CardActionButtons'; import CollectionDialog from './CollectionDialog'; // Assuming you have a separate dialog for Collections -import CardCountDisplay from '../other/CardCountDisplay'; +import CardCountDisplay from '../other/dataDisplay/CardCountDisplay'; import { useCartStore } from '../../context/CartContext/CartContext'; const useStyles = makeStyles({ diff --git a/src/components/cleanUp/Hero.jsx b/src/components/cleanUp/Hero.jsx new file mode 100644 index 0000000..7bc8866 --- /dev/null +++ b/src/components/cleanUp/Hero.jsx @@ -0,0 +1,49 @@ +// import React from 'react'; +// import { Box, Container, Typography } from '@mui/material'; +// import { makeStyles } from '@mui/styles'; +// import { Canvas } from '@react-three/fiber'; +// import Cube from '../../assets/animations/Cube'; + +// const useStyles = makeStyles((theme) => ({ +// bannerBox: { +// backgroundImage: `linear-gradient(to right, ${theme.palette.primary.light}, ${theme.palette.primary.main})`, +// minHeight: '100vh', +// padding: theme.spacing(4), +// display: 'flex', +// alignItems: 'center', +// justifyContent: 'center', +// }, +// })); + +// const Hero = () => { +// const classes = useStyles(); +// return ( +// +// +// +// {/* eslint-disable-next-line react/no-unknown-property */} +// +// +// +// +// +// Buy Amazing Cards +// +// +// +// ); +// }; + +// export default Hero; diff --git a/src/theme.jsx b/src/components/cleanUp/theme.jsx similarity index 100% rename from src/theme.jsx rename to src/components/cleanUp/theme.jsx diff --git a/src/components/collection/CardPortfolio.jsx b/src/components/collection/CardPortfolio.jsx index 59bf124..aa2b248 100644 --- a/src/components/collection/CardPortfolio.jsx +++ b/src/components/collection/CardPortfolio.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useState } from 'react'; import SelectCollection from './SelectCollection'; -import PortfolioContent from '../content/PortfolioContent'; +import PortfolioContent from '../../containers/collectionPageContainers/PortfolioContent'; import { Box, Typography } from '@mui/material'; import CollectionContainer from '../../containers/collectionPageContainers/CollectionContainer'; import { useCollectionStore } from '../../context/CollectionContext/CollectionContext'; @@ -89,7 +89,7 @@ const CardPortfolio = ({ allCollections }) => { display="flex" alignItems="center" justifyContent="center" - minHeight="100vh" + // minHeight="100vh" backgroundColor="#f1f1f1" > No Collection Selected diff --git a/src/components/collection/SelectCollection.jsx b/src/components/collection/SelectCollection.jsx index 8fd8b84..ba03979 100644 --- a/src/components/collection/SelectCollection.jsx +++ b/src/components/collection/SelectCollection.jsx @@ -15,7 +15,7 @@ const useStyles = makeStyles((theme) => ({ justifyContent: 'space-between', alignItems: 'stretch', height: '100%', - width: '50vw', + // width: '50vw', padding: theme.spacing(2), }, button: { diff --git a/src/components/content/PortfolioContent.jsx b/src/components/content/PortfolioContent.jsx deleted file mode 100644 index 57d9f1e..0000000 --- a/src/components/content/PortfolioContent.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import { Box, Paper } from '@mui/material'; -import PortfolioHeader from '../headings/PortfolioHeader'; -import PortfolioListContainer from '../../containers/PortfolioListContainer'; -import PortfolioChartContainer from '../../containers/PortfolioChartContainer'; - -const PortfolioContent = ({ error, selectedCards, removeCard }) => { - return ( - - - - - - - - - - - - ); -}; - -export default PortfolioContent; diff --git a/src/components/forms/SearchForm.jsx b/src/components/forms/SearchForm.jsx new file mode 100644 index 0000000..358bc91 --- /dev/null +++ b/src/components/forms/SearchForm.jsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { TextField, Button, Paper } from '@mui/material'; +import { makeStyles } from '@mui/styles'; +import { useMode } from '../../context/hooks/colormode'; + +const useStyles = makeStyles((theme) => ({ + formContainer: { + width: '100%', + padding: theme.spacing(3), + borderRadius: theme.shape.borderRadius, + backgroundColor: theme.palette.background.default, + boxShadow: theme.shadows[3], // Use the theme's predefined shadows + maxWidth: 400, + margin: 'auto', + }, + form: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + padding: theme.spacing(2), + }, + textField: { + '& label.Mui-focused': { + color: theme.palette.primary.main, + }, + '& .MuiOutlinedInput-root': { + '&:hover fieldset': { + borderColor: theme.palette.primary.light, + }, + '&.Mui-focused fieldset': { + borderColor: theme.palette.primary.main, + }, + }, + }, + submitButton: { + backgroundColor: theme.palette.success.main, + color: theme.palette.getContrastText(theme.palette.success.main), + '&:hover': { + backgroundColor: theme.palette.success.dark, + }, + }, +})); + +const SearchForm = ({ searchTerm, handleChange, handleSubmit }) => { + const { theme } = useMode(); + const classes = useStyles(theme); + + return ( + +
+ + + +
+ ); +}; + +export default SearchForm; diff --git a/src/components/forms/customerCheckoutForm/CustomerForm.js b/src/components/forms/customerCheckoutForm/CustomerForm.js index 080959d..854c76e 100644 --- a/src/components/forms/customerCheckoutForm/CustomerForm.js +++ b/src/components/forms/customerCheckoutForm/CustomerForm.js @@ -1,17 +1,25 @@ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useContext } from 'react'; import { Box, Container, Typography } from '@mui/material'; import { useCartStore } from '../../../context/CartContext/CartContext'; import CustomerInfoFields from './CustomerInfoFields'; import CartActions from './CartActions'; import StripeCheckoutModal from '../../modals/stripeModal/StripeCheckoutModal'; +import { ModalContext } from '../../../context/ModalContext/ModalContext'; const CustomerForm = () => { const { getTotalCost, cartData } = useCartStore(); - const [isModalOpen, setIsModalOpen] = useState(false); - - const handleModalOpen = useCallback(() => setIsModalOpen(true), []); - const handleModalClose = useCallback(() => setIsModalOpen(false), []); - + // const [isModalOpen, setIsModalOpen] = useState(false); + const { + openModalWithCard, + closeModal, + isModalOpen, + setModalOpen, + modalContent, + } = useContext(ModalContext); + // const handleModalOpen = useCallback(() => setIsModalOpen(true), []); + // const handleModalClose = useCallback(() => setIsModalOpen(false), []); + const handleModalOpen = useCallback(() => setModalOpen(true), []); + const handleModalClose = useCallback(() => setModalOpen(false), []); const onToken = useCallback( (token) => { console.log(token); diff --git a/src/components/grids/DeckDisplay.js b/src/components/grids/DeckDisplay.js index fcce34c..c5e9be0 100644 --- a/src/components/grids/DeckDisplay.js +++ b/src/components/grids/DeckDisplay.js @@ -1,5 +1,5 @@ import React, { useContext, useEffect, useState } from 'react'; -import { Paper, Button } from '@mui/material'; +import { Paper, Button, Typography, Box } from '@mui/material'; import { DeckContext } from '../../context/DeckContext/DeckContext'; import DeckButtonList from './deckBuilderGrids/DeckButtonList'; import CardsGrid from './deckBuilderGrids/CardsGrid'; @@ -7,24 +7,39 @@ import DeckEditPanel from '../other/DeckEditPanel'; import { makeStyles } from '@mui/styles'; const useStyles = makeStyles((theme) => ({ - root: { backgroundColor: '#f4f6f8' }, - paper: { + root: { padding: theme.spacing(3), - borderRadius: '10px', - boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.1)', + backgroundColor: theme.palette.background.default, + border: `1px solid ${theme.palette.divider}`, + borderRadius: theme.shape.borderRadius, + margin: 'auto', // Centering the form }, - deckEditPanel: { + paper: { padding: theme.spacing(2), - backgroundColor: '#ffffff', - border: '1px solid #ddd', - borderRadius: theme.spacing(1), - boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.1)', - transition: 'opacity 0.5s ease-in-out', + borderRadius: theme.shape.borderRadius, + boxShadow: theme.shadows[4], + backgroundColor: theme.palette.background.paper, + }, + button: { + margin: theme.spacing(1), + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, + '&:hover': { + backgroundColor: theme.palette.primary.dark, + }, }, noCardsText: { - color: '#666', + marginTop: theme.spacing(2), + textAlign: 'center', + color: theme.palette.text.secondary, fontStyle: 'italic', }, + title: { + fontWeight: 'bold', + color: theme.palette.text.primary, + marginBottom: theme.spacing(2), + }, + // Other styles... })); const DeckDisplay = ({ userDecks = [] }) => { @@ -46,9 +61,15 @@ const DeckDisplay = ({ userDecks = [] }) => { }; return ( -
+ - {showAllDecks && ( @@ -61,16 +82,17 @@ const DeckDisplay = ({ userDecks = [] }) => { )} {selectedCards.length > 0 ? ( ) : ( -
No cards to display
+ + No cards to display + )}
-
+ ); }; diff --git a/src/components/grids/collectionGrids/CardList.jsx b/src/components/grids/collectionGrids/CardList.jsx index 6b074b9..787831d 100644 --- a/src/components/grids/collectionGrids/CardList.jsx +++ b/src/components/grids/collectionGrids/CardList.jsx @@ -14,13 +14,17 @@ import { TableRow, Button, TableHead, + useTheme, + ButtonGroup, + Divider, } from '@mui/material'; import AssessmentIcon from '@mui/icons-material/Assessment'; import TablePaginationActions from './TablePaginationActions'; -import Logger from './Logger'; import PropTypes from 'prop-types'; import { useCollectionStore } from '../../../context/CollectionContext/CollectionContext'; - +import { makeStyles, styled } from '@mui/styles'; +import { useMode } from '../../../context/hooks/colormode'; +import Logger from './Logger'; // Instantiate logger outside of the component const cardLogger = new Logger([ 'Action', @@ -29,7 +33,100 @@ const cardLogger = new Logger([ 'Total Price', ]); +const StyledContainer = styled(Container)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + height: '100%', + alignItems: 'center', + background: theme.palette.background.dark, + color: '#fff', // White text color + padding: 2, + borderRadius: 2, +})); + +// const Paper = styled(MuiPaper)(({ theme }) => ({ +// padding: theme.spacing(2), +// borderRadius: 2, +// width: '100%', +// color: '#fff', +// background: theme.palette.background.main, +// })); + +const StyledButtonGroup = styled(ButtonGroup)(({ theme }) => ({ + display: 'flex', + width: '100%', + borderRadius: '5px', + overflow: 'hidden', + + flexDirection: 'column', + justifyContent: 'space-between', + alignItems: 'center', + boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)', +})); + +// const TableContainer = styled(MuiTableContainer)(({ theme }) => ({ +// background: theme.palette.background.secondary, +// })); + +// const TableHead = styled(MuiTableHead)(({ theme }) => ({ +// backgroundColor: '#555', +// color: '#fff', +// })); + +// const TablePagination = styled(MuiTablePagination)(({ theme }) => ({ +// color: '#fff', +// })); +const StyledTableHeader = styled(TableHead)(({ theme }) => ({ + backgroundColor: '#555', // Darker shade for header + color: '#fff', +})); +// const TableFooter = styled(MuiTableFooter)(({ theme }) => ({ +// color: '#fff', +// })); + +// const TableBody = styled(MuiTableBody)(({ theme }) => ({ +// color: '#fff', +// })); + +// const Table = styled(MuiTable)(({ theme }) => ({ +// color: '#fff', +// })); + +const StyledTableCell = styled(TableCell)(({ theme }) => ({ + color: '#ddd', // Lighter text for better readability +})); +const Title = styled(TableCell)(({ theme }) => ({ + color: '#4cceac', // Green color for the title +})); + +// const Button = styled(MuiButton)(({ theme }) => ({ +// margin: theme.spacing(1), +// color: theme.palette.primary.contrastText, +// backgroundColor: theme.palette.primary.main, +// '&:hover': { +// backgroundColor: theme.palette.primary.dark, +// }, +// })); + +// const ButtonGroup = styled(MuiButtonGroup)(({ theme }) => ({ +// display: 'flex', +// width: '100%', +// borderRadius: '5px', +// overflow: 'hidden', +// flexDirection: 'column', +// justifyContent: 'space-between', +// alignItems: 'center', +// boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)', +// })); + +// const TablePaginationActions = styled(MuiTablePaginationActions)( +// ({ theme }) => ({ +// color: '#fff', +// }) +// ); + const CardList = ({ selectedCards }) => { + const { theme } = useMode(); const { getTotalCost, selectedCollection, @@ -43,6 +140,10 @@ const CardList = ({ selectedCards }) => { const [rowsPerPage, setRowsPerPage] = useState(5); const chartContainerRef = useRef(null); const count = selectedCards?.length || 0; + const [chartDimensions, setChartDimensions] = useState({ + width: 0, + height: 0, + }); const emptyRows = useMemo(() => { // <-- Use useMemo for better performance return page > 0 @@ -70,58 +171,62 @@ const CardList = ({ selectedCards }) => { addOneToCollection(card, card.id); cardLogger.logCardAction('Add Card', card); }; - // let totalPrice = getTotalPrice(); - - // console.log('TOTAL PRICE:', totalPrice); - - const chartDimensions = useMemo( - () => - chartContainerRef.current?.getBoundingClientRect() || { - width: 400, - height: 600, - }, - [chartContainerRef.current] - ); return ( - + - + Cards in Portfolio {/* Include the CronTrigger button */} - + - + - Name - Price - Total Price - Quantity - TCGPlayer Price - Actions + Name + Price + Total Price + Quantity + TCGPlayer Price + Actions - + {(rowsPerPage > 0 @@ -133,53 +238,86 @@ const CardList = ({ selectedCards }) => { : selectedCards )?.map((card, index) => ( - + {card?.name} - - {card?.price} - {card?.totalPrice} - {card?.quantity} - + + {card?.price} + + {card?.totalPrice} + + + {card?.quantity} + + {card.card_prices && card.card_prices[0] && card.card_prices[0].tcgplayer_price ? `$${card.card_prices[0].tcgplayer_price}` : 'Price not available'} - - - - - + + + + + + + + ))} {emptyRows > 0 && ( - + )} @@ -209,10 +347,12 @@ const CardList = ({ selectedCards }) => { mt={2} sx={{ width: '100%' }} > - {`Total: $${totalCost}`} + + {`Total: $${totalCost}`} + - + ); }; diff --git a/src/components/grids/collectionGrids/SelectCollectionList.jsx b/src/components/grids/collectionGrids/SelectCollectionList.jsx index dec753a..7b5de9b 100644 --- a/src/components/grids/collectionGrids/SelectCollectionList.jsx +++ b/src/components/grids/collectionGrids/SelectCollectionList.jsx @@ -6,11 +6,13 @@ import { ListItemText, Divider, Button, + Grid, + Typography, } from '@mui/material'; import { makeStyles } from '@mui/styles'; import { useCookies } from 'react-cookie'; import PropTypes from 'prop-types'; -import LoadingIndicator from '../../indicators/LoadingIndicator'; +import LoadingIndicator from '../../reusable/indicators/LoadingIndicator'; import { useCollectionStore } from '../../../context/CollectionContext/CollectionContext'; const useStyles = makeStyles((theme) => ({ @@ -43,6 +45,16 @@ const useStyles = makeStyles((theme) => ({ backgroundColor: theme.palette.primary.dark, }, }, + gridItem: { + display: 'flex', + flexDirection: 'column', + alignItems: 'flex-start', + justifyContent: 'center', + padding: theme.spacing(1), + }, + gridItemText: { + fontWeight: 'bold', + }, })); const SelectCollectionList = ({ @@ -83,11 +95,6 @@ const SelectCollectionList = ({ [openDialog, setSelectedCollection] ); - // console.log( - // 'SELECTED COLLECTION (SELECT COLLECTIN LIST):', - // selectedCollection - // ); - // The rendering part of the component return ( <> {isLoading ? ( @@ -103,10 +110,35 @@ const SelectCollectionList = ({ sx={{ width: '100%' }} onClick={() => handleSelect(collection?._id)} > - + + + + Name: + + {collection?.name} + + + + Value: + + {/* Replace with actual value */} + ${collection?.currentValue} + + + + Performance: + + {/* Replace with actual data */} + {collection?.performance} % + + + + Cards: + + {/* Replace with actual count */} + {collection?.numberOfCards} + + + )} + + + + + )} + + + + + ); +} diff --git a/src/components/other/CollectionValueTracker.jsx b/src/components/other/CollectionValueTracker.jsx index df3f7f8..73ddfcd 100644 --- a/src/components/other/CollectionValueTracker.jsx +++ b/src/components/other/CollectionValueTracker.jsx @@ -1,37 +1,71 @@ import React from 'react'; -import { Typography, Box, useTheme, Grid } from '@mui/material'; +import { + Typography, + Box, + useTheme, + Grid, + Accordion, + AccordionSummary, + AccordionDetails, + Container, +} from '@mui/material'; import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import { useCollectionStore } from '../../context/CollectionContext/CollectionContext'; import { styled } from '@mui/styles'; - +import { GridExpandMoreIcon } from '@mui/x-data-grid'; +import ChangeHistoryIcon from '@mui/icons-material/ChangeHistory'; const StatBox = styled(Box)(({ theme }) => ({ - backgroundColor: theme.palette.background.paper, + backgroundColor: theme.palette.background.default, borderRadius: theme.shape.borderRadius, padding: theme.spacing(2), + boxShadow: theme.shadows[3], + color: theme.palette.text.primary, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'space-around', - boxShadow: theme.shadows[2], + gap: theme.spacing(1), // Adds spacing between items })); const StatItem = styled(Box)(({ theme }) => ({ display: 'flex', alignItems: 'center', marginBottom: theme.spacing(1), + '&:hover': { + backgroundColor: theme.palette.action.hover, // Hover effect + }, })); - +const styles = { + container: { + // padding: '15px', + border: '2px solid #444', + borderRadius: '8px', + backgroundColor: '#222', + color: '#fff', + boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)', + fontFamily: '"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', + maxWidth: '100%', // Adjusted for full width + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + alignItems: 'center', + height: '100%', + }, + statusBox: { + width: '100%', // Set to full width + // marginTop: '15px', + padding: '3px', + margin: '2px', + // padding: '10px', + background: '#333', + borderRadius: '6px', + border: '1px solid #555', + }, +}; const CollectionValueTracker = ({ stats }) => { const theme = useTheme(); - const { - totalCost, - selectedCollection, - getTotalPrice, - getTotalPrice2, - totalPrice, - allCardPrices, - } = useCollectionStore(); + const { getTotalPrice, selectedCollection } = useCollectionStore(); const twentyFourHourChange = stats.twentyFourHourAverage; const newTotal = getTotalPrice(); @@ -43,47 +77,105 @@ const CollectionValueTracker = ({ stats }) => { value: `$${newTotal}`, }, { - label: '24 Hour Change', + label: '24 Hour Change (%)', value: `${twentyFourHourChange?.percentageChange}`, isIncrease: twentyFourHourChange?.priceIncreased, }, + { + label: '24 Hour Change ($)', + value: `$${twentyFourHourChange?.priceChange}`, + isIncrease: twentyFourHourChange?.priceIncreased, + }, // Additional stats - { label: 'Last Price', value: `${twentyFourHourChange?.lastPrice}` }, - { label: 'Average Price', value: `${twentyFourHourChange?.averagePrice}` }, - { label: 'Volume', value: `${twentyFourHourChange?.volume}` }, - { label: 'Volatility', value: `${twentyFourHourChange?.volatility}` }, + { + label: 'Last Price', + value: `${selectedCollection?.lastSavedPrice?.num}`, + }, + { label: 'Average Price', value: `${stats?.average}` }, + { label: 'Volume', value: `${stats?.volume}` }, + { label: 'Volatility', value: `${stats?.volatility}` }, { label: 'High Point', value: `${twentyFourHourChange?.highPoint}` }, { label: 'Low Point', value: `${twentyFourHourChange?.lowPoint}` }, + // TODO: add stats for top performing cards in certain sets ]; return ( - - {statsArray.map((stat, index) => { - const IconComponent = - stat?.isIncrease !== undefined - ? stat?.isIncrease - ? ArrowUpwardIcon - : ArrowDownwardIcon - : null; - const iconColor = stat?.isIncrease - ? theme.palette.success.main - : theme.palette.error.main; - - return ( - - {IconComponent && ( - - )} - - {stat?.label}: {stat?.value} +
+ + +
+ + Performance:{' '} + {twentyFourHourChange?.percentageChange > 0 + ? 'Positive' + : 'Negative'} - - ); - })} - + {/*
*/} + {/*
*/} + + {' '} + {twentyFourHourChange?.percentageChange}% + + + } + aria-controls="collection-stats-content" + id="collection-stats-header" + > + Collection Statistics + + + + {statsArray.map((stat, index) => { + const IconComponent = + stat?.isIncrease !== undefined + ? stat?.isIncrease + ? ArrowUpwardIcon + : ArrowDownwardIcon + : null; + const iconColor = stat?.isIncrease + ? theme.palette.success.main + : theme.palette.error.main; + + return ( + + {IconComponent && ( + + )} + + {stat?.label}: + + + {stat?.value} + + + ); + })} + + + +
+
+
+
); }; diff --git a/src/components/other/DeckEditPanel.js b/src/components/other/DeckEditPanel.js index c544dc0..dd79ce3 100644 --- a/src/components/other/DeckEditPanel.js +++ b/src/components/other/DeckEditPanel.js @@ -1,53 +1,60 @@ import React, { useState } from 'react'; import { makeStyles } from '@mui/styles'; -import { Button, TextField, Paper } from '@mui/material'; +import { Button, TextField, Paper, Typography } from '@mui/material'; +import { useMode } from '../../context/hooks/colormode'; const useStyles = makeStyles((theme) => ({ root: { - padding: theme.spacing(3), + padding: theme.spacing(4), + margin: theme.spacing(2), borderRadius: theme.shape.borderRadius, - boxShadow: theme.shadows[3], + boxShadow: theme.shadows[3], // Utilize theme's predefined shadows + backgroundColor: theme.palette.background.paper, // Use theme's paper background color + }, + title: { + marginBottom: theme.spacing(3), + fontWeight: theme.typography.fontWeightBold, // Use theme's font weight for bold text + fontSize: theme.typography.h6.fontSize, // Align font size with h6 variant + color: theme.palette.text.primary, // Use theme's primary text color }, textField: { - marginBottom: theme.spacing(2), + marginBottom: theme.spacing(3), '& .MuiOutlinedInput-root': { '&:hover fieldset': { - borderColor: theme.palette.primary.main, + borderColor: theme.palette.secondary.light, // Use secondary color for hover state }, '&.Mui-focused fieldset': { - borderColor: theme.palette.primary.dark, + borderColor: theme.palette.secondary.main, // Use secondary main color for focus }, }, + '& .MuiInputBase-input': { + fontSize: theme.typography.subtitle1.fontSize, // Align with subtitle1 font size + }, }, saveButton: { - boxShadow: 'none', + boxShadow: theme.shadows[1], // Use a softer shadow from theme textTransform: 'none', - fontSize: 16, - padding: theme.spacing(1, 2), - border: `1px solid ${theme.palette.primary.main}`, - lineHeight: 1.5, - backgroundColor: theme.palette.primary.main, - color: theme.palette.primary.contrastText, + fontSize: theme.typography.button.fontSize, // Align with button font size + padding: theme.spacing(1, 4), + lineHeight: theme.typography.button.lineHeight, // Align with button line height + backgroundColor: theme.palette.secondary.main, // Use secondary color for button + color: theme.palette.secondary.contrastText, // Contrast text for readability '&:hover': { - backgroundColor: theme.palette.primary.dark, - borderColor: theme.palette.primary.dark, - boxShadow: 'none', + backgroundColor: theme.palette.secondary.dark, // Darken on hover + boxShadow: theme.shadows[2], // Slightly elevate shadow on hover }, }, })); const DeckEditPanel = ({ selectedDeck, onSave }) => { - const classes = useStyles(); + const { theme } = useMode(); + const classes = useStyles(theme); const [name, setName] = useState(selectedDeck?.name || ''); const [description, setDescription] = useState( selectedDeck?.description || '' ); const handleSave = () => { - // Log the new deck name and description before saving - console.log('New Deck Name:', name); - console.log('New Deck Description:', description); - onSave({ ...selectedDeck, name, @@ -57,6 +64,9 @@ const DeckEditPanel = ({ selectedDeck, onSave }) => { return ( + + Edit Deck + { /> + } + backButton={ + + } + /> + + ); +}; + +export default TopCardsDisplay; diff --git a/src/components/other/dataDisplay/CardCountDisplay.jsx b/src/components/other/dataDisplay/CardCountDisplay.jsx new file mode 100644 index 0000000..ecf90b4 --- /dev/null +++ b/src/components/other/dataDisplay/CardCountDisplay.jsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Grid, Typography } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import PropTypes from 'prop-types'; + +// Styled components +const StyledGrid = styled(Grid)(({ theme }) => ({ + padding: theme.spacing(1), + backgroundColor: theme.palette.background.paper, + borderRadius: theme.shape.borderRadius, + boxShadow: theme.shadows[2], + textAlign: 'center', +})); + +const CardCountDisplay = ({ quantity, label, className }) => { + const totalItems = quantity && quantity.totalItems ? quantity.totalItems : 0; + + return ( + + + + {label}: {totalItems} + + + + ); +}; + +CardCountDisplay.propTypes = { + quantity: PropTypes.shape({ + totalItems: PropTypes.number, + }), + label: PropTypes.string, + className: PropTypes.string, +}; + +export default CardCountDisplay; diff --git a/src/components/Auth/PrivateRoute.jsx b/src/components/reusable/PrivateRoute.jsx similarity index 100% rename from src/components/Auth/PrivateRoute.jsx rename to src/components/reusable/PrivateRoute.jsx diff --git a/src/components/indicators/ErrorIndicator.js b/src/components/reusable/indicators/ErrorIndicator.js similarity index 68% rename from src/components/indicators/ErrorIndicator.js rename to src/components/reusable/indicators/ErrorIndicator.js index f19f6e1..f3c5fa2 100644 --- a/src/components/indicators/ErrorIndicator.js +++ b/src/components/reusable/indicators/ErrorIndicator.js @@ -5,26 +5,7 @@ import { makeStyles } from '@mui/styles'; import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; import Container from '@mui/material/Container'; import { Alert, AlertTitle } from '@mui/material'; - -const useStyles = makeStyles((theme) => ({ - paper: { - padding: theme.spacing(2), - margin: theme.spacing(2), - backgroundColor: theme.palette.error.light, - }, - typography: { - color: theme.palette.error.dark, - }, - container: { - display: 'flex', - alignItems: 'center', - justifyContent: 'flex-start', - }, - icon: { - marginRight: theme.spacing(1), - color: theme.palette.error.main, - }, -})); +import { useStyles } from './styles'; const ErrorIndicator = ({ error }) => { const classes = useStyles(); diff --git a/src/components/indicators/LoadingIndicator.js b/src/components/reusable/indicators/LoadingIndicator.js similarity index 100% rename from src/components/indicators/LoadingIndicator.js rename to src/components/reusable/indicators/LoadingIndicator.js diff --git a/src/components/reusable/indicators/LoadingIndicator2.js b/src/components/reusable/indicators/LoadingIndicator2.js new file mode 100644 index 0000000..b1e75b1 --- /dev/null +++ b/src/components/reusable/indicators/LoadingIndicator2.js @@ -0,0 +1,13 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import LoadingCardAnimation from '../../../assets/animations/LoadingCardAnimation'; + +const LoadingIndicator2 = () => { + return ( + + + + ); +}; + +export default LoadingIndicator2; diff --git a/src/components/reusable/indicators/styles.js b/src/components/reusable/indicators/styles.js new file mode 100644 index 0000000..c5f9378 --- /dev/null +++ b/src/components/reusable/indicators/styles.js @@ -0,0 +1,21 @@ +import { makeStyles } from '@mui/styles'; + +export const useStyles = makeStyles((theme) => ({ + paper: { + padding: theme.spacing(2), + margin: theme.spacing(2), + backgroundColor: theme.palette.error.light, + }, + typography: { + color: theme.palette.error.dark, + }, + container: { + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-start', + }, + icon: { + marginRight: theme.spacing(1), + color: theme.palette.error.main, + }, +})); diff --git a/src/components/search/DeckSearch.js b/src/components/search/DeckSearch.js index 1fa3f56..6f58b6b 100644 --- a/src/components/search/DeckSearch.js +++ b/src/components/search/DeckSearch.js @@ -9,8 +9,7 @@ import { Pagination, } from '@mui/material'; import { useCardStore } from '../../context/CardContext/CardStore'; -import { useTheme } from '@emotion/react'; -import SearchForm from './SearchForm'; +import SearchForm from '../forms/SearchForm'; import DeckSearchCardGrid from '../grids/searchResultGrids/DeckSearchCardGrid'; import CustomPagination from '../reusable/CustomPagination'; diff --git a/src/components/search/SearchBar.js b/src/components/search/SearchBar.js index 5895198..2de0086 100644 --- a/src/components/search/SearchBar.js +++ b/src/components/search/SearchBar.js @@ -1,72 +1,51 @@ import React from 'react'; -import { Grid, Box, Typography, Container } from '@mui/material'; +import { Grid, Box, Typography, Container, Paper } from '@mui/material'; import { useCardStore } from '../../context/CardContext/CardStore'; import SearchButton from '../buttons/other/SearchButton'; import CardNameInput from '../other/InputComponents/CardNameInput'; import CustomSelector from '../other/InputComponents/CustomSelector'; import { useCombinedContext } from '../../context/CombinedProvider'; - -const initialState = { - name: '', - race: '', - type: '', - attribute: '', - level: '', -}; +import { makeStyles } from '@mui/styles'; +import search from './search.json'; +const useStyles = makeStyles((theme) => ({ + searchContainer: { + padding: theme.spacing(3), + borderRadius: theme.shape.borderRadius, + backgroundColor: theme.palette.background.paper, + boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.12)', + margin: theme.spacing(2, 'auto'), + width: '100%', + maxWidth: 'md', + }, + title: { + marginBottom: theme.spacing(3), + fontWeight: 600, + color: theme.palette.primary.main, + }, + searchGrid: { + gap: theme.spacing(2), + }, + searchButtonContainer: { + display: 'flex', + width: '100%', + justifyContent: 'center', + marginTop: theme.spacing(2), + }, +})); const SearchBar = () => { - const { searchParams, setSearchParams } = useCombinedContext(initialState); + const { searchParams, setSearchParams } = useCombinedContext(); const { handleRequest } = useCardStore(); - - const filters = [ - { - label: 'Level', - name: 'level', - values: [ - 'Unset', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', - ], - }, - { label: 'Race', name: 'race', values: ['Unset', 'Aqua', 'Beast'] }, - { - label: 'Type', - name: 'type', - values: ['Unset', 'Effect Monster', 'Flip Effect Monster'], - }, - { - label: 'Attribute', - name: 'attribute', - values: [ - 'Unset', - 'Dark', - 'Divine', - 'Earth', - 'Fire', - 'Light', - 'Water', - 'Wind', - ], - }, - ]; + const classes = useStyles(); + const { initialState, filters } = search; // Destructure the data from JSON return ( - - - + + + Search Cards - + { /> {filters?.map((filter) => ( - - setSearchParams((prevState) => ({ - ...prevState, - [filter?.name]: newValue, - })) - } - values={filter?.values} - /> + + + setSearchParams((prevState) => ({ + ...prevState, + [filter?.name]: newValue, + })) + } + values={filter?.values} + /> + ))} - + + + - + ); }; diff --git a/src/components/search/SearchForm.jsx b/src/components/search/SearchForm.jsx deleted file mode 100644 index 923b629..0000000 --- a/src/components/search/SearchForm.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import { TextField, Button } from '@mui/material'; -import { makeStyles } from '@mui/styles'; -const useStyles = makeStyles((theme) => ({ - form: { - // minWidth: '90%', // Set the min-width - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(2), // You can control the gap between the TextField and Button - }, -})); - -const SearchForm = ({ searchTerm, handleChange, handleSubmit }) => { - const classes = useStyles(); - - return ( -
- - - - ); -}; - -export default SearchForm; diff --git a/src/components/search/SearchResultsDisplay.jsx b/src/components/search/SearchResultsDisplay.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/search/search.json b/src/components/search/search.json new file mode 100644 index 0000000..d5b07c4 --- /dev/null +++ b/src/components/search/search.json @@ -0,0 +1,54 @@ +{ + "initialState": { + "name": "", + "race": "", + "type": "", + "attribute": "", + "level": "" + }, + "filters": [ + { + "label": "Level", + "name": "level", + "values": [ + "Unset", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12" + ] + }, + { + "label": "Race", + "name": "race", + "values": ["Unset", "Aqua", "Beast"] + }, + { + "label": "Type", + "name": "type", + "values": ["Unset", "Effect Monster", "Flip Effect Monster"] + }, + { + "label": "Attribute", + "name": "attribute", + "values": [ + "Unset", + "Dark", + "Divine", + "Earth", + "Fire", + "Light", + "Water", + "Wind" + ] + } + ] +} diff --git a/src/containers/PortfolioChartContainer.jsx b/src/containers/PortfolioChartContainer.jsx deleted file mode 100644 index a91e87e..0000000 --- a/src/containers/PortfolioChartContainer.jsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useMemo } from 'react'; -import { Box, Grid, Paper } from '@mui/material'; -import PortfolioChart from '../components/chart/PortfolioChart'; -import TimeRangeSelector from '../components/other/InputComponents/TimeRangeSelector'; -import CollectionStatisticsSelector, { - calculateStatistics, -} from '../components/other/InputComponents/CollectionStatisticsSelector'; -import UpdateStatusBox from '../components/other/InputComponents/UpdateStatusBox'; -import { useTheme } from '@mui/material/styles'; -import { useSocketContext } from '../context/SocketProvider'; -import { useChartContext } from '../context/ChartContext/ChartContext'; -import { useCollectionStore } from '../context/CollectionContext/CollectionContext'; -import CollectionValueTracker from '../components/other/CollectionValueTracker'; -import { useCombinedContext } from '../context/CombinedProvider'; -import UpdateStatusBox2 from '../components/other/InputComponents/UpdateStatusBox2'; -const paperChartStyle = { - elevation: 3, - borderRadius: 2, - p: 2, - // height: '25vh', // Set the container height to 25vh - maxWidth: '100vw', - display: 'flex', - flexDirection: 'row', // Change to 'row' to fit selectors horizontally - justifyContent: 'space-between', // Distribute space evenly between selectors - alignItems: 'center', // Align items vertically in the center - gap: 2, // Set a gap between the selectors -}; -const selectorStyle = { - flex: 1, // Allow the selector to grow - display: 'flex', - flexDirection: 'column', - height: '100%', // Set the selector height to 100% -}; - -const PortfolioChartContainer = ({ selectedCards, removeCard }) => { - const theme = useTheme(); - // const { socket } = useSocketContext(); - const { socket } = useCombinedContext(); - const { timeRange } = useChartContext(); - const { allCollections, selectedCollection } = useCollectionStore(); - const data = allCollections.map((collection) => ({ - data: collection?.currentChartDataSets2, - })); - const dataForStats = data[0]; - const stats = useMemo( - () => calculateStatistics(dataForStats, timeRange), - [dataForStats, timeRange] - ); - - const containerStyle = { - maxWidth: '100%', - // margin: 'auto', - padding: theme.spacing(2), - overflow: 'hidden', - }; - - const paperStyle = { - padding: theme.spacing(2), - marginBottom: theme.spacing(2), - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - height: 'auto', - boxShadow: theme.shadows[3], - borderRadius: theme.shape.borderRadius, - }; - - const gridItemStyle = { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'space-between', - }; - - return ( - - {/* Updaters Row */} - {/* */} - - - - - - {/* */} - - - - - - - {/* Main Grid Container */} - - {/* Portfolio Chart Row */} - - - - - - - {/* Selectors Row */} - - - - - - - - - {' '} - - - - - ); -}; - -export default PortfolioChartContainer; diff --git a/src/containers/PortfolioListContainer.jsx b/src/containers/PortfolioListContainer.jsx deleted file mode 100644 index 5bffa50..0000000 --- a/src/containers/PortfolioListContainer.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import { Grid, Paper } from '@mui/material'; -import CardList from '../components/grids/collectionGrids/CardList'; - -const PortfolioListContainer = ({ selectedCards, removeCard }) => { - return ( - - - - - - ); -}; - -export default PortfolioListContainer; diff --git a/src/containers/CartContainer.jsx b/src/containers/cartPageContainers/CartContainer.jsx similarity index 100% rename from src/containers/CartContainer.jsx rename to src/containers/cartPageContainers/CartContainer.jsx diff --git a/src/components/content/CartContent.js b/src/containers/cartPageContainers/CartContent.js similarity index 84% rename from src/components/content/CartContent.js rename to src/containers/cartPageContainers/CartContent.js index a3d926f..533bf48 100644 --- a/src/components/content/CartContent.js +++ b/src/containers/cartPageContainers/CartContent.js @@ -1,9 +1,9 @@ import React from 'react'; import { Typography } from '@mui/material'; import { useCartStore } from '../../context/CartContext/CartContext'; -import CartContainer from '../../containers/CartContainer'; -import CartItem from '../grids/CartItem'; -import CartTotal from '../other/CartTotal'; +import CartContainer from './CartContainer'; +import CartItem from '../../components/grids/CartItem'; +import CartTotal from '../../components/other/CartTotal'; const CartContent = () => { const { cartData, getTotalCost } = useCartStore(); diff --git a/src/containers/CartContentContainer.js b/src/containers/cartPageContainers/CartContentContainer.js similarity index 79% rename from src/containers/CartContentContainer.js rename to src/containers/cartPageContainers/CartContentContainer.js index 89161b4..5dd3823 100644 --- a/src/containers/CartContentContainer.js +++ b/src/containers/cartPageContainers/CartContentContainer.js @@ -1,7 +1,7 @@ import React from 'react'; import { Box } from '@mui/system'; -import LoadingIndicator from '../components/indicators/LoadingIndicator'; -import CartContent from '../components/content/CartContent'; +import LoadingIndicator from '../../components/reusable/indicators/LoadingIndicator'; +import CartContent from './CartContent'; const CartContentContainer = ({ cartData, diff --git a/src/containers/CustomerFormContainer.js b/src/containers/cartPageContainers/CustomerFormContainer.js similarity index 72% rename from src/containers/CustomerFormContainer.js rename to src/containers/cartPageContainers/CustomerFormContainer.js index d55887a..46d79bb 100644 --- a/src/containers/CustomerFormContainer.js +++ b/src/containers/cartPageContainers/CustomerFormContainer.js @@ -1,6 +1,6 @@ import React from 'react'; import { Box } from '@mui/system'; -import CustomerForm from '../components/forms/customerCheckoutForm/CustomerForm'; +import CustomerForm from '../../components/forms/customerCheckoutForm/CustomerForm'; const CustomerFormContainer = () => { return ( diff --git a/src/containers/collectionPageContainers/PortfolioChartContainer.jsx b/src/containers/collectionPageContainers/PortfolioChartContainer.jsx new file mode 100644 index 0000000..f5f3fe1 --- /dev/null +++ b/src/containers/collectionPageContainers/PortfolioChartContainer.jsx @@ -0,0 +1,77 @@ +import React, { useMemo } from 'react'; +import { Box, Grid, Paper, Container, useTheme } from '@mui/material'; +import PortfolioChart from '../../components/chart/PortfolioChart'; +import TimeRangeSelector from '../../components/other/InputComponents/TimeRangeSelector'; +import CollectionStatisticsSelector, { + calculateStatistics, +} from '../../components/other/InputComponents/CollectionStatisticsSelector'; +import UpdateStatusBox2 from '../../components/other/InputComponents/UpdateStatusBox2'; +import TopCardsDisplay from '../../components/other/TopCardsDisplay'; +import { useChartContext } from '../../context/ChartContext/ChartContext'; +import { useCollectionStore } from '../../context/CollectionContext/CollectionContext'; +import { useCombinedContext } from '../../context/CombinedProvider'; + +const PortfolioChartContainer = ({ selectedCards, removeCard }) => { + const theme = useTheme(); + const { timeRange } = useChartContext(); + const { allCollections } = useCollectionStore(); + const { socket } = useCombinedContext(); + + const data = allCollections.map((collection) => ({ + data: collection?.currentChartDataSets2, + })); + const dataForStats = data[0]; + const stats = useMemo( + () => calculateStatistics(dataForStats, timeRange), + [dataForStats, timeRange] + ); + + return ( + + {/* Updaters Row */} + + + + + + + + + + {/* Additional Components */} + + + + {/* Main Grid Container */} + + {/* Portfolio Chart Row */} + + + + + {/* Additional Rows */} + + + + + + ); +}; + +export default PortfolioChartContainer; diff --git a/src/containers/collectionPageContainers/PortfolioContent.jsx b/src/containers/collectionPageContainers/PortfolioContent.jsx new file mode 100644 index 0000000..692a4aa --- /dev/null +++ b/src/containers/collectionPageContainers/PortfolioContent.jsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { Box, Container, Grid, Paper, useTheme } from '@mui/material'; +import PortfolioHeader from '../../components/headings/PortfolioHeader'; +import PortfolioListContainer from './PortfolioListContainer'; +import PortfolioChartContainer from './PortfolioChartContainer'; +import HeaderTitle from '../../components/reusable/HeaderTitle'; +import { useCollectionStore } from '../../context/CollectionContext/CollectionContext'; +import { useMode } from '../../context/hooks/colormode'; + +const PortfolioContent = ({ error, selectedCards, removeCard }) => { + const { theme } = useMode(); + const { selectedCollection } = useCollectionStore(); + + return ( + + + + + + + + + + + + + + + + + ); +}; + +export default PortfolioContent; diff --git a/src/containers/collectionPageContainers/PortfolioListContainer.jsx b/src/containers/collectionPageContainers/PortfolioListContainer.jsx new file mode 100644 index 0000000..579f87b --- /dev/null +++ b/src/containers/collectionPageContainers/PortfolioListContainer.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Box, Grid, Paper } from '@mui/material'; +import CardList from '../../components/grids/collectionGrids/CardList'; +import { useTheme } from '@mui/material/styles'; +import { useMode } from '../../context/hooks/colormode'; + +const PortfolioListContainer = ({ selectedCards, removeCard }) => { + const { theme } = useMode(); + + return ( + + + + + + + + ); +}; + +export default PortfolioListContainer; diff --git a/src/containers/deckBuilderPageContainers/DeckBuilderContainer.js b/src/containers/deckBuilderPageContainers/DeckBuilderContainer.js index b365cfd..2f1a775 100644 --- a/src/containers/deckBuilderPageContainers/DeckBuilderContainer.js +++ b/src/containers/deckBuilderPageContainers/DeckBuilderContainer.js @@ -1,56 +1,45 @@ import React from 'react'; -import { Grid, useMediaQuery, useTheme } from '@mui/material'; +import { Grid } from '@mui/material'; import DeckDisplay from '../../components/grids/DeckDisplay'; import DeckSearch from '../../components/search/DeckSearch'; -import { styled } from '@mui/system'; // Use @mui/system for Emotion styling +import { styled } from '@mui/styles'; +import { useMode } from '../../context/hooks/colormode'; -// Define your components using the styled function from @mui/system const SearchGrid = styled(Grid)(({ theme }) => ({ - [theme.breakpoints.up('xl')]: { flexBasis: '60%' }, - [theme.breakpoints.up('lg')]: { flexBasis: '50%' }, - [theme.breakpoints.between('md', 'lg')]: { flexBasis: '50%' }, - [theme.breakpoints.down('sm')]: { flexBasis: '50%' }, + padding: theme.spacing(2), + [theme.breakpoints.down('md')]: { + width: '50%', // Half width on small and medium screens + }, })); const DisplayGrid = styled(Grid)(({ theme }) => ({ - flex: 1, - padding: theme.spacing(1), + padding: theme.spacing(2), + [theme.breakpoints.down('md')]: { + width: '50%', // Half width on small and medium screens + }, })); const RootGrid = styled(Grid)(({ theme }) => ({ overflow: 'auto', - backgroundColor: '#f4f6f8', + display: 'flex', + flexDirection: 'row', + flexWrap: 'wrap', // Ensure wrapping on smaller screens + backgroundColor: theme.palette.background.secondary, padding: theme.spacing(3), - borderRadius: '10px', - boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.1)', + borderRadius: theme.shape.borderRadius, + boxShadow: theme.shadows[5], width: '100%', })); const DeckBuilderContainer = ({ userDecks }) => { - const theme = useTheme(); // Use the theme from Emotion - const isLargeScreen = useMediaQuery(theme.breakpoints.up('lg')); - const isMediumScreen = useMediaQuery(theme.breakpoints.between('sm', 'md')); - const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); - - const getXsValue = () => { - if (isLargeScreen) return 3; - if (isMediumScreen) return 4; - return 5; - }; - const getDisplayXsValue = () => { - // if (isSmallScreen || isMediumScreen) return 7; - // return 9; - if (isLargeScreen) return 9; - if (isMediumScreen) return 8; - return 7; - }; + const { theme } = useMode(); return ( - - + + - + diff --git a/src/context/AppContext/AppContextProvider.jsx b/src/context/AppContext/AppContextProvider.jsx new file mode 100644 index 0000000..295d4ff --- /dev/null +++ b/src/context/AppContext/AppContextProvider.jsx @@ -0,0 +1,54 @@ +// CombinedContext.js +import React, { createContext, useContext, useState } from 'react'; +import { DeckContext } from '../DeckContext/DeckContext'; +import { CartContext } from '../CartContext/CartContext'; +import { CollectionContext } from '../CollectionContext/CollectionContext'; + +// Create the combined context +export const AppContext = createContext({}); + +// Create a provider component that combines the contexts +export const AppContextProvider = ({ children }) => { + const [context, setContext] = useState({}); + const Deck = useContext(DeckContext); + const Cart = useContext(CartContext); + const Collection = useContext(CollectionContext); + + // Combine the context values into one object + const appContextValues = { Deck, Cart, Collection }; + + return ( + + {children} + + ); +}; + +// import React, { createContext, useContext, useState } from 'react'; +// import { DeckContext } from '../DeckContext/DeckContext'; +// import { CartContext } from '../CartContext/CartContext'; +// import { CollectionContext } from '../CollectionContext/CollectionContext'; + +// export const AppContext = createContext({ +// Deck: {}, +// Cart: {}, +// Collection: {}, +// context: '', +// // eslint-disable-next-line @typescript-eslint/no-empty-function +// setContext: () => {}, +// }); + +// export const AppContextProvider = ({ children }) => { +// const [context, setContext] = useState('Collection'); // Default context +// const Deck = useContext(DeckContext); +// const Cart = useContext(CartContext); +// const Collection = useContext(CollectionContext); + +// const appContextValues = { Deck, Cart, Collection, context, setContext }; + +// return ( +// +// {children} +// +// ); +// }; diff --git a/src/context/AppContextProvider.jsx b/src/context/AppContextProvider.jsx deleted file mode 100644 index 1bb9d65..0000000 --- a/src/context/AppContextProvider.jsx +++ /dev/null @@ -1,24 +0,0 @@ -// CombinedContext.js -import React, { createContext, useContext } from 'react'; -import { DeckContext } from './DeckContext/DeckContext'; -import { CartContext } from './CartContext/CartContext'; -import { CollectionContext } from './CollectionContext/CollectionContext'; - -// Create the combined context -export const AppContext = createContext({}); - -// Create a provider component that combines the contexts -export const AppContextProvider = ({ children }) => { - const Deck = useContext(DeckContext); - const Cart = useContext(CartContext); - const Collection = useContext(CollectionContext); - - // Combine the context values into one object - const appContextValues = { Deck, Cart, Collection }; - - return ( - - {children} - - ); -}; diff --git a/src/context/CardContext/CardStore.js b/src/context/CardContext/CardStore.js index 404a196..491c2fb 100644 --- a/src/context/CardContext/CardStore.js +++ b/src/context/CardContext/CardStore.js @@ -8,10 +8,7 @@ const CardContext = createContext(); export const CardProvider = ({ children }) => { const [cookies, setCookie] = useCookies(['cart'], ['deckData']); const initialStore = cookies.store || []; - // const initialCart = cookies.cart || []; - // console.log('Initial cart:', initialCart); const [cardsArray, setCardsArray] = useState(initialStore); - // console.log('Initial store:', initialStore); const currentCart = cookies.cart || []; const [currenCartArray, setCurrentCartArray] = useState(currentCart); const [searchData, setSearchData] = useState([]); @@ -19,7 +16,6 @@ export const CardProvider = ({ children }) => { const [savedDeckData, setSavedDeckData] = useState(currentDeckData); const [deckSearchData, setDeckSearchData] = useState([]); - // console.log('Current cart:', currentCart); if (!currenCartArray || !savedDeckData) { return
Loading...
; } @@ -82,7 +78,6 @@ export const CardProvider = ({ children }) => { searchData, deckSearchData, savedDeckData, - setSearchData, setDeckSearchData, setSavedDeckData, diff --git a/src/context/CardImagesContext/CardImagesContext.jsx b/src/context/CardImagesContext/CardImagesContext.jsx new file mode 100644 index 0000000..d6cad47 --- /dev/null +++ b/src/context/CardImagesContext/CardImagesContext.jsx @@ -0,0 +1,112 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; + +const CardImagesContext = createContext(); + +export const useCardImages = () => useContext(CardImagesContext); + +const fetchWrapper = async (url, method, body = null) => { + const options = { + method, + headers: { + 'Content-Type': 'application/json', + // 'Access-Control-Allow-Origin': '*', + // crossOrigin: 'anonymous', + }, + ...(body && { body: JSON.stringify(body) }), + }; + + try { + const response = await fetch(url, options); + if (!response.ok) { + // We handle non-ok responses immediately + throw new Error(`API request failed with status ${response.status}`); + } + // updateLastRequestTime(method); // Assumed to be a function that updates some kind of state + return await response.json(); // Directly returning the JSON response + } catch (error) { + console.error(`Fetch failed: ${error}`); + console.trace(); + throw error; // Re-throwing the error for upstream catch blocks to handle + } +}; + +export const CardImagesProvider = ({ children }) => { + const [cards, setCards] = useState([]); + const [images, setImages] = useState([]); // [ + const [randomCardImage, setRandomCardImage] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + const BASE_API_URL = 'http://localhost:3001/api/card-image'; + + const downloadCardImages = async () => { + setIsLoading(true); + try { + const response = await fetchWrapper(BASE_API_URL + '/download', 'GET'); + console.log('Response from fetchWrapper:', response); + + // If response is already JSON + setCards(response.data); // Directly setting the response if it's already in the desired format + + cards.forEach((card) => { + if (card.card_images && card.card_images.length > 0) { + // Adding a dummy GET parameter to bypass caching + const imageUrl = + card.card_images[0].image_url + '?dummy=' + Date.now(); + setImages(imageUrl); + } + }); + + // If response needs to be parsed as JSON + // const jsonResponse = await response.json(); // Uncomment if response is in JSON format + // setCards(jsonResponse); // Assuming jsonResponse is an array of cards + } catch (error) { + console.error('Error in downloadCardImages:', error); + setError(error.message); + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + downloadCardImages(); + }, []); + + useEffect(() => { + if (cards && cards.length > 0) { + const randomCard = cards[Math.floor(Math.random() * cards.length)]; + if (randomCard.card_images && randomCard.card_images.length > 0) { + // Adding a dummy GET parameter to bypass caching + const imageUrl = + randomCard.card_images[0].image_url + '?dummy=' + Date.now(); + setRandomCardImage(imageUrl); + } + } + }, [cards]); + + useEffect(() => { + if (images && images.length > 0) { + const randomCard = images[Math.floor(Math.random() * images.length)]; + if (randomCard.card_images && randomCard.card_images.length > 0) { + setRandomCardImage(randomCard.card_images[0].image_url); + } + } + }, [images]); + + return ( + + {children} + + ); +}; + +export default CardImagesProvider; diff --git a/src/context/CartContext/keepsaved.js b/src/context/CartContext/keepsaved.js deleted file mode 100644 index 98c7d5d..0000000 --- a/src/context/CartContext/keepsaved.js +++ /dev/null @@ -1,240 +0,0 @@ -// /* eslint-disable @typescript-eslint/no-empty-function */ -// import React, { createContext, useState, useEffect, useCallback } from 'react'; -// import { useCookies } from 'react-cookie'; -// import { useCardStore } from './CardStore'; - -// export const CartContext = createContext({ -// cart: [], -// getCardQuantity: () => {}, -// addOneToCart: () => {}, -// removeOneFromCart: () => {}, -// deleteFromCart: () => {}, -// getTotalCost: () => {}, -// setCart: () => {}, -// fetchUserCart: () => {}, -// }); - -// export const CartProvider = ({ children }) => { -// const [cartData, setCartData] = useState([]); -// const [loading, setLoading] = useState(false); -// const [error, setError] = useState(null); -// const { getCardData } = useCardStore(); -// const [cookies, setCookie] = useCookies(['userCookie', 'cart']); - -// const userId = cookies.userCookie?.id; // Update the way to get the userId -// console.log('cartData', cartData); - -// useEffect(() => { -// let isMounted = true; - -// const fetchAndSetUserCart = async (userId) => { -// try { -// setLoading(true); -// const cartData = await fetchUserCart(userId); -// console.log('Fetched cartData:', cartData); - -// if (isMounted) { -// setCartData(cartData); -// setLoading(false); -// } -// } catch (error) { -// if (isMounted) { -// setError(`Failed to fetch user cart data: ${error.message}`); -// setLoading(false); -// } -// } -// }; - -// if (userId && typeof userId === 'string') { -// fetchAndSetUserCart(userId); -// } - -// return () => { -// isMounted = false; -// }; -// }, [userId]); - -// // Add new function here -// const createUserCart = async (userId) => { -// const newCartResponse = await fetch( -// `${process.env.REACT_APP_SERVER}/api/carts/newCart/${userId}`, -// { -// method: 'POST', -// } -// ); - -// if (!newCartResponse.ok) { -// throw new Error(`HTTP error! status: ${newCartResponse.status}`); -// } - -// const newCartData = await newCartResponse.json(); -// console.log('CART CREATED:', newCartData); -// const newCartItems = Array.isArray(newCartData.cart) -// ? newCartData.cart -// : []; -// setCookie('activeCartCardsArray', newCartItems, { path: '/' }); -// setCookie('allCarts', newCartItems, { path: '/' }); -// return newCartItems; -// }; - -// const fetchUserCart = useCallback( -// async (userId) => { -// const response = await fetch( -// `${process.env.REACT_APP_SERVER}/api/carts/userCart/${userId}` -// ); - -// if (!response.ok) { -// if (response.status === 404) { -// // Call the new function here -// return createUserCart(userId); -// } else { -// throw new Error(`HTTP error! status: ${response.status}`); -// } -// } else { -// const data = await response.json(); -// console.log('CART EXISTS:', data); -// setCookie( -// 'activeCartCardsArray', -// Array.isArray(data.cart) ? data.cart : [], -// { -// path: '/', -// } -// ); -// setCookie('allCarts', Array.isArray(data) ? data : {}, { -// path: '/', -// }); -// return data; -// } -// }, -// [setCookie, createUserCart] // Added createUserCart to the dependencies array -// ); - -// const updateCartInBackend = async (cartId, cartData) => { -// console.log('received values:', 'cartId', cartId, 'cartData', cartData); - -// // Ensure cartData is in the correct format -// const formattedCartData = cartData.map((item) => ({ -// cardId: item.id, -// quantity: item.quantity, -// })); - -// try { -// const response = await fetch( -// `${process.env.REACT_APP_SERVER}/api/carts/${cartId}`, -// { -// method: 'PUT', -// headers: { -// 'Content-Type': 'application/json', -// }, -// body: JSON.stringify(formattedCartData), -// } -// ); - -// if (!response.ok) { -// throw new Error(`HTTP error! status: ${response.status}`); -// } - -// const data = await response.json(); -// return data; -// } catch (error) { -// console.error(`Failed to update cart in backend: ${error.message}`); -// } -// }; - -// const getCardQuantity = (cardInfo) => { -// const cartItem = cartData?.cart?.find((item) => item.id === cardInfo.id); -// return cartItem ? cartItem.quantity : 0; -// }; - -// const addOneToCart = async (cardInfo) => { -// const itemExistsInCart = cartData?.cart?.some( -// (item) => item.id === cardInfo.id -// ); - -// let updatedCart; -// if (itemExistsInCart) { -// updatedCart = -// cartData?.cart?.map((item) => { -// if (item.id === cardInfo.id) { -// return { -// ...item, -// quantity: item.quantity + 1, -// }; -// } -// return item; -// }) ?? []; -// } else { -// updatedCart = [...(cartData?.cart || []), { ...cardInfo, quantity: 1 }]; -// } - -// const newCartData = await updateCartInBackend(cartData?._id, updatedCart); -// setCartData(newCartData || []); -// }; - -// const removeOneFromCart = async (cardInfo) => { -// const quantity = getCardQuantity(cardInfo.id); -// const updatedCart = -// quantity === 1 -// ? cartData?.cart?.filter( -// (currentCard) => currentCard.id !== cardInfo.id -// ) ?? [] -// : cartData?.cart?.map((card) => -// card.id === cardInfo.id -// ? { ...card, quantity: card.quantity - 1 } -// : card -// ) ?? []; - -// const newCartData = await updateCartInBackend(cartData?._id, updatedCart); -// setCartData(newCartData || []); -// }; - -// const deleteFromCart = async (cardInfo) => { -// const updatedCart = -// cartData?.cart?.filter( -// (currentCard) => currentCard.cardId !== cardInfo.id -// ) ?? []; - -// const newCartData = await updateCartInBackend(cartData?._id, updatedCart); -// setCartData(newCartData || []); -// }; - -// const getTotalCost = () => { -// if (!cartData?.cart || cartData.cart.length === 0) { -// return 0; -// } - -// console.log('cartData:', cartData); -// return cartData.cart.reduce((totalCost, cartItem) => { -// const cardData = getCardData(cartItem.id); -// return totalCost + cardData.price * cartItem.quantity; -// }, 0); -// }; - -// const setCart = async (cart) => { -// const updatedCart = Array.isArray(cart) ? cart : []; -// setCartData(updatedCart); -// // update in backend -// const newCartData = await updateCartInBackend(cartData?._id, updatedCart); -// setCartData(newCartData || []); -// }; - -// const contextValue = { -// cart: cartData.cart, -// getCardQuantity, -// addOneToCart, -// createUserCart, -// removeOneFromCart, -// fetchUserCart, -// deleteFromCart, -// getTotalCost, -// setCart, -// loading, -// error, -// }; - -// return ( -// {children} -// ); -// }; - -// export default CartProvider; diff --git a/src/context/ChartContext/ChartContext.jsx b/src/context/ChartContext/ChartContext.jsx index d28e564..eefb826 100644 --- a/src/context/ChartContext/ChartContext.jsx +++ b/src/context/ChartContext/ChartContext.jsx @@ -24,7 +24,7 @@ export const ChartProvider = ({ children }) => { const currentValue = timeRanges.find((option) => option.value === timeRange); - console.log('currentValue: ', currentValue); + // console.log('currentValue: ', currentValue); const handleChange = (e) => { setTimeRange(e.target.value); }; diff --git a/src/context/CollectionContext/ChunkPaylod.jsx b/src/context/CollectionContext/ChunkPaylod.jsx deleted file mode 100644 index 81b3b17..0000000 --- a/src/context/CollectionContext/ChunkPaylod.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import { fetchWrapper } from './collectionUtility'; - -const chunkSize = 1024 * 1024 * 10; // 10 MB, adjust as needed - -// Function to break the payload into smaller chunks -export const chunkPayload = (payload) => { - const stringPayload = JSON.stringify(payload); - const payloadSize = new TextEncoder().encode(stringPayload).length; - - if (payloadSize <= chunkSize) { - return [payload]; // If payload is small, no need to chunk - } - - // If payload is too large, chunk it - const chunks = []; - for (let i = 0; i < stringPayload.length; i += chunkSize) { - const chunkStr = stringPayload.slice(i, i + chunkSize); - chunks.push(JSON.parse(chunkStr)); - } - - return chunks; -}; - -// Function to send chunks to the server -export const sendChunks = async (endpoint, method, chunks) => { - for (const chunk of chunks) { - await fetchWrapper(endpoint, method, chunk); - } -}; diff --git a/src/context/CollectionContext/CollectionContext.jsx b/src/context/CollectionContext/CollectionContext.jsx index df096f3..f964a05 100644 --- a/src/context/CollectionContext/CollectionContext.jsx +++ b/src/context/CollectionContext/CollectionContext.jsx @@ -27,8 +27,6 @@ import { calculateCollectionValue, } from './collectionUtility.jsx'; import moment from 'moment'; -import { createNewDataSet } from './cardHelpers.jsx'; -import { chunkPayload, sendChunks } from './ChunkPaylod.jsx'; export const CollectionContext = createContext(defaultContextValue); @@ -240,49 +238,76 @@ export const CollectionProvider = ({ children }) => { } }; - const getUpdatedCards = (activeCollection, cardUpdate, operation) => { + const getUpdatedCards = (collection, cardUpdate, operation) => { let cardsToUpdate; switch (operation) { case 'add': - cardsToUpdate = handleCardAddition(activeCollection?.cards, cardUpdate); + cardsToUpdate = handleCardAddition(collection?.cards, cardUpdate); break; case 'remove': - cardsToUpdate = handleCardRemoval(activeCollection?.cards, cardUpdate); + cardsToUpdate = handleCardRemoval(collection?.cards, cardUpdate); + console.log('CARD REMOVAL:', cardUpdate); + console.log('CARD REMOVAL:', cardsToUpdate); break; case 'update': - // eslint-disable-next-line no-case-declarations - const cardIndex = activeCollection.cards.findIndex( - (c) => c.id === cardUpdate.id - ); - if (cardIndex === -1) { - console.error('Card not found in the collection.'); - return activeCollection.cards; + if (!collection?.cards) { + console.error('No cards found in the collection.'); + return collection?.cards; + } + if (!cardUpdate?.id) { + console.warn('Card ID is missing.', cardUpdate); + // return collection?.cards; } - - // eslint-disable-next-line no-case-declarations - const existingCard = activeCollection.cards[cardIndex]; - // eslint-disable-next-line no-case-declarations - const updatedPriceHistory = updatePriceHistory( - existingCard, - cardUpdate - ); // eslint-disable-next-line no-case-declarations - const updatedCard = getUpdatedCard( - existingCard, - cardUpdate, - updatedPriceHistory, - activeCollection._id - ); - cardsToUpdate = replaceCardInArray( - activeCollection.cards, - updatedCard, - cardIndex - ); + const cards = collection?.cards; + + for (let i = 0; i < cards?.length; i++) { + // eslint-disable-next-line no-case-declarations + // const cardIndex = selectedCollection?.cards?.findIndex( + // (c) => c?.id === cardUpdate?.id + // ); + if (!cards[i]?.id) { + console.warn('Card ID is missing.', cards[i]); + continue; + } + cardUpdate = cards[i]; + const cardIndex = selectedCollection?.cards?.findIndex( + (c) => c?.id === cardUpdate?.id + ); + + if (cardIndex === -1) { + console.error( + 'Card not found in the collection.', + collection?.cards[cardIndex] + ); + return collection?.cards; + } + + // eslint-disable-next-line no-case-declarations + const existingCard = collection?.cards[cardIndex]; + // eslint-disable-next-line no-case-declarations + const updatedPriceHistory = updatePriceHistory( + existingCard, + cardUpdate + ); + // eslint-disable-next-line no-case-declarations + const updatedCard = getUpdatedCard( + existingCard, + cardUpdate, + updatedPriceHistory, + collection?._id + ); + cardsToUpdate = replaceCardInArray( + collection?.cards, + updatedCard, + cardIndex + ); + } break; default: console.error('Unsupported operation:', operation); - return activeCollection.cards; + return collection?.cards; } return cardsToUpdate; @@ -306,7 +331,11 @@ export const CollectionProvider = ({ children }) => { quantity: update.quantity || card.quantity, collectionId: collectionId, totalPrice: cardPrice * (update.quantity || card.quantity), - lastSavedPrice: update.lastSavedPrice, + // lastSavedPrice: update.lastSavedPrice, + lastSavedPrice: { + num: card.price || card.card_prices[0].tcgplayer_price, + timestamp: new Date(), + }, latestPrice: update.latestPrice, tag: 'monitored', chart_datasets: [...(card.chart_datasets || []), newChartDataEntry], @@ -322,18 +351,18 @@ export const CollectionProvider = ({ children }) => { function updatePriceHistory(card, update) { const newPriceHistoryEntry = createPriceHistoryObject( - update.latestPrice.num + update?.latestPrice?.num ); const lastPriceHistoryEntry = - card.priceHistory[card.priceHistory.length - 1]; + card?.priceHistory[card?.priceHistory?.length - 1]; if ( !lastPriceHistoryEntry || - lastPriceHistoryEntry.num !== newPriceHistoryEntry.num + lastPriceHistoryEntry?.num !== newPriceHistoryEntry?.num ) { return [...card.priceHistory, newPriceHistoryEntry]; } - return card.priceHistory; + return card?.priceHistory; } function createChartDataEntry(price) { @@ -383,12 +412,16 @@ export const CollectionProvider = ({ children }) => { totalCost: updatedTotalPrice ? updatedTotalPrice.toString() : '0', totalQuantity: cards.reduce((acc, card) => acc + (card.quantity || 0), 0), quantity: cards.length, - // _id, + lastSavedPrice: { + num: collectionWithCards?.totalPrice || 0, + timestamp: collectionWithCards?.lastSavedPrice?.timeStamp || new Date(), + }, + latestPrice: { + num: updatedTotalPrice || 0, + timestamp: new Date(), + }, dailyPriceChange: getPriceChange(currentChartDataSets2)[0]?.priceChange || '', - currentChartDataSets: filterUniqueDataPoints( - getCurrentChartDataSets(updatedChartData) - ), currentChartDataSets2: filterUniqueDataPoints( transformPriceHistoryToXY(collectionPriceHistory) ), @@ -424,20 +457,6 @@ export const CollectionProvider = ({ children }) => { }; }; - const removeCardsFromCollection = async (userId, collectionId, cardIds) => { - const endpoint = `/api/${userId}/collections/${collectionId}/removeCards`; - const method = 'POST'; - const payload = { cardIds }; - - try { - const response = await fetchWrapper(endpoint, method, payload); - return response; // The response contains the updated cards list - } catch (error) { - console.error('Error removing cards:', error); - throw error; - } - }; - const getUpdatedCollection = async ( collectionWithCards, // updated cards cardUpdate, // updated card @@ -446,28 +465,55 @@ export const CollectionProvider = ({ children }) => { ) => { const collectionId = collectionWithCards?._id || collectionData?._id; if (!collectionId) { - console.error('Collection ID is missing.'); + console.error('Collection ID is missing.', collectionId); return; } - const cardExists = collectionWithCards.cards.some( - (card) => card.id === cardUpdate.id + if (!userId) { + console.error('User ID is missing.', userId); + return; + } + const cardExists = collectionWithCards?.cards?.some( + (card) => card?.id === cardUpdate?.id ); - // Determine the method and endpoint based on operation and card existence - const method = - operation === 'remove' ? 'DELETE' : cardExists ? 'PUT' : 'POST'; - const endpointSuffix = - operation === 'remove' ? 'removeCards' : 'updateCards'; + let multipleOfSameCard = []; + let cardQuantity = 0; + if (cardExists) { + multipleOfSameCard = collectionWithCards?.cards?.filter( + (card) => card?.id === cardUpdate?.id + ); + cardQuantity = multipleOfSameCard[0]?.quantity; + } + console.log('MULTIPLE OF SAME CARD:', multipleOfSameCard); + console.log('CARD QUANTITY:', cardQuantity); + console.log('CARD EXISTS:', cardExists); + + let method; + if (operation === 'remove' && cardQuantity === 1) { + method = 'DELETE'; + } else if (cardExists) { + method = 'PUT'; + } else { + method = 'POST'; + } + + let endpointSuffix; + if (operation === 'remove' && cardQuantity === 1) { + endpointSuffix = 'removeCards'; + } else { + endpointSuffix = 'updateCards'; + } + const endpoint = createApiUrl( `${userId}/collections/${collectionId}/${endpointSuffix}` ); - console.log('CARDS BEFORE: ', collectionWithCards); const updatedCards = getUpdatedCards( collectionWithCards, cardUpdate, operation + // collectionId ); console.log('CARDS AFTER: ', updatedCards); @@ -478,22 +524,50 @@ export const CollectionProvider = ({ children }) => { // (total, card) => total + card.price * card.quantity, // 0 // ); - const updatedTotalQuantity = updatedCards.reduce( - (total, card) => total + card.quantity, + const updatedTotalQuantity = updatedCards?.reduce( + (total, card) => total + card?.quantity, 0 ); const newCollectionPriceHistoryObject = createPriceHistoryObject(updatedTotalPrice); let cardsResponse; - // if (operation === 'remove') { - // const cardIds = updatedCards.map((card) => card.id); - // cardsResponse = await fetchWrapper(endpoint, method, cardsPayload); - // } else { - // const cardsPayload = { cards: updatedCards }; - // cardsResponse = await fetchWrapper(endpoint, method, cardsPayload); - // } - const cardsPayload = { cards: updatedCards }; + let cardsPayload; + if (operation === 'remove') { + const allCardsWithIds = []; + for (const card of updatedCards) { + // const cardIds = updatedCards.map((card) => card.id); + // const cardObjIds = updatedCards.map((card) => card._id); + const cardIds = { + id: card?.id, + _id: card?._id, + }; + + allCardsWithIds?.push(cardIds); + } + const removeCard = allCardsWithIds?.find( + (idPair) => idPair?.id === cardUpdate?.id + ); + cardsPayload = { cardIds: removeCard }; + } else { + const allCardsWithIds = []; + for (const card of updatedCards) { + // const cardIds = updatedCards.map((card) => card.id); + // const cardObjIds = updatedCards.map((card) => card._id); + const cardIds = { + id: card.id, + _id: card._id, + }; + + allCardsWithIds?.push(cardIds); + } + const removeCard = allCardsWithIds?.find( + (idPair) => idPair?.id === cardUpdate?.id + ); + cardsPayload = { cards: updatedCards, cardIds: removeCard }; + } + console.log('CARDS PAYLOAD:', cardsPayload); + cardsResponse = await fetchWrapper(endpoint, method, cardsPayload); const { cardMessage } = cardsResponse; @@ -513,10 +587,6 @@ export const CollectionProvider = ({ children }) => { const { chartMessage } = chartDataResponse; - // const updatedTotalPrice = calculateCollectionValue(selectedCollection); - // console.log('NEW VALUE:', newVal); - - // Prepare the updated collection data const updatedCollection = getUpdatedCollectionData( selectedCollection, updatedTotalPrice, @@ -526,7 +596,6 @@ export const CollectionProvider = ({ children }) => { // updatedCards ); - // Update the collection const collectionEndpoint = createApiUrl( `${userId}/collections/${collectionId}` ); @@ -536,28 +605,6 @@ export const CollectionProvider = ({ children }) => { const { collectionMessage } = collectionResponse; - console.log('********** [--------------------] **********'); - console.log('********** [CARDS] **********'); - console.log(`********** [${cardMessage}] **********`); - console.log(`********** [${cardsResponse.cards}] **********`); - console.log('********** [--------------------] **********'); - console.log('********** [CHARTS] **********'); - console.log(`********** [${chartMessage}] **********`); - console.log(`********** [${chartDataResponse.chartData}] **********`); - console.log('********** [--------------------] **********'); - console.log('********** [COLLECTION] **********'); - console.log(`********** [${collectionMessage}] **********`); - console.log(`********** [${collectionResponse.collectionData}] **********`); - console.log('********** [--------------------] **********'); - - // Restructure the collection object - // Optionally, update context or state with the new collection data - // updateCollectionData(collectionResponse.collectionData, 'allCollections'); - // updateCollectionData( - // collectionResponse.collectionData, - // 'selectedCollection' - // ); - // updateCollectionData(collectionResponse.collectionData, 'collectionData'); setTotalPrice(calculateCollectionValue(updatedCards)); const restructuredCollection = { ...collectionResponse.collectionData, @@ -566,7 +613,6 @@ export const CollectionProvider = ({ children }) => { }; console.log('RESTRUCTURED COLLECTION:', restructuredCollection); - // Return the updated collection data along with responses for cards and chart data return { restructuredCollection, }; @@ -581,7 +627,6 @@ export const CollectionProvider = ({ children }) => { description, }; - // Update in the state first setSelectedCollection(updatedCollection); setAllCollections((prevCollections) => prevCollections.map((collection) => @@ -618,7 +663,7 @@ export const CollectionProvider = ({ children }) => { const handleCardOperation = async ( card, operation, - selectedCollection, + collection, userId, allCollections ) => { @@ -627,33 +672,15 @@ export const CollectionProvider = ({ children }) => { return; } - // Check if selectedCollection is defined, if not set to first collection in allCollections - if (!selectedCollection || !selectedCollection._id) { - if (allCollections && allCollections.length > 0) { - selectedCollection = allCollections[0]; - } else { - console.error('Selected collection or allCollections is empty.'); - return; // Stop execution if no valid collection is available - } + if (!collection) { + console.error( + `Collection with ID ${collection._id} not found in allCollections.` + ); + return; } - const collectionId = selectedCollection._id; - - const updatedCards = getUpdatedCards( - selectedCollection, - card, - operation, - collectionId - ); - - const collectionWithCards = { - ...selectedCollection, - cards: updatedCards, - _id: selectedCollection._id, - }; - const updatedCollection = await getUpdatedCollection( - collectionWithCards, + collection, card, operation, userId @@ -661,9 +688,21 @@ export const CollectionProvider = ({ children }) => { if (updatedCollection) { console.log('UPDATED COLLECTION:', updatedCollection); - updateCollectionData(updatedCollection, 'allCollections'); - updateCollectionData(updatedCollection, 'selectedCollection'); - updateCollectionData(updatedCollection, 'collectionData'); + // Update the relevant collections in the state/context + updateCollectionData( + updatedCollection?.restructuredCollection, + 'allCollections' + ); + if (collection._id === selectedCollection?._id) { + updateCollectionData( + updatedCollection?.restructuredCollection, + 'selectedCollection' + ); + } + updateCollectionData( + updatedCollection?.restructuredCollection, + 'collectionData' + ); } else { console.error('Failed to update collection.'); } @@ -683,7 +722,6 @@ export const CollectionProvider = ({ children }) => { xys: xyData || [], openChooseCollectionDialog, calculateCollectionValue, - // setAllCardPrices, setTotalPrice, setXyData, setUpdatedPricesFromCombinedContext, @@ -694,25 +732,19 @@ export const CollectionProvider = ({ children }) => { getTotalCost: () => getTotalCost(selectedCollection), getCardQuantity: (cardId) => selectedCollection?.cards?.find((c) => c?.id === cardId)?.quantity || 0, - createUserCollection: (userId, newCollectionInfo, name, description) => - createUserCollection(userId, newCollectionInfo, name, description), + createUserCollection, removeCollection, addOneToCollection: (card) => handleCardOperation(card, 'add', selectedCollection, userId), removeOneFromCollection: (card) => handleCardOperation(card, 'remove', selectedCollection, userId), - updateOneFromCollection: (card) => handleCardOperation(card, 'update'), - updateCollection: (collectionWithCards, card, operation, userId) => - getUpdatedCollection(collectionWithCards, card, operation, userId), - updateCollectionState: (collection) => updateCollectionData(collection), - updateCollectionDetails: (updatedInfo, userId, collectionId) => - updateCollectionDetails(updatedInfo, userId, collectionId), - // updateCollectionDetails: (collection) => { - // console.log('UPDATING COLLECTION DETAILS:', collection); - // updateCollectionData(collection, 'allCollections'); - // updateCollectionData(collection, 'selectedCollection'); - // updateCollectionData(collection, 'collectionData'); - // } + updateOneFromCollection: (card) => + handleCardOperation(card, 'update', selectedCollection, userId), + updateCollection: (card, operation, collection) => + handleCardOperation(card, operation, collection, userId), + getUpdatedCollection, + updateCollectionState: updateCollectionData, + updateCollectionDetails, fetchAllCollectionsForUser: fetchAndSetCollections, fetchAllCollections: fetchAndSetCollections, setSelectedCollection, @@ -742,13 +774,13 @@ export const CollectionProvider = ({ children }) => { if (userId) fetchAndSetCollections(); }, [userId]); - useEffect(() => { - if (selectedCollection?.chartData) - setCurrentChartDataSets( - getCurrentChartDataSets(selectedCollection?.chartData) - ); - console.log('CURRENT CHART DATASETS:', currentChartDataSets); - }, [selectedCollection]); + // useEffect(() => { + // if (selectedCollection?.chartData) + // // setCurrentChartDataSets( + // // getCurrentChartDataSets(selectedCollection?.chartData) + // // ); + // console.log('CURRENT CHART DATASETS:', currentChartDataSets); + // }, [selectedCollection]); useEffect(() => { if (selectedCollection?.cards) { @@ -763,32 +795,12 @@ export const CollectionProvider = ({ children }) => { } }, [allCollections, fetchAndSetCollections]); - // setCurrentChartDataSets2( - // transformPriceHistoryToXY(selectedCollection?.chartData) - // ); - // console.log('CURRENT CHART DATASETS 2:', currentChartDataSets2); - // } - useEffect(() => { if (selectedCollection === null || selectedCollection === undefined) { setSelectedCollection(allCollections?.[0]); } }, [userId]); - // setTotalPrice(calculateTotalPrice(selectedCollection)); - // console.log('TOTAL PRICE:', totalPrice); - // }, [selectedCollection, setTotalPrice]); - - // useEffect(() => { - // const updatedSelectedCollection - // { - // ...selectedCollection, - - // } - - // getUpdatedCollection - // }, [setAllCardPrices, setTotalPrice]); - return ( {children} diff --git a/src/context/CollectionContext/checkServerForUpdates.jsx b/src/context/CollectionContext/checkServerForUpdates.jsx deleted file mode 100644 index b622890..0000000 --- a/src/context/CollectionContext/checkServerForUpdates.jsx +++ /dev/null @@ -1,66 +0,0 @@ -// Client-side JavaScript (e.g., in a React component or similar) -import axios from 'axios'; -import { useContext, useState } from 'react'; -import { CollectionContext } from './CollectionContext'; -import { useCombinedContext } from '../CombinedProvider'; -import { createApiUrl, fetchWrapper } from './collectionUtility'; -import { useCookies } from 'react-cookie'; - -async function checkServerForUpdates() { - const { socket } = useCombinedContext(); - const [updateResponse, setUpdateResponse] = useState(null); - try { - // Send an API request to check for updates - socket.emit('UPDATE_REQUEST_CHECK', { - message: 'Checking for updates...', - }); - - socket.on('UPDATE_CHECK_INITIAL_RESPONSE', (response) => { - console.log('UPDATE_CHECK_INITIAL_RESPONSE', response); - setUpdateResponse(response.message); - }); - - socket.on('UPDATES_RECEIVED', (response) => { - console.log('UPDATE_CHECK_RESPONSE', response); - if (response.hasUpdates) { - // If there are updates, call your client-side update function - updateCollectionClientSide(response.data); - } - setUpdateResponse(response.message); - }); - } catch (error) { - console.error('Error checking for updates:', error); - } -} -setInterval(checkServerForUpdates, 300000); // Check every 5 minutes - -const updateCollectionClientSide = async (response) => { - const { collection, setCollection, updateCollectionState } = - useContext(CollectionContext); - try { - updateCollectionState(response); - // Now, send an API request to save the updated collection data - await saveUpdatedCollectionData(response); - } catch (error) { - console.error('Error updating collection client-side:', error); - } -}; - -const saveUpdatedCollectionData = async (response) => { - const [cookies] = useCookies(['user']); - const userId = cookies.user?.id; - const collectionId = response._id; - try { - const collectionEndpoint = createApiUrl( - `${userId}/collections/${collectionId || ''}` - ); - const collectionResponse = await fetchWrapper(collectionEndpoint, 'PUT', { - response, - }); - - console.log('collectionResponse', collectionResponse); - console.log('Collection data saved successfully'); - } catch (error) { - console.error('Error saving updated collection data:', error); - } -}; diff --git a/src/context/CollectionContext/collectionUtility.jsx b/src/context/CollectionContext/collectionUtility.jsx index 3b2858f..93ee1af 100644 --- a/src/context/CollectionContext/collectionUtility.jsx +++ b/src/context/CollectionContext/collectionUtility.jsx @@ -9,12 +9,20 @@ const initialCollectionState = { quantity: 0, // Initialize as 0 if not provided totalQuantity: 0, // Initialize as 0 if not provided previousDayTotalPrice: 0, // Initialize as 0 if not provided + lastSavedPrice: { + num: 0, + timestamp: '', + }, // Initialize as 0 if not provided + latestPrice: { + num: 0, + timestamp: '', + }, // Initialize as 0 if not provided dailyPriceChange: 0, // Initialize as 0 if not provided priceDifference: 0, // Initialize as 0 if not provided priceChange: 0, // Initialize as 0 if not provided allCardPrices: [], // Initialize as empty array if not provided cards: [], // Initialize as empty array if not provided - currentChartDataSets: [], // Initialize as empty array if not provided + currentChartDataSets2: [], // Initialize as empty array if not provided xys: [], // Use defaultXyData or initialize as empty if not provided chartData: { name: '', // Initialize as empty string if not provided @@ -586,44 +594,87 @@ const constructPayloadWithDifferences = ( return { payload, nonMatchingKeys, typeMismatchContent }; // Return both the payload, the list of non-matching keys, and type mismatch messages }; -function getCurrentChartDataSets(chartData) { - if (!chartData || !chartData.datasets) { - console.error('Invalid or missing chart data'); - return []; - } +// function getCurrentChartDataSets(chartData) { +// if (!chartData || !chartData?.allXYValues) { +// console.error('Invalid or missing chart data'); +// return []; +// } - const currentChartDataSets = []; - - // Iterate over each dataset - chartData.datasets.forEach((dataset) => { - // Check if dataset has 'data' array - if (dataset.data && dataset.data.length > 0) { - dataset.data.forEach((dataEntry) => { - // Check if dataEntry has 'xys' array - if (dataEntry.xys && dataEntry.xys.length > 0) { - // Add both 'data' and 'label' from each 'xys' entry - currentChartDataSets.push( - ...dataEntry.xys.map((xy) => ({ - ...xy.data, // Spread the 'data' object - label: xy.label, // Include the 'label' - })) - ); - } - }); - } - }); +// const currentChartDataSets = []; + +// // Iterate over each dataset +// chartData.allXYValues.forEach((val) => { +// // Check if dataset has 'data' array +// if (dataset.data && dataset.data.length > 0) { +// dataset.data.forEach((dataEntry) => { +// // Check if dataEntry has 'xys' array +// if (dataEntry.xys && dataEntry.xys.length > 0) { +// // Add both 'data' and 'label' from each 'xys' entry +// currentChartDataSets.push( +// ...dataEntry.xys.map((xy) => ({ +// ...xy.data, // Spread the 'data' object +// label: xy.label, // Include the 'label' +// })) +// ); +// } +// }); +// } +// }); + +// return currentChartDataSets; +// } - return currentChartDataSets; -} +// function getCurrentChartDataSets(chartData) { +// if (!chartData || !chartData.datasets) { +// console.error('Invalid or missing chart data'); +// return []; +// } + +// const currentChartDataSets = []; + +// // Iterate over each dataset +// chartData.datasets.forEach((dataset) => { +// // Check if dataset has 'data' array +// if (dataset.data && dataset.data.length > 0) { +// dataset.data.forEach((dataEntry) => { +// // Check if dataEntry has 'xys' array +// if (dataEntry.xys && dataEntry.xys.length > 0) { +// // Add both 'data' and 'label' from each 'xys' entry +// currentChartDataSets.push( +// ...dataEntry.xys.map((xy) => ({ +// ...xy.data, // Spread the 'data' object +// label: xy.label, // Include the 'label' +// })) +// ); +// } +// }); +// } +// }); + +// return currentChartDataSets; +// } const calculateCollectionValue = (cards) => { - if (!cards?.cards && !Array.isArray(cards) && !cards?.name) { + if ( + !cards?.cards && + !Array.isArray(cards) && + !cards?.name && + !cards?.restructuredCollection + ) { console.warn('Invalid or missing collection', cards); return 0; } + if (cards?.tag === 'new') { return 0; } + if (cards?.restructuredCollection) { + return cards?.restructuredCollection?.cards.reduce((totalValue, card) => { + const cardPrice = card?.price || 0; + const cardQuantity = card?.quantity || 0; + return totalValue + cardPrice * cardQuantity; + }, 0); + } if (cards?.cards && Array.isArray(cards?.cards)) { return cards?.cards.reduce((totalValue, card) => { const cardPrice = card?.price || 0; @@ -823,7 +874,7 @@ module.exports = { createPayload, logError, constructPayloadWithDifferences, - getCurrentChartDataSets, + // getCurrentChartDataSets, getPriceChange, getUpdatedChartData, mergeCards, diff --git a/src/context/CollectionContext/copy.js b/src/context/CollectionContext/copy.js deleted file mode 100644 index cb6ff73..0000000 --- a/src/context/CollectionContext/copy.js +++ /dev/null @@ -1,926 +0,0 @@ -// import React, { -// createContext, -// useState, -// useEffect, -// useCallback, -// useMemo, -// useContext, -// } from 'react'; -// import { useCookies } from 'react-cookie'; -// import { -// initialCollectionState, -// fetchWrapper, -// removeDuplicateCollections, -// calculateAndUpdateTotalPrice, -// calculateTotalPrice, -// getTotalCost, -// getCardPrice, -// } from './exampleImport.js'; -// import { useCombinedContext } from '../CombinedProvider.jsx'; -// import { useUserContext } from '../UserContext/UserContext.js'; -// import moment from 'moment'; -// // import { useUserContext } from '../UserContext/UserContext.js'; -// // 1. Define a default context value -// const defaultContextValue = { -// allCollections: [], -// allCardPrices: [], -// xy: [], -// selectedCollection: {}, -// collectionData: initialCollectionState, -// totalCost: 0, -// openChooseCollectionDialog: false, -// updatedPricesFromCombinedContext: {}, -// setUpdatedPricesFromCombinedContext: () => {}, -// setOpenChooseCollectionDialog: () => {}, -// calculateTotalPrice: () => {}, -// getTotalCost: () => {}, -// createUserCollection: () => {}, -// removeCollection: () => {}, -// fetchAllCollectionsForUser: () => {}, -// setSelectedCollection: () => {}, -// setAllCollections: () => {}, -// addOneToCollection: () => {}, -// removeOneFromCollection: () => {}, -// }; - -// // 2. Replace null with the default value when creating the context -// export const CollectionContext = createContext(defaultContextValue); -// const BASE_API_URL = `${process.env.REACT_APP_SERVER}/api/users`; - -// const filterOutDuplicateYValues = (datasets) => { -// // console.log('DATASETS:', datasets); -// const seenYValues = new Set(); -// return datasets?.filter((data) => { -// const yValue = data?.y; -// if (seenYValues.has(yValue)) { -// return false; -// } -// seenYValues.add(yValue); -// return true; -// }); -// }; - -// const transformChartData = (chartData) => { -// let pointsArray = []; - -// if (Array.isArray(chartData?.datasets)) { -// chartData?.datasets?.forEach((dataset) => { -// dataset.data?.forEach((dataEntry) => { -// dataEntry.xys?.forEach((xyEntry) => { -// const { x, y } = xyEntry.data; -// if (x && y !== undefined) { -// pointsArray.push({ x, y }); -// } -// }); -// }); -// }); -// } else { -// console.error( -// 'Expected chartData.datasets to be an array, but got:', -// chartData -// ); -// } - -// return pointsArray; -// }; - -// function convertData(originalData) { -// let finalDataForChart = []; - -// const { datasets } = originalData; - -// if (Array.isArray(datasets) && datasets.length > 0) { -// datasets.forEach((dataset, index) => { -// // Loop over all datasets, not just the last one -// if (Array.isArray(dataset.data) && dataset.data.length > 0) { -// dataset.data.forEach((dataEntry) => { -// dataEntry.xys?.forEach((xyEntry) => { -// const { label, data } = xyEntry; -// // Assume that finalDataForChart has an array for each label -// finalDataForChart[label] = finalDataForChart[label] || []; - -// data.forEach(({ x, y }) => { -// if (x && y !== undefined) { -// finalDataForChart[label].push({ x, y }); -// } -// }); -// }); -// }); -// } -// }); -// } - -// // Convert the data into the format expected by Nivo -// const nivoData = Object.keys(finalDataForChart).map((label) => ({ -// id: label, -// data: finalDataForChart[label], -// })); - -// return { -// ...originalData, -// finalDataForChart: nivoData, // Replace this line to return Nivo-formatted data -// }; -// } - -// const isEmpty = (obj) => { -// return ( -// [Object, Array].includes((obj || {}).constructor) && -// !Object.entries(obj || {}).length -// ); -// }; -// const validateData = (data, eventName, functionName) => { -// const dataType = typeof data; -// console.log( -// `[Info] Received data of type: ${dataType} in ${functionName} triggered by event: ${eventName}` -// ); -// if (data === null || data === undefined) { -// console.warn( -// `[Warning] Received null or undefined data in ${functionName} triggered by event: ${eventName}` -// ); -// return false; -// } -// if (isEmpty(data)) { -// console.error( -// `[Error] Received empty data object or array in ${functionName} triggered by event: ${eventName}` -// ); -// return false; -// } -// return true; -// }; - -// const handleCardAddition = (currentCards, cardToAdd) => { -// // Check if the card already exists in the currentCards array -// const cardToAddId = -// typeof cardToAdd.id === 'number' ? String(cardToAdd.id) : cardToAdd.id; - -// const matchingCard = currentCards.find((c) => c.id === cardToAddId); - -// if (matchingCard) { -// matchingCard.quantity++; -// return [...currentCards]; -// } else { -// return [...currentCards, { ...cardToAdd, id: cardToAddId, quantity: 1 }]; -// } -// }; - -// const handleCardRemoval = (currentCards, cardToRemove) => { -// // Convert the cardToRemove's id to a string if it's a number -// const cardToRemoveId = -// typeof cardToRemove.id === 'number' -// ? String(cardToRemove.id) -// : cardToRemove.id; - -// const matchingCard = currentCards.find((c) => c.id === cardToRemoveId); - -// if (!matchingCard) { -// console.error('Card not found in the collection.'); -// return [...currentCards]; -// } - -// if (matchingCard.quantity > 1) { -// matchingCard.quantity--; -// return [...currentCards]; -// } else { -// return currentCards.filter((card) => card.id !== cardToRemoveId); -// } -// }; -// const calculateTotalFromAllCardPrices = (allCardPrices) => { -// if (!Array.isArray(allCardPrices)) return 0; -// return allCardPrices.reduce( -// (total, price) => total + ensureNumber(price, 0), -// 0 -// ); -// }; -// const ensureNumber = (value, defaultValue = 0) => { -// let num = parseFloat(value); -// return isNaN(num) ? defaultValue : num; -// }; -// const HTTP_METHODS = { -// POST: 'POST', -// PUT: 'PUT', -// GET: 'GET', -// DELETE: 'DELETE', -// }; -// const handleError = (error) => { -// console.error(`An error occurred: ${error.message}`); -// }; -// const findCollectionIndex = useCallback( -// (collections, id) => -// collections?.findIndex((collection) => collection?._id === id) ?? -1, -// [] -// ); -// const apiRequest = async (userId, endpoint, method, data) => { -// try { -// const url = createApiUrl(endpoint); -// const response = await fetchWrapper(url, method, data); - -// if (!response || response.error) { -// throw new Error( -// response ? response.error.message : 'Failed to connect to the server.' -// ); -// } - -// return handleApiResponse(response, method); -// } catch (error) { -// console.error(`API Request failed: ${error.message}`); -// return null; -// } -// }; -// const createApiUrl = (path) => `${BASE_API_URL}/${path}`; -// const handleApiResponse = (response, method) => { -// // Handling POST requests -// if (method === 'POST' && response?.data?.newCollection) { -// return response.data.newCollection; -// } - -// // Handling PUT requests (updating a collection) -// if (method === 'PUT' && response.data?.data?.updatedCollection) { -// return response.data.data.updatedCollection; -// } - -// throw new Error('Unexpected response format'); -// }; -// const formatDate = () => moment().format('YYYY-MM-DD HH:mm'); -// // Utility function to filter unique X values -// const filterUniqueXValues = (values) => { -// const uniqueXValues = new Set(); -// return values.filter((entry) => { -// if (!uniqueXValues.has(entry.x)) { -// uniqueXValues.add(entry.x); -// return true; -// } -// return false; -// }); -// }; -// const getUniqueFilteredXYValues = (allXYValues) => { -// const uniqueXValues = new Set(); - -// return allXYValues -// .filter((entry) => entry.y !== 0) -// .filter((entry) => { -// if (!uniqueXValues.has(entry.x)) { -// uniqueXValues.add(entry.x); -// return true; -// } -// return false; -// }); -// }; -// const safeFetch = async (url, method, payload) => { -// try { -// const response = await fetchWrapper(url, method, payload); -// return handleApiResponse(response, method); -// } catch (error) { -// handleError(error); -// return null; -// } -// }; -// const fetchAndUpdateCollectionData = async ( -// endpoint, -// method, -// payload, -// setCollectionFn, -// setErrorFn -// ) => { -// try { -// const response = await fetchWrapper(endpoint, method, payload); -// const data = handleApiResponse(response, method); - -// if (data) { -// setCollectionFn(data); -// } else { -// throw new Error('Data is null'); -// } -// } catch (error) { -// setErrorFn(`Failed to process the request: ${error.message}`); -// } -// }; - -// const apiRequestWrapper = (userId, endpoint, method, data) => { -// return apiRequest(userId, `${userId}/collections${endpoint}`, method, data); -// }; - -// const updateCollectionData = (newData, updaterFn) => { -// updaterFn((prevData) => { -// const index = findCollectionIndex(prevData, newData._id); -// return index === -1 -// ? [...prevData, newData] -// : Object.assign([...prevData], { [index]: newData }); -// }); -// }; - -// const CollectionHandler = () => { -// // const { cardPrices } = useCombinedContext(); -// const [cookies] = useCookies(['user']); -// const { triggerCronJob } = useUserContext(); -// const [collectionData, setCollectionData] = useState(initialCollectionState); -// const [allCollections, setAllCollections] = useState([]); -// const [allCardPrices, setAllCardPrices] = useState([]); -// const [xyData, setXyData] = useState([ -// // { -// // label: '', -// // data: [], -// // }, -// ]); // New state to hold xy data -// // const [updatedPrices, setUpdatedPrices] = useState([]); -// const [ -// updatedPricesFromCombinedContext, -// setUpdatedPricesFromCombinedContext, -// ] = useState({}); -// const [selectedCollection, setSelectedCollection] = useState({}); -// const [openChooseCollectionDialog, setOpenChooseCollectionDialog] = -// useState(false); -// const chartData = selectedCollection?.chartData || {}; -// // const datasets = chartData?.datasets || []; -// const userId = cookies.user?.id; -// const totalCost = useMemo( -// () => getTotalCost(selectedCollection), -// [selectedCollection] -// ); -// // [COLLECTION RETRIEVAL] -// const fetchCollections = useCallback( -// async (userId) => { -// fetchAndUpdateCollectionData( -// `${BASE_API_URL}/${userId}/collections`, -// HTTP_METHODS.GET, -// null, -// setAllCollections, -// handleError -// ); -// }, -// [setAllCollections, handleError] -// ); -// const useCollection = (userId) => { -// const [collections, setCollections] = useState(null); - -// const fetchAndSetCollections = useCallback(async () => { -// const data = await safeFetch( -// `${BASE_API_URL}/${userId}/collections`, -// HTTP_METHODS.GET -// ); -// if (data) setCollections(data.allCollections); -// }, [userId]); - -// return [collections, fetchAndSetCollections]; -// }; -// // ... Other Collection related methods - -// // [COLLECTION CREATION & DELETION] -// const useCollectionCrud = (userId) => { -// const [selectedCollection, setSelectedCollection] = useState(null); -// const [allCollections, setAllCollections] = useState([]); - -// const createUserCollection = useCallback( -// async (newCollectionInfo) => { -// const payload = { ...newCollectionInfo, userId, totalCost: 0, totalPrice: 0 }; -// const response = await safeFetch( -// createApiUrl(`${userId}/collections/newCollection/${userId}`), -// 'POST', -// payload -// ); -// if (response) performCollectionUpdate(response, 'create'); -// }, -// [userId] -// ); - -// const removeCollection = useCallback( -// async (collectionId) => { -// await safeFetch( -// createApiUrl(`${userId}/collections/${collectionId}`), -// 'DELETE' -// ); -// setAllCollections((prev) => prev.filter((item) => item._id !== collectionId)); -// }, -// [userId] -// ); -// const handleCollection = useCallback( -// async (method, payload, collectionId = '') => { -// const endpoint = collectionId -// ? `${userId}/collections/${collectionId}` -// : `${userId}/collections`; -// fetchAndUpdateCollectionData( -// endpoint, -// method, -// payload, -// setAllCollections, -// handleError -// ); -// }, -// [userId, setAllCollections, handleError] -// ); -// return [createUserCollection, removeCollection]; -// }; -// // [CARD ADDITION & REMOVAL] -// const addOrRemoveCard = useCallback( -// async (card, cardInfo, operation) => { -// // Your previous logic here. - -// const collectionId = selectedCollection._id || allCollections[0]._id; -// if (!collectionId) { -// // Handle error -// return; -// } - -// const updateInfo = getUpdateInfo(card, cardInfo, operation); // Assume this function is implemented -// const method = operation === 'create' ? 'POST' : 'PUT'; - -// fetchAndUpdateCollectionData( -// `${userId}/collections/${collectionId}`, -// method, -// updateInfo, -// setSelectedCollection, -// handleError -// ); -// }, -// [userId, setSelectedCollection, handleError] -// ); -// const useCardManager = (userId, selectedCollection, allCollections) => { -// const addOrRemoveCard = useCallback( -// async (card, operation) => { -// // ... (your existing logic here, try to use helper methods) -// await performCollectionUpdate(updatedCollection, 'update'); -// }, -// [userId, selectedCollection, allCollections] -// ); - -// return [addOrRemoveCard]; -// }; - -// // [COLLECTION DATA UPDATE] -// const performCollectionUpdate = useCallback( -// async (updateInfo, operation) => { -// const method = operation === 'create' ? 'POST' : 'PUT'; -// const endpoint = updateInfo._id ? `/${updateInfo._id}` : ''; -// const updatedCollection = await apiRequestWrapper( -// userId, -// endpoint, -// method, -// updateInfo -// ); -// if (updatedCollection) { -// updateCollectionData(updatedCollection, setAllCollections); -// updateCollectionData(updatedCollection, setSelectedCollection); -// } -// }, -// [userId, setAllCollections, setSelectedCollection] -// ); -// return ( -// // Your JSX components here -// ); -// }; -// // [COLLECTION RETRIEVAL] -// // const fetchCollections = useCallback( -// // async (userId) => { -// // fetchAndUpdateCollectionData( -// // `${BASE_API_URL}/${userId}/collections`, -// // HTTP_METHODS.GET, -// // null, -// // setAllCollections, -// // handleError -// // ); -// // }, -// // [setAllCollections, handleError] -// // ); -// // const setCollections = useCallback((collections) => { -// // try { -// // if (!collections || !Array.isArray(collections)) -// // throw new Error('Invalid collections array'); - -// // // Other logic for setting collections -// // } catch (error) { -// // handleError(error); -// // } -// // }, []); -// // const fetchAndSetCollections = useCallback(async () => { -// // try { -// // const collections = await fetchCollections(userId); -// // if (collections) setCollections(collections); -// // } catch (error) { -// // handleError(error); -// // } -// // }, [userId, fetchCollections, setCollections]); -// // // [COLLECTION CREATION & DELETION] -// // const createUserCollection = async ( -// // userId, -// // newCollectionInfo, -// // name, -// // description -// // ) => { -// // if (!userId) { -// // console.error('User ID is undefined.'); -// // return; -// // } -// // if ( -// // !validateData( -// // newCollectionInfo, -// // 'Create User Collection', -// // 'createUserCollection' -// // ) -// // ) { -// // console.error('Validation failed for collection data.'); -// // return; -// // } - -// // const payload = { -// // name: name || newCollectionInfo.name, -// // description: description || newCollectionInfo.description, -// // userId: userId || newCollectionInfo.userId, -// // totalCost: 0, -// // totalPrice: 0, -// // quantity: 0, -// // totalQuantity: 0, -// // xys: xyData || [], -// // allCardPrices: [], -// // cards: [], -// // chartData: {}, -// // }; - -// // try { -// // const url = createApiUrl(`${userId}/collections/newCollection/${userId}`); -// // const response = await fetchWrapper(url, 'POST', payload); - -// // if (!response) { -// // console.error('Failed to connect to the server.'); -// // return; -// // } - -// // if (response.error) { -// // console.error( -// // `Failed to create a new collection: ${response.error.message}` -// // ); -// // return; -// // } - -// // const savedCollection = handleApiResponse(response, 'POST'); -// // await performCollectionUpdate(payload, 'create'); - -// // // updateCollectionData(savedCollection, 'allCollections'); -// // // updateCollectionData(savedCollection, 'collectionData'); -// // } catch (error) { -// // console.error(`Failed to create a new collection: ${error.message}`); -// // } -// // }; -// // const removeCollection = async (collection) => { -// // if (!collection._id) { -// // console.error('Collection ID is undefined.'); -// // return; -// // } - -// // try { -// // const url = createApiUrl(`${userId}/collections/${collection._id}`); -// // const response = await fetchWrapper(url, 'DELETE'); - -// // if (response.error) { -// // console.error('Failed to delete the collection:', response.error); -// // return; -// // } - -// // setAllCollections((prev) => -// // prev.filter((item) => item._id !== collection._id) -// // ); - -// // if (selectedCollection._id === collection._id) { -// // setSelectedCollection(initialCollectionState); -// // setCollectionData(initialCollectionState); -// // } -// // } catch (error) { -// // console.error(`Failed to delete the collection: ${error.message}`); -// // } -// // }; -// // // const handleCollection = useCallback( -// // // async (method, payload, collectionId = '') => { -// // // const endpoint = collectionId -// // // ? `${userId}/collections/${collectionId}` -// // // : `${userId}/collections`; -// // // fetchAndUpdateCollectionData( -// // // endpoint, -// // // method, -// // // payload, -// // // setAllCollections, -// // // handleError -// // // ); -// // // }, -// // // [userId, setAllCollections, handleError] -// // // ); -// // // [CARD ADDITION & REMOVAL] -// // const getUpdatedCards = (activeCollection, card, operation) => { -// // const handler = -// // operation === 'add' ? handleCardAddition : handleCardRemoval; -// // const updatedCards = handler(activeCollection?.cards, card); - -// // return updatedCards.map((card) => { -// // const cardPrice = card.card_prices?.[0]?.tcgplayer_price; -// // const computedPrice = cardPrice * card.quantity; - -// // const newDataset = { x: formatDate(), y: computedPrice }; -// // card.chart_datasets = filterUniqueXValues([ -// // ...(card?.chart_datasets || []), -// // newDataset, -// // ]); - -// // card.price = cardPrice; -// // card.totalPrice = computedPrice; -// // return card; -// // }); -// // }; -// // const getNewChartData = (activeCollection, updatedPrice, newDataSet) => { -// // const combinedXYValues = [ -// // ...(selectedCollection?.chartData?.datasets?.flatMap( -// // (dataset) => dataset.data -// // ) || []), -// // newDataSet.data[0].xy, -// // ]; - -// // const filteredXYValues = getUniqueFilteredXYValues(combinedXYValues); - -// // return { -// // name: `Chart for Collection: ${activeCollection?.name}`, -// // userId: userId, -// // updatedPrice: updatedPrice, -// // xys: xyData || [], -// // datasets: [ -// // ...(selectedCollection?.chartData?.datasets || []), -// // newDataSet, -// // ], -// // allXYValues: filteredXYValues, -// // }; -// // }; -// // // const addOrRemoveCard = useCallback( -// // // async (card, cardInfo, operation) => { -// // // // Your previous logic here. - -// // // const collectionId = selectedCollection._id || allCollections[0]._id; -// // // if (!collectionId) { -// // // // Handle error -// // // return; -// // // } - -// // // const updateInfo = getUpdateInfo(card, cardInfo, operation); // Assume this function is implemented -// // // const method = operation === 'create' ? 'POST' : 'PUT'; - -// // // fetchAndUpdateCollectionData( -// // // `${userId}/collections/${collectionId}`, -// // // method, -// // // updateInfo, -// // // setSelectedCollection, -// // // handleError -// // // ); -// // // }, -// // // [userId, setSelectedCollection, handleError] -// // // ); -// // // // [COLLECTION DATA UPDATE] -// // // const performCollectionUpdate = useCallback( -// // // async (updateInfo, operation) => { -// // // const method = operation === 'create' ? 'POST' : 'PUT'; -// // // const endpoint = updateInfo._id ? `/${updateInfo._id}` : ''; -// // // const updatedCollection = await apiRequestWrapper( -// // // userId, -// // // endpoint, -// // // method, -// // // updateInfo -// // // ); -// // // if (updatedCollection) { -// // // updateCollectionData(updatedCollection, setAllCollections); -// // // updateCollectionData(updatedCollection, setSelectedCollection); -// // // } -// // // }, -// // // [userId, setAllCollections, setSelectedCollection] -// // // ); -// // const updateCollection = (newData, collectionType, updaterFn) => { -// // switch (collectionType) { -// // case 'allCollections': -// // return updaterFn((prevCollections = []) => { -// // const existingIndex = findCollectionIndex( -// // prevCollections, -// // newData?._id -// // ); -// // return existingIndex === -1 -// // ? [...prevCollections, newData] -// // : Object.assign([...prevCollections], { [existingIndex]: newData }); -// // }); -// // case 'selectedCollection': -// // case 'collectionData': -// // return updaterFn(newData); -// // default: -// // break; -// // } -// // }; -// // const updateActiveCollection = useCallback( -// // async (collectionData, existingChartData = {}) => { -// // const isCreatingNew = !collectionData?._id; -// // const isNewCollectionEndpoint = -// // collectionData?.endpoint === 'newCollection'; // Add this line to check if it's the newcollection endpoint -// // const endpoint = isCreatingNew -// // ? createApiUrl(`${userId}/collections`) -// // : createApiUrl(`${userId}/collections/${collectionData._id}`); -// // let method = isCreatingNew ? 'POST' : 'PUT'; // Default setting -// // console.log(`Debug: Fetching ${method} ${endpoint}`); // Debugging log - -// // // Restrict to only POST for the 'newcollection' endpoint -// // if (!isCreatingNew && isNewCollectionEndpoint) { -// // method = 'POST'; -// // } -// // try { -// // const response = await fetchWrapper(endpoint, method, collectionData); -// // const updatedCollection = handleApiResponse(response, method); - -// // if (!isCreatingNew && !updatedCollection) { -// // throw new Error('Failed to update the existing collection'); -// // } -// // const newChartData = { -// // ...updatedCollection.chartData, -// // xys: [ -// // ...(existingChartData.xys || []), // Spread existing xys data -// // { -// // label: `Update Number ${ -// // updatedCollection?.chartData?.datasets?.length + 1 || 1 -// // }`, -// // data: [ -// // ...(existingChartData.data || []), // Spread existing data -// // { -// // x: moment().format('YYYY-MM-DD HH:mm'), -// // y: updatedCollection.totalPrice, -// // }, -// // ], -// // }, -// // ], -// // datasets: [ -// // ...(updatedCollection.chartData?.datasets || []), -// // { -// // data: [ -// // { -// // xys: [ -// // { -// // label: `Update Number ${ -// // updatedCollection?.chartData?.datasets?.length + 1 || 1 -// // }`, -// // data: [ -// // { -// // x: moment().format('YYYY-MM-DD HH:mm'), -// // y: updatedCollection.totalPrice, -// // }, -// // ], -// // }, -// // ], -// // additionalPriceData: { -// // priceChanged: false, -// // initialPrice: -// // updatedCollection.chartData?.updatedPrice || 0, -// // updatedPrice: updatedCollection.totalPrice, -// // priceDifference: 0, -// // priceChange: 0, -// // }, -// // }, -// // ], -// // }, -// // ], -// // }; -// // updatedCollection.chartData = newChartData; - -// // const convertedData = convertData(newChartData); -// // updatedCollection.xys = convertedData; -// // // setXyData(convertedData.finalDataForChart); -// // xyData.push(...convertedData.finalDataForChart); // Spread to add to existing array -// // updateCollectionData(updatedCollection, 'selectedCollection'); -// // updateCollectionData(updatedCollection, 'allCollections'); -// // } catch (error) { -// // console.error(`Failed to update the collection: ${error.message}`); -// // } -// // }, -// // [userId, updateCollectionData] -// // ); -// // console.log( -// // '<----------$$$$$$$$$CONVERTED DATA FOR CHART$$$$$$$$$---------->', -// // xyData -// // ); -// // useEffect(() => { -// // // Check if the prices are updated or new cards are added -// // const updatedPricesArray = -// // updatedPricesFromCombinedContext?.updatedPrices || []; - -// // if (!Array.isArray(updatedPricesArray)) { -// // return; // Exit the useEffect early if not an array -// // } - -// useEffect(() => { -// const updatedPricesArray = Object.keys( -// updatedPricesFromCombinedContext || {} -// ).map((cardId) => updatedPricesFromCombinedContext[cardId]); - -// console.log( -// '[1][PRICE UPDATE: COMBINED CONTEXT IN COLLECTION][UPDATED PRICES]==========>', -// updatedPricesArray -// ); - -// const updatedCardPrices = []; - -// updatedPricesArray.forEach((card) => { -// const currentCardPrice = selectedCollection?.cards[card?.id]?.price; - -// // Check if this is the special tagged card -// if (card._tag === 'updated') { -// console.log('Found the special card:', card); -// } - -// if (card?.updatedPrice !== currentCardPrice) { -// updatedCardPrices.push(card); -// console.log( -// '[2][PRICE UPDATE: COMBINED CONTEXT IN COLLECTION][CARD]==========>', -// card -// ); -// console.log( -// 'CARD FROM SELECTED COLLECTIONS:', -// selectedCollection.cards[card.id] -// ); -// } else { -// console.log('Price has not been updated for card with ID:', card.id); -// } -// }); - -// if (updatedCardPrices.length > 0) { -// updatedCardPrices.forEach((card) => { -// addOrRemoveCard(card, { updated: true }, 'update'); -// }); -// } -// }, [updatedPricesFromCombinedContext]); - -// const contextValue = useMemo( -// () => ({ -// // DATA -// allCollections, -// selectedCollection, -// collectionData, -// totalCost, - -// allCardPrices: selectedCollection?.allCardPrices || [], -// xys: xyData || [], -// openChooseCollectionDialog, -// updatedPricesFromCombinedContext, -// setUpdatedPricesFromCombinedContext: (updatedPrices) => { -// // This is the function that will be passed to the combined context to update the prices -// setUpdatedPricesFromCombinedContext(updatedPrices); -// }, -// setOpenChooseCollectionDialog, -// // FUNCTIONS -// calculateTotalPrice: () => getCardPrice(selectedCollection), -// getTotalCost: () => getTotalCost(selectedCollection), -// // FUNCTIONS -// createUserCollection: (userId, newCollectionInfo) => -// createUserCollection( -// userId, -// newCollectionInfo, -// newCollectionInfo.name, -// newCollectionInfo.description -// ), -// removeCollection: (collection) => removeCollection(collection), -// fetchAllCollectionsForUser: fetchAndSetCollections, -// setSelectedCollection: updateActiveCollection, -// setAllCollections: (collections) => setAllCollections(collections), -// addOneToCollection: (card, cardInfo) => -// addOrRemoveCard(card, cardInfo, 'add'), -// removeOneFromCollection: (card) => addOrRemoveCard(card, null, 'remove'), -// }), -// [allCollections, selectedCollection, totalCost] -// ); - -// useEffect(() => { -// console.log('COLLECTION CONTEXT: ', { -// contextValue, -// }); -// }, [contextValue, updatedPricesFromCombinedContext]); -// // Assuming updatedPrices is passed as a prop or state - -// useEffect(() => { -// if (selectedCollection && totalCost) { -// // Trigger the cron job whenever the selectedCollection changes -// triggerCronJob(); -// } -// }, [selectedCollection, triggerCronJob, totalCost]); - -// useEffect(() => { -// console.log('Total Cost has been updated:', totalCost); -// }, [totalCost]); - -// useEffect(() => { -// if (userId) fetchAndSetCollections(); -// }, [userId]); - -// return ( -// -// {children} -// -// ); -// }; - -// // useCollectionStore.js -// // import { useContext } from 'react'; -// // import { CollectionContext } from '../CollectionContext/CollectionContext'; - -// export const useCollectionStore = () => { -// const context = useContext(CollectionContext); -// if (!context) { -// throw new Error( -// 'useCollectionStore must be used within a CollectionProvider' -// ); -// } -// return context; -// }; diff --git a/src/context/CollectionContext/exampleImport.js b/src/context/CollectionContext/exampleImport.js deleted file mode 100644 index 68aebb7..0000000 --- a/src/context/CollectionContext/exampleImport.js +++ /dev/null @@ -1,123 +0,0 @@ -// // Initial collection state -// export const initialCollectionState = { -// _id: '', -// cards: [], -// quantity: 0, -// totalPrice: 0, -// }; - -// // Fetch wrapper function -// // export const fetchWrapper = async (url, method, body = null) => { -// // try { -// // const options = { -// // method, -// // headers: { 'Content-Type': 'application/json' }, -// // ...(body && { body: JSON.stringify(body) }), -// // }; -// // const response = await fetch(url, options); -// // // console.log('RESPONSE:', response); -// // if (!response.ok) { -// // const message = `HTTP error! status: ${response.status}`; -// // console.error(message); -// // throw new Error(message); -// // } -// // return await response.json(); -// // } catch (error) { -// // console.error('Fetch Error:', error); -// // throw error; -// // // if (!response.ok) { -// // // throw new Error(`HTTP error! status: ${response.status}`); -// // // } -// // // return await response.json(); -// // } -// // }; - -// // Remove duplicate collections -// // export const removeDuplicateCollections = (collections) => { -// // const uniqueCollections = {}; -// // collections?.forEach((collection) => { -// // uniqueCollections[collection._id] = collection; -// // }); -// // return Object.values(uniqueCollections); -// // }; - -// // Calculate and update total price -// export const calculateAndUpdateTotalPrice = (collection) => { -// if (!collection || !collection.cards) return 0; -// return collection.cards.reduce( -// (total, card) => total + card.price * card.quantity, -// 0 -// ); -// }; - -// // Format card data -// export const formatCardData = (card) => ({ -// id: card.id, -// ...Object.fromEntries( -// [ -// 'name', -// 'type', -// 'frameType', -// 'description', -// 'card_images', -// 'archetype', -// 'atk', -// 'def', -// 'level', -// 'race', -// 'attribute', -// 'quantity', -// ].map((key) => [key, card[key] || null]) -// ), -// }); - -// // Get card quantity from a collection -// export const getCardQuantity = (collectionId, allCollections) => { -// const collection = allCollections?.find((item) => item._id === collectionId); -// if (!collection) return 0; -// return collection.cards.reduce((acc, card) => acc + card.quantity, 0); -// }; - -// // Calculate total cost of the selected collection -// // export const getTotalCost = (selectedCollection) => { -// // if (!selectedCollection?.cards) return 0; -// // return selectedCollection.cards.reduce((total, card) => { -// // const price = card.card_prices?.[0]?.tcgplayer_price; -// // return price ? total + parseFloat(price) : total; -// // }, 0); -// // }; -// export const getTotalCost = (selectedCollection) => { -// if (!selectedCollection || !Array.isArray(selectedCollection.cards)) return 0; - -// return selectedCollection.cards.reduce((total, card) => { -// const cardPrice = -// (card.card_prices && card.card_prices[0]?.tcgplayer_price) || 0; -// return total + cardPrice * card.quantity; -// }, 0); -// }; - -// export const getCardPrice = (collection) => -// console.log('CARD:', collection) || -// parseFloat(collection?.cards?.card_prices?.[0]?.tcgplayer_price || 0); - -// // Function to calculate total price of a collection -// export const calculateTotalPrice = (collection) => { -// // Assuming collection is an object where each key-value pair is cardId-price -// return Object.allCardPrices(collection).reduce; -// }; -// // const getCollectionQuantity = useCallback(() => { -// // return ( -// // selectedCollection?.cards?.reduce( -// // (acc, card) => acc + card.quantity, -// // 0 -// // ) || 0 -// // ); -// // }, [selectedCollection]); - -// // getCardPrice(selectedCollection); -// // console.log( -// // 'SELECTED COLLECTION CARD PRICE:', -// // getCardPrice( -// // selectedCollection?.cards?.find((card) => card.id === collectionData.id) -// // ) -// // ); diff --git a/src/context/CollectionContext/fml.jsx b/src/context/CollectionContext/fml.jsx deleted file mode 100644 index 439993d..0000000 --- a/src/context/CollectionContext/fml.jsx +++ /dev/null @@ -1,1389 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ - -import React, { - createContext, - useState, - useEffect, - useCallback, - useMemo, - useContext, - useRef, -} from 'react'; -import { useCookies } from 'react-cookie'; -import { - filterOutDuplicateYValues, - handleCardAddition, - handleCardRemoval, - getUniqueFilteredXYValues, - calculateTotalFromAllCardPrices, - ensureNumber, - findCollectionIndex, - createApiUrl, - handleApiResponse, - BASE_API_URL, - fetchWrapper, - removeDuplicateCollections, - getTotalCost, - initialCollectionState, - getCardPrice, - defaultContextValue, - validateUserIdAndData, - createPayload, - logError, - determineHttpMethod, -} from './collectionUtility.jsx'; -import { useCombinedContext } from '../CombinedProvider.jsx'; -import { useUserContext } from '../UserContext/UserContext.js'; -import moment from 'moment'; -import CustomLogger from '../CutstomLogger.jsx'; -import { - createNewDataSet, - getCollectionId, - // updateCardInCollection, -} from './cardHelpers.jsx'; - -export const CollectionContext = createContext(defaultContextValue); - -const constructPayloadWithDifferences = (existingData, newData) => { - const payload = {}; - let logContent = - '[constructPayloadWithDifferences] -----> Differences found in collection data:\n'; - - Object.keys(newData).forEach((key) => { - if (newData[key] !== existingData[key]) { - payload[key] = newData[key]; - - logContent += ` - Field "${key}":\n`; - logContent += ` Old Value: ${JSON.stringify(existingData[key])}\n`; - logContent += ` New Value: ${JSON.stringify(newData[key])}\n`; - } - }); - - if (Object.keys(payload).length > 0) { - console.log('Payload with differences:', payload); - } else { - console.log('No differences found between existing and new data.'); - } - - return payload; -}; - -const updateCardInCollection = (cards, cardToUpdate) => { - // Validate that cards is an array - if (!Array.isArray(cards)) { - throw new TypeError('The first argument must be an array of cards.'); - } - - // Validate that cardToUpdate is an object - if (typeof cardToUpdate !== 'object' || cardToUpdate === null) { - throw new TypeError('The card to update must be an object.'); - } - - // Validate that cardToUpdate has an id property - if (!('id' in cardToUpdate)) { - throw new Error('The card to update must have an "id" property.'); - } - - try { - // Attempt to update the card within the collection - const updatedCards = cards.map((card) => - card.id === cardToUpdate.id ? { ...card, ...cardToUpdate } : card - ); - return updatedCards; - } catch (error) { - // Handle any other errors that occur during the map operation - throw new Error(`Failed to update card in collection: ${error.message}`); - } -}; - -export const CollectionProvider = ({ children }) => { - // const { cardPrices } = useCombinedContext(); - const [cookies] = useCookies(['user']); - const [collectionData, setCollectionData] = useState(initialCollectionState); - const [collection, setCollection] = useState({ - cards: [], - allCardPrices: [], - }); - - const [officialCollectionDatasets, setOfficialCollectionDatasets] = useState([ - {}, - ]); - const [allCollections, setAllCollections] = useState([]); - const [allCardPrices, setAllCardPrices] = useState([]); - const [totalPrice, setTotalPrice] = useState(0); - const [xyData, setXyData] = useState([]); - const [ - updatedPricesFromCombinedContext, - setUpdatedPricesFromCombinedContext, - ] = useState({}); - // const [selectedCollection, setSelectedCollection] = useState({}); - const [openChooseCollectionDialog, setOpenChooseCollectionDialog] = - useState(false); - const userId = cookies.user?.id; - const totalCost = useMemo( - () => getTotalCost(selectedCollection), - [selectedCollection] - ); - const [selectedCollection, setSelectedCollection] = useState( - initialCollectionState - ); - const lastFetchedTime = useRef(null); - const calculateTotalPrice = (collection) => - collection.cards.reduce( - (total, card) => total + (card.price || 0) * (card.quantity || 0), - 0 - ); - - // Consolidated state setters for collection - const setCollectionState = (newState) => { - setSelectedCollection(newState); - setAllCollections((prev) => - prev.map((c) => (c._id === newState._id ? newState : c)) - ); - }; - - const createUpdateInfo = ( - updatedCards, - updatedPrice, - cardInfo, - userId, - selectedCollection, - collectionId, - newDataSet, - xyData - ) => { - // const { updateCollectionChartData } = useCollectionStore(); - return { - ...cardInfo, - name: selectedCollection?.name, - description: selectedCollection?.description, - cards: updatedCards, - userId: userId, - totalCost: updatedPrice, - totalPrice: updatedPrice, - xys: xyData, - quantity: updatedCards.length, - totalQuantity: updatedCards, - // chartData: updateCollectionChartData( - // updatedPrice, - // selectedCollection, - // newDataSet - // ), - allCardPrices: updatedCards?.flatMap((card) => - Array(card.quantity).fill(card.card_prices?.[0]?.tcgplayer_price) - ), - _id: collectionId, - }; - }; - - const saveCollectionToApi = async (collection, isUpdate = false) => { - const method = isUpdate ? 'PUT' : 'POST'; - const endpoint = isUpdate ? `/${collection._id}` : ''; - const url = `/api/users/collections${endpoint}`; - - try { - const response = await fetchWrapper(url, method, collection); - console.log( - `Collection ${isUpdate ? 'updated' : 'created'}:`, - response.data - ); - return response.data; // Return the updated data from the server - } catch (error) { - console.error( - `Failed to ${isUpdate ? 'update' : 'create'} collection:`, - error - ); - } - }; - - const createUserCollection = async ( - userId, - newCollectionInfo, - name, - description - ) => { - if (!userId) { - console.warn('Invalid userId to createUserCollection.'); - return; - } - if (!name) { - console.warn('Invalid name to createUserCollection.'); - return; - } - const actionDescription = 'create a new collection'; - if (!validateUserIdAndData(userId, newCollectionInfo, actionDescription)) - return; - - const payload = createPayload( - { name, description, userId }, - newCollectionInfo - ); - // const url = createApiUrl(`${userId}/collections`); - // console.log('URL:', url); - try { - // const response = await fetchWrapper(url, 'POST', payload); - const response = await saveCollectionToApi(payload, false); // Pass false to indicate it's not an update - - if (response?.error) { - console.error('Failed to create the collection:', response.error); - return; - } - if (response?.data) { - console.log('RESPONSE:', response); - setAllCollections((prev) => [...prev, response.data]); - setSelectedCollection(response.data); - setCollectionData(response.data); - } - return response; - } catch (error) { - logError('createUserCollection', { actionDescription, error }); - } - }; - - const removeCollection = async (collection) => { - if (!collection._id) { - console.error('Collection ID is undefined.'); - return; - } - - try { - const url = createApiUrl(`${userId}/collections`); - const response = await fetchWrapper(url, 'DELETE'); - - if (response.error) { - console.error('Failed to delete the collection:', response.error); - return; - } - - setAllCollections((prev) => - prev.filter((item) => item._id !== collection._id) - ); - - if (selectedCollection._id === collection._id) { - setSelectedCollection(initialCollectionState); - setCollectionData(initialCollectionState); - } - } catch (error) { - console.error(`Failed to delete the collection: ${error.message}`); - } - }; - - const fetchCollections = useCallback(async (userId) => { - if (!userId) { - console.warn('userId is not set, aborting fetchCollections.'); - return null; - } - const url = createApiUrl(`${userId}/collections`); - console.log('URL:', url); - try { - console.log(`Debug: ${'GET'} ${url}`); - const response = await fetchWrapper(url, 'GET'); - console.log('[GET RESPONSE]:', response); - const collections = response?.data; - console.log('Fetched collections:', collections); - return collections; - } catch (error) { - console.error('Error fetching collections:', error); - logError('fetchCollections', 'fetch collections', error); - return null; - } - }, []); - - const setCollections = useCallback((collections) => { - if (!collections || !Array.isArray(collections)) { - console.warn('Invalid collections array:', collections); - return; - } - - const uniqueCollections = removeDuplicateCollections(collections); - const validCollections = uniqueCollections.filter(Boolean); - - validCollections.forEach((collection) => { - collection.totalPrice = calculateTotalFromAllCardPrices( - collection.allCardPrices - ); - }); - - setAllCollections(validCollections); - - setCollectionData( - validCollections.length === 0 - ? initialCollectionState - : validCollections[0] - ); - setSelectedCollection( - validCollections.length === 0 - ? initialCollectionState - : validCollections[0] - ); - }, []); - - const fetchAndSetCollections = useCallback(async () => { - // Throttle the fetch calls - const currentTime = Date.now(); - const fetchDelay = 60000; // 1 minute - if ( - lastFetchedTime.current && - currentTime - lastFetchedTime.current < fetchDelay - ) - return; - lastFetchedTime.current = currentTime; - - try { - const response = await fetchWrapper( - createApiUrl(`collections/${userId}`), - 'GET' - ); - - console.log('FETCHED COLLECTIONS:', response); - setAllCollections(response.data || []); - } catch (error) { - console.error(`Failed to fetch collections: ${error}`); - } - }, [userId]); - // Function to update chart datasets and avoid duplicate entries - - // Function to calculate the total price from all card prices - const calculateTotalFromCardPrices = (cards) => - cards.reduce((total, card) => total + card.price * card.quantity, 0); - - // Function to update the card details - const updateCardDetails = (card, newPrice) => { - const priceHasChanged = - !card.priceHistory.length || - card.priceHistory.slice(-1)[0].num !== newPrice; - return { - ...card, - price: newPrice, - totalPrice: newPrice * card.quantity, - latestPrice: createPriceHistoryEntry(newPrice), - lastSavedPrice: card.latestPrice || createPriceHistoryEntry(newPrice), - chart_datasets: updateChartDatasets( - card, - newPrice * card.quantity, - new Date().toISOString() - ), - quantity: card.quantity, - priceHistory: priceHasChanged - ? [...card.priceHistory, createPriceHistoryEntry(newPrice)] - : card.priceHistory, - }; - }; - - // Function to add a card to the selected collection - // Add card to the selected collection - const createPriceHistoryEntry = (price) => ({ - num: price, - timestamp: new Date().toISOString(), - }); - - const updateChartDatasets = (card, computedPrice) => { - const newDataset = { x: new Date().toISOString(), y: computedPrice }; - const datasets = card.chart_datasets || []; - // Assuming a utility function is in place to filter out duplicates - return [...datasets, newDataset]; // For simplicity, this example does not filter out duplicates - }; - - const updateCardPriceDetails = (card, newPrice) => { - const priceChanged = - !card.priceHistory.length || - card.priceHistory.slice(-1)[0].num !== newPrice; - return { - ...card, - price: newPrice, - totalPrice: newPrice * (card.quantity || 1), // Default to 1 if quantity is not set - latestPrice: createPriceHistoryEntry(newPrice), - lastSavedPrice: priceChanged ? card.latestPrice : card.lastSavedPrice, // Update lastSavedPrice only if price changed - priceHistory: priceChanged - ? [...card.priceHistory, createPriceHistoryEntry(newPrice)] - : card.priceHistory, - chart_datasets: updateChartDatasets( - card, - newPrice * (card.quantity || 1) - ), - }; - }; - // const addOrRemoveCard = useCallback( -// async (card, cardInfo, operation) => { -// const collectionId = getCollectionId(selectedCollection, allCollections); -// if (!collectionId) { -// console.error('No valid collection selected.'); -// setOpenChooseCollectionDialog(true); -// return; -// } - -// let updatedCards = -// operation === 'update' -// ? updateCardInCollection(selectedCollection.cards, card) -// : getUpdatedCards(selectedCollection, card, operation); - -// const updatedPrice = calculateTotalFromAllCardPrices(updatedCards); -// const accumulatedTotal = selectedCollection.totalPrice + updatedPrice; - -// const newXYValue = { -// label: `Update - ${new Date().toISOString()}`, -// x: new Date().toISOString(), -// y: accumulatedTotal, -// }; - -// // Update the chart data with the new accumulated total -// const updatedChartData = { -// ...selectedCollection.chartData, -// allXYValues: [...selectedCollection.chartData.allXYValues, newXYValue], -// }; - -// const updateInfo = createUpdateInfo( -// updatedCards, -// accumulatedTotal, -// cardInfo, -// userId, -// selectedCollection, -// collectionId, -// updatedChartData, -// xyData -// ); -// console.log('[UPDATE INFO]:', updateInfo); -// console.log('[ACCUMULATED]:', accumulatedTotal); -// const updatedCollection = { -// ...selectedCollection, -// ...updateInfo, -// chartData: updatedChartData, -// // totalPrice: accumulatedTotal, -// collectionPriceHistory: [ -// ...(selectedCollection.collectionPriceHistory || []), -// createPriceHistoryObject(accumulatedTotal), -// ], -// }; - -// console.log('[COLLECTION DATA][B4UPDATE]:', updatedCollection); -// await updateActiveCollection(updatedCollection, updateInfo.chartData); -// updateCollectionData(updatedCollection, 'selectedCollection'); -// updateCollectionData(updatedCollection, 'collectionData'); -// }, -// [ -// selectedCollection, -// allCollections, -// userId, -// openChooseCollectionDialog, -// handleCardAddition, -// handleCardRemoval, -// updateCollectionData, -// setOpenChooseCollectionDialog, -// ] -// ); - - // Main functions - const addOrUpdateCardInCollection = (collection, card, newPrice) => { - // Find the card in the collection or create a new one - const existingCardIndex = collection.cards.findIndex( - (c) => c.id === card.id - ); - const updatedCard = updateCardPriceDetails(card, newPrice); - - let updatedCards = [...collection.cards]; - if (existingCardIndex >= 0) { - updatedCards[existingCardIndex] = updatedCard; // Update existing card - } else { - updatedCards = [...updatedCards, updatedCard]; // Add new card - } - - const updatedTotalPrice = updatedCards.reduce( - (acc, card) => acc + card.totalPrice, - 0 - ); - const updatedAllCardPrices = updatedCards.map((card) => card.price); - - return { - ...collection, - cards: updatedCards, - totalPrice: updatedTotalPrice, - allCardPrices: updatedAllCardPrices, - }; - }; - - // Function to save the collection to the backend - // const saveCollectionToApi = async (collection, method) => { - // try { - // const url = `/api/users/collections/${ - // method === 'POST' ? '' : collection._id - // }`; - // const response = await fetchWrapper(url, method, collection); - // console.log( - // `Collection ${method === 'POST' ? 'created' : 'updated'}:`, - // response.data - // ); - // } catch (error) { - // console.error( - // `Failed to ${method === 'POST' ? 'create' : 'update'} collection:`, - // error - // ); - // } - // }; - - const contextValue = useMemo( - () => ({ - allCollections, - selectedCollection, - collectionData, - officialCollectionDatasets, - totalCost, - totalPrice, - // processAndUpdateCardPrices: (cardPrices, newCardPricesData) => - // processAndUpdateCardPrices(cardPrices, newCardPricesData), - allCardPrices: selectedCollection?.allCardPrices || [], - xys: xyData || [], - openChooseCollectionDialog, - // collectionData, - // createUserCollection, - // removeCollection, - updatedPricesFromCombinedContext, - // addCard: (card) => addOrUpdateCard(card, 'add'), - // updateCardPrices: (cardUpdates) => - // cardUpdates.forEach((card) => addOrUpdateCard(card, 'update')), - setAllCardPrices: (cardPrices) => setAllCardPrices(cardPrices), - // updateCardPrices: (priceUpdates) => updateCardPrices(priceUpdates), - addCardToCollection: (card) => addOrUpdateCardInCollection(card), - setXyData: (newXyData) => setXyData(newXyData), - // updateCollection: (collection) => updateCollection(collection), - updateCardCollection: (collection, cardData, operation) => - updateCardInCollection(collection, cardData, operation), - setOfficialCollectionDatasets, - setUpdatedPricesFromCombinedContext: (updatedPrices) => { - setUpdatedPricesFromCombinedContext(updatedPrices); - }, - setOpenChooseCollectionDialog, - calculateTotalPrice: () => getCardPrice(selectedCollection), - getTotalCost: () => getTotalCost(selectedCollection), - getCardQuantity: (cardId) => { - const card = selectedCollection?.cards?.find((c) => c?.id === cardId); - return card?.quantity || 0; - }, - createUserCollection: (userId, newCollectionInfo) => - createUserCollection( - userId, - newCollectionInfo, - newCollectionInfo.name, - newCollectionInfo.description - ), - - removeCollection: (collection) => removeCollection(collection), - fetchAllCollectionsForUser: fetchAndSetCollections, - // addCardToCollection: (card) => addCardToCollection(card), - setSelectedCollection: (collectionId) => - setSelectedCollection(collectionId), - setAllCollections: (collections) => setAllCollections(collections), - addOneToCollection: (card) => addOrUpdateCardInCollection(card), - // removeOneFromCollection: (card) => addOrRemoveCard(card, null, 'remove'), - }), - [allCollections, selectedCollection, totalCost, totalPrice, xyData] - ); - - useEffect(() => { - // Log context values - console.log('CONTEXT:', contextValue); - console.log('TOTAL COST:', totalCost); - console.log('TOTAL PRICE:', totalPrice); - console.log('SELECTED COLLECTION:', selectedCollection); - console.log('ALL COLLECTIONS:', allCollections); - console.log('COLLECTION DATA:', collectionData); - console.log('OFFICIAL COLLECTION DATASETS:', officialCollectionDatasets); - console.log( - 'UPDATED PRICES FROM CONTEXT:', - updatedPricesFromCombinedContext - ); - console.log('ALL CARD PRICES:', selectedCollection?.allCardPrices); - console.log('XY DATA:', xyData); - }, [ - contextValue, - totalPrice, - selectedCollection, - updatedPricesFromCombinedContext, - xyData, - ]); - useEffect(() => { - console.log('UPDATED COLLECTION DATA POST SERVER:', collectionData); - }, [collectionData]); - - useEffect(() => { - if (userId) { - fetchAndSetCollections(); - } - }, [userId]); - - return ( - - {children} - - ); -}; - -export const useCollectionStore = () => { - const context = useContext(CollectionContext); - if (!context) { - throw new Error( - 'useCollectionStore must be used within a CollectionProvider' - ); - } - return context; -}; -// const updateActiveCollection = useCallback( -// async (collectionData) => { -// if (!collectionData) throw new Error('No collection data provided.'); - -// CustomLogger({ 'COLLECTION DATA': collectionData }); -// const isCreatingNew = !collectionData?._id; -// const actionDescription = isCreatingNew -// ? 'create a new collection' -// : 'update the existing collection'; -// const method = determineHttpMethod( -// isCreatingNew, -// collectionData?.endpoint -// ); -// CustomLogger({ 'METHOD:': method }); -// try { -// if (isCreatingNew) { -// console.log( -// `Skipping fetch call to ${method} since it's a new collection` -// ); -// } else { -// const endpoint = createApiUrl( -// `${userId}/collections/${collectionData?._id}` -// ); -// console.log('ENDPOINT:', endpoint); - -// const payload = constructPayloadWithDifferences( -// initialCollectionState, -// collectionData -// ); -// console.log('PAYLOAD:', collectionData); -// console.log(`Debug: ${method} ${endpoint}`); -// const response = await fetchWrapper(endpoint, method, payload); -// console.log('[UPDATE RESPONSE][RAW]:', response); -// const updatedCollection = response?.data; -// console.log('[UPDATE RESPONSE][DES]:', updatedCollection); - -// if (!updatedCollection) -// throw new Error('No collection returned from server.'); -// updateCollectionChartData(updatedCollection, existingChartData); -// // updateChartDataForCollection(updatedCollection, existingChartData); -// updateCollectionData(updatedCollection, 'selectedCollection'); -// updateCollectionData(updatedCollection, 'collectionData'); -// updateCollectionData(updatedCollection, 'allCollections'); -// } -// } catch (error) { -// console.error( -// `Failed to update collection for user ${userId} with collectionId ${collectionData._id}:`, -// error -// ); -// logError('updateActiveCollection', actionDescription, error); -// console.error(`Failed to ${actionDescription}: ${error.message}`); -// } -// }, -// [userId, updateCollectionData] -// ); -// Adds or updates a card in the collection - -// const handleUpdatePrices = useCallback( -// (priceUpdates) => { -// const timestamp = new Date().toISOString(); - -// setSelectedCollection((prevCollection) => { -// const updatedCards = prevCollection.cards.map((card) => { -// const update = priceUpdates.find((u) => u.id === card.id); -// if (update) { -// return { -// ...card, -// price: update.newPrice, -// latestPrice: { num: update.newPrice, timestamp }, -// lastSavedPrice: card.latestPrice, -// priceHistory: -// card.latestPrice.num !== update.newPrice -// ? [...card.priceHistory, { num: update.newPrice, timestamp }] -// : card.priceHistory, -// chart_datasets: updateChartDatasets( -// card, -// update.newPrice * card.quantity, -// timestamp -// ), -// }; -// } -// return card; -// }); - -// const newTotalPrice = calculateTotalFromCardPrices(updatedCards); - -// // Save the updated collection to the backend -// updateCardPrices({ -// ...prevCollection, -// cards: updatedCards, -// totalPrice: newTotalPrice, -// }); - -// return { -// ...prevCollection, -// cards: updatedCards, -// totalPrice: newTotalPrice, -// }; -// }); -// }, -// [updateCardPrices] -// ); - -// Transform the card data to fit the collection item structure -// const transformCardToCollectionItem = (card) => { -// return { -// id: card.id, -// name: card.name, -// price: card.price, -// quantity: 1, // Default to 1 for a new card, adjust as needed -// ... any other card properties needed -// }; -// }; -// const updateCardPriceHistory = (card, newPrice) => { -// if (card.priceHistory.slice(-1)[0]?.num !== newPrice) { -// return [...card.priceHistory, createPriceHistoryEntry(newPrice)]; -// } -// return card.priceHistory; -// }; -// // Refactored getUpdatedCards function -// const getUpdatedCards = (activeCollection, card, operation) => { -// const handleOperation = -// operation === 'add' ? handleCardAddition : handleCardRemoval; -// const cardsToUpdate = handleOperation(activeCollection?.cards, card); - -// return cardsToUpdate.map(updateCardDetails); -// }; - -// const updateCollectionData = useCallback( -// (newData, collectionType) => { -// switch (collectionType) { -// case 'allCollections': -// setAllCollections((prevCollections) => { -// const indexToUpdate = prevCollections.findIndex( -// (collection) => collection._id === newData._id -// ); -// if (indexToUpdate === -1) { -// return [...prevCollections, newData]; -// } else { -// const updatedCollections = [...prevCollections]; -// updatedCollections[indexToUpdate] = newData; -// return updatedCollections; -// } -// }); -// break; -// case 'selectedCollection': -// setSelectedCollection(newData); -// break; -// case 'collectionData': -// setCollectionData(newData); -// break; -// default: -// console.warn('Unknown collection type for update:', collectionType); -// } -// }, -// [setAllCollections, setSelectedCollection, setCollectionData] -// ); - -// // // Function to handle the update of card price and price history -// // const updateCardDetails = (card) => { -// // const cardPrice = card.card_prices?.[0]?.tcgplayer_price; -// // const computedPrice = cardPrice * card.quantity; - -// // card.chart_datasets = updateChartDatasets(card, computedPrice); -// // card.price = cardPrice; -// // card.totalPrice = computedPrice; - -// // const lastEntry = card.priceHistory?.slice(-1)?.[0]; -// // if (!lastEntry || lastEntry.num !== cardPrice) { -// // card.priceHistory = [ -// // ...(card.priceHistory || []), -// // createPriceHistoryObject(cardPrice), -// // ]; -// // } - -// // return card; -// // }; - -// // Update the allCardPrices array based on the new card's price -// const updateAllCardPrices = (card) => { -// return [...selectedCollection.allCardPrices, card.price]; -// }; -// // Update the collection's cards array with the new card -// const updatePriceHistory = (card, newPrice) => { -// const lastPriceEntry = card.priceHistory?.slice(-1)?.[0]; -// return lastPriceEntry && lastPriceEntry.num !== newPrice -// ? [ -// ...card.priceHistory, -// { num: newPrice, timestamp: new Date().toISOString() }, -// ] -// : card.priceHistory; -// }; - -// Only add a new entry if the price has changed -// Refactored addOrRemoveCard function -// const addOrRemoveCard = useCallback( -// async (card, cardInfo, operation) => { -// const collectionId = selectedCollection._id; -// if (!collectionId) { -// console.error('No valid collection selected.'); -// setOpenChooseCollectionDialog(true); -// return; -// } - -// const updatedCards = -// operation === 'update' -// ? updateCardInCollection(selectedCollection.cards, card) -// : getUpdatedCards(selectedCollection, card, operation); - -// const updateInfo = createUpdateInfo(selectedCollection, updatedCards); -// const updatedCollection = await updateActiveCollection( -// selectedCollection._id, -// updateInfo -// ); - -// updateCollectionData(updatedCollection, 'selectedCollection'); -// }, -// [selectedCollection, updateCollectionData, setOpenChooseCollectionDialog] -// ); - -// Function to update the active collection on the backend -// const updateActiveCollection = async (collectionId, updateInfo) => { -// const url = createApiUrl(`${userId}/collections/${collectionId._id}`); - -// try { -// const response = await fetchWrapper(url, 'PUT', updateInfo); -// if (!response.data) -// throw new Error('No collection returned from server.'); -// return response.data; -// } catch (error) { -// console.error(`Failed to update collection: ${error}`); -// throw error; -// } -// }; - -// const getUpdatedCards = (activeCollection, card, operation) => { -// const cardsToUpdate = -// operation === 'add' -// ? handleCardAddition(activeCollection?.cards, card) -// : handleCardRemoval(activeCollection?.cards, card); - -// return cardsToUpdate.map((card) => { -// const cardPrice = card.card_prices?.[0]?.tcgplayer_price; -// const computedPrice = cardPrice * card.quantity; -// console.log('COMPUTED PRICE:', computedPrice); - -// // Update chart_datasets if necessary -// const allDatasets = [ -// ...(card?.chart_datasets || []), -// { x: moment().format('YYYY-MM-DD HH:mm'), y: computedPrice }, -// ]; -// card.chart_datasets = filterOutDuplicateYValues(allDatasets); - -// // Set card's price and total price -// card.price = cardPrice; -// card.totalPrice = computedPrice; - -// // Create a new price history object -// const newPriceHistoryObject = createPriceHistoryObject(cardPrice); - -// // Update priceHistory only if the last entry's num is different from the new price -// if ( -// !card.priceHistory || -// card.priceHistory.length === 0 || -// card.priceHistory[card.priceHistory.length - 1].num !== cardPrice -// ) { -// card.priceHistory = [ -// ...(card.priceHistory || []), -// newPriceHistoryObject, -// ]; -// } -// card.tag = card.tag || 'monitored'; -// return card; -// }); -// }; - -// const createPriceHistoryObject = (price) => ({ -// timestamp: new Date().toISOString(), -// num: price, -// }); - -// // The main function for adding or removing a card -// const addOrRemoveCard = useCallback( -// async (card, cardInfo, operation) => { -// const collectionId = getCollectionId(selectedCollection, allCollections); -// if (!collectionId) { -// console.error('No valid collection selected.'); -// setOpenChooseCollectionDialog(true); -// return; -// } - -// let updatedCards = -// operation === 'update' -// ? updateCardInCollection(selectedCollection.cards, card) -// : getUpdatedCards(selectedCollection, card, operation); - -// const updatedPrice = calculateTotalFromAllCardPrices(updatedCards); -// const accumulatedTotal = selectedCollection.totalPrice + updatedPrice; - -// const newXYValue = { -// label: `Update - ${new Date().toISOString()}`, -// x: new Date().toISOString(), -// y: accumulatedTotal, -// }; - -// // Update the chart data with the new accumulated total -// const updatedChartData = { -// ...selectedCollection.chartData, -// allXYValues: [...selectedCollection.chartData.allXYValues, newXYValue], -// }; - -// const updateInfo = createUpdateInfo( -// updatedCards, -// accumulatedTotal, -// cardInfo, -// userId, -// selectedCollection, -// collectionId, -// updatedChartData, -// xyData -// ); -// console.log('[UPDATE INFO]:', updateInfo); -// console.log('[ACCUMULATED]:', accumulatedTotal); -// const updatedCollection = { -// ...selectedCollection, -// ...updateInfo, -// chartData: updatedChartData, -// // totalPrice: accumulatedTotal, -// collectionPriceHistory: [ -// ...(selectedCollection.collectionPriceHistory || []), -// createPriceHistoryObject(accumulatedTotal), -// ], -// }; - -// console.log('[COLLECTION DATA][B4UPDATE]:', updatedCollection); -// await updateActiveCollection(updatedCollection, updateInfo.chartData); -// updateCollectionData(updatedCollection, 'selectedCollection'); -// updateCollectionData(updatedCollection, 'collectionData'); -// }, -// [ -// selectedCollection, -// allCollections, -// userId, -// openChooseCollectionDialog, -// handleCardAddition, -// handleCardRemoval, -// updateCollectionData, -// setOpenChooseCollectionDialog, -// ] -// ); - -// const updateCardCollection = async ( -// selectedCollection, -// priceData, -// operation -// ) => { -// if (operation !== 'update') { -// console.warn('Invalid operation:', operation); -// return; -// } - -// const updatedCards = priceData.data.data; -// let accumulatedTotal = selectedCollection.totalPrice || 0; -// let newXYValues = [...selectedCollection.chartData.allXYValues]; - -// updatedCards.forEach((card) => { -// // Only add to priceHistory if the num value is different from the last entry -// const lastPriceHistoryNum = card.priceHistory?.slice(-1)[0]?.num; -// if (card.latestPrice?.num !== lastPriceHistoryNum) { -// const newPriceHistoryEntry = { -// num: card.latestPrice?.num || 0, -// timestamp: new Date().toISOString(), -// }; -// card.priceHistory = [ -// ...(card.priceHistory || []), -// newPriceHistoryEntry, -// ]; -// } - -// card.lastSavedPrice = { -// num: card.lastSavedPrice?.num || card.latestPrice?.num || 0, -// timestamp: card.lastSavedPrice?.timestamp || new Date().toISOString(), -// }; - -// card.latestPrice = { -// num: card.latestPrice?.num || 0, -// timestamp: new Date().toISOString(), -// }; - -// card.tag = card.tag || 'monitored'; - -// // Calculate the new total price -// accumulatedTotal += card.latestPrice.num * card.quantity; - -// // Update the chart data -// newXYValues.push({ -// label: `Update - ${new Date().toISOString()}`, -// x: new Date().toISOString(), -// y: accumulatedTotal, -// }); -// }); - -// // Update the chart data with accumulated values -// const updatedChartData = { -// ...selectedCollection.chartData, -// allXYValues: newXYValues, -// }; - -// const updatedCollectionWithChartData = { -// ...selectedCollection, -// cards: updatedCards, -// totalPrice: accumulatedTotal, -// chartData: updatedChartData, -// }; - -// await updateActiveCollection(updatedCollectionWithChartData); -// return updatedCollectionWithChartData; -// }; - -// const updateCollectionChartData = ( -// collection, -// updatedPrice, -// updatedChartData -// ) => { -// const chartData = collection.chartData || { -// name: `Chart for Collection: ${collection.name}`, -// userId: collection.userId, -// datasets: [], -// allXYValues: [], -// xys: [], -// }; - -// const newXYValue = { -// label: `Update - ${new Date().toISOString()}`, -// x: new Date().toISOString(), -// y: updatedPrice, -// }; - -// // Update the allXYValues array with the new data point -// const allXYValues = [...collection.chartData.allXYValues, newXYValue]; - -// const uniqueFilteredXYValues = getUniqueFilteredXYValues(allXYValues); - -// return { -// ...collection, -// chartData: { -// ...collection.chartData, -// allXYValues: uniqueFilteredXYValues, -// }, -// totalPrice: updatedPrice, -// }; -// }; - -// Create a new XY value for the current update -// const newXYValue = { -// label: `Update ${new Date().toISOString()}`, -// x: new Date().toISOString(), -// y: updatedPrice, -// additionalPriceData: { -// priceChanged: updatedPrice !== collection.totalPrice, -// initialPrice: collection.totalPrice, -// updatedPrice: updatedPrice, -// priceDifference: updatedPrice - collection.totalPrice, -// priceChange: parseFloat( -// ((updatedPrice - collection.totalPrice) / collection.totalPrice) * 100 -// ).toFixed(2), -// }, -// }; - -// Update datasets if newDataSet is provided -// if (newDataSet && Array.isArray(newDataSet.data)) { -// newDataSet.data.forEach((dataset) => { -// if (dataset.xys && Array.isArray(dataset.xys)) { -// chartData.datasets.push(dataset); -// } -// }); -// } -// if (newDataSet && Array.isArray(newDataSet.data)) { -// newDataSet.data.forEach((dataset) => { -// if (dataset.xys && Array.isArray(dataset.xys)) { -// chartData.datasets.push(...dataset.xys); -// } -// }); -// } -// Return the updated collection with the new chartData -// const updateCardCollection = async ( -// selectedCollection, -// priceData, -// operation -// ) => { -// const priceArray = priceData.data.data; -// let updatedCards = selectedCollection.cards.map((card) => { -// const matchingNewCard = priceArray.find( -// (newCard) => newCard.id === card.id -// ); -// if (matchingNewCard) { -// const { latestPrice, priceHistory, name, quantity, tag, _id } = -// matchingNewCard; -// return { ...card, latestPrice, priceHistory, name, quantity, tag, _id }; -// } -// return card; -// }); - -// let newTotalPrice = updatedCards.reduce( -// (total, card) => total + (card.latestPrice?.num * card.quantity || 0), -// 0 -// ); - -// let newAllCardPrices = updatedCards.flatMap((card) => -// Array(card.quantity).fill(card.latestPrice?.num) -// ); - -// const newDataSet = { -// data: [ -// { -// xys: [ -// { -// label: `Update ${new Date().toISOString()}`, -// x: new Date().toISOString(), -// y: newTotalPrice, -// }, -// ], -// }, -// ], -// }; - -// const updatedCollectionWithChartData = updateCollectionChartData( -// selectedCollection, -// newTotalPrice, -// newDataSet -// ); - -// await updateActiveCollection(updatedCollectionWithChartData); - -// return updatedCollectionWithChartData; -// }; - -// function updateChartDataForCollection(collection, chartData) { -// if (!collection.chartData) { -// collection.chartData = { -// name: `Chart for Collection: ${collection.name}`, -// userId: collection.userId, -// datasets: [], -// allXYValues: [], -// }; -// } - -// if (chartData) { -// collection.chartData = { ...collection.chartData, ...chartData }; -// } - -// const newXYSData = getUniqueFilteredXYValues( -// collection.chartData.allXYValues -// ); -// const timestamp = new Date(); -// const newPrice = collection.totalPrice; -// const previousPrice = collection.previousDayTotalPrice || newPrice; -// const priceDifference = newPrice - previousPrice; -// const priceChange = previousPrice -// ? (priceDifference / previousPrice) * 100 -// : 0; - -// collection.chartData.allXYValues.push({ -// label: `Update ${timestamp.toISOString()}`, -// x: timestamp, -// y: newPrice, -// }); - -// const newDatasetEntry = { -// name: collection.name, -// data: newXYSData.map((xy) => ({ -// ...xy, -// additionalPriceData: { -// priceChanged: priceDifference !== 0, -// initialPrice: previousPrice, -// updatedPrice: newPrice, -// priceDifference: priceDifference, -// priceChange: parseFloat(priceChange.toFixed(2)), -// }, -// })), -// }; - -// collection.chartData.datasets.push(newDatasetEntry); - -// return collection; -// } -// useEffect(() => { -// if (!selectedCollection || typeof totalPrice !== 'number') return; - -// const updateNumber = -// selectedCollection.chartData?.datasets?.length + 1 || 1; -// const newDataSet = createNewDataSet(updateNumber, totalPrice); - -// const updatedChartData = updateCollectionChartData( -// selectedCollection, -// totalPrice, -// newDataSet -// ); - -// const updatedCollection = { -// ...selectedCollection, -// chartData: updatedChartData, -// totalPrice, -// allCardPrices, -// }; - -// updateActiveCollection(updatedCollection); -// }, [totalPrice, selectedCollection, allCardPrices]); - -// const createNewDataSet = (updateNumber, totalPrice) => ({ -// data: [ -// { -// xys: [ -// { -// label: `Update Number ${updateNumber}`, -// data: { x: new Date().toISOString(), y: totalPrice }, -// }, -// ], -// additionalPriceData: {}, // Additional data can be added here -// }, -// ], -// }); - -// Additional functions and context setup as necessary - -// const updateActiveCollection = useCallback( -// async (collectionData, existingChartData = {}) => { -// if (!collectionData) throw new Error('No collection data provided.'); - -// const isCreatingNew = !collectionData?._id; -// const actionDescription = isCreatingNew -// ? 'create a new collection' -// : 'update the existing collection'; -// const method = determineHttpMethod( -// isCreatingNew, -// collectionData?.endpoint -// ); - -// try { -// if (isCreatingNew) { -// console.log( -// `Skipping fetch call to ${method} since it's a new collection` -// ); -// updateChartDataForCollection(collectionData, existingChartData); -// } else { -// const endpoint = createApiUrl( -// `${userId}/collections/${collectionData._id}` -// ); -// console.log('ENDPOINT:', endpoint); - -// // Calculate the difference between the existing chart data and the new collection data -// const payload = constructPayloadWithDifferences( -// existingChartData, -// collectionData -// ); - -// console.log(`Debug: ${method} ${endpoint}`); -// const response = await fetchWrapper(endpoint, method, payload); -// console.log('RESPONSE:', response); -// const updatedCollection = response?.data; -// console.log('[COLLECTION DATA][A4UPDATE]:', updatedCollection); - -// if (!updatedCollection) -// throw new Error('No collection returned from server.'); - -// updateChartDataForCollection(updatedCollection, existingChartData); -// updateCollectionData(updatedCollection, 'selectedCollection'); -// updateCollectionData(updatedCollection, 'collectionData'); -// updateCollectionData(updatedCollection, 'allCollections'); -// } -// } catch (error) { -// console.error( -// `Failed to update collection for user ${userId} with collectionId ${collectionData._id}:`, -// error -// ); -// logError('updateActiveCollection', actionDescription, error); -// console.error(`Failed to ${actionDescription}: ${error.message}`); -// } -// }, -// [userId, updateCollectionData] -// ); - -// // Helper function to construct payload with only the differences -// function constructPayloadWithDifferences(existingData, newData) { -// const payload = {}; -// Object.keys(newData).forEach((key) => { -// if (newData[key] !== existingData[key]) { -// payload[key] = newData[key]; -// } -// }); -// return payload; -// } - -// function updateChartDataForCollection(collection, chartData) { -// if (!collection.chartData) { -// collection.chartData = { -// // Initialize with defaults if not present -// name: `Chart for Collection: ${collection.name}`, -// userId: collection.userId, -// datasets: [], -// allXYValues: [], -// }; -// } - -// // Merge in any additional chartData that was passed in -// if (chartData) { -// collection.chartData = { ...collection.chartData, ...chartData }; -// } - -// // Get new x and y values -// const newXYSData = getUniqueFilteredXYValues( -// collection.chartData.allXYValues -// ); - -// // Calculate new and previous price -// const timestamp = new Date(); -// const newPrice = collection.totalPrice; // Fixed the code to get the correct value -// const previousPrice = collection.previousDayTotalPrice || newPrice; // Assuming previousDayTotalPrice is a property -// const priceDifference = newPrice - previousPrice; -// const priceChange = previousPrice -// ? (priceDifference / previousPrice) * 100 -// : 0; - -// // Update the allXYValues field with the new data point -// collection.chartData.allXYValues.push({ -// label: `Update ${new Date().toISOString()}`, -// x: new Date(), -// y: newPrice, -// }); - -// const newDatasetEntry = { -// name: collection.name, // or some other way to name the dataset -// data: [ -// { -// xys: [ -// // newXYSData.map((xy) => ({ -// { -// label: `Update ${timestamp.toISOString()}`, -// data: { x: timestamp, y: newPrice }, -// }, -// // })), -// ], -// additionalPriceData: [ -// { -// priceChanged: priceDifference !== 0, -// initialPrice: previousPrice, -// updatedPrice: newPrice, -// priceDifference: priceDifference, -// priceChange: parseFloat(priceChange.toFixed(2)), -// }, -// ], -// }, -// ], -// }; - -// // Push the new Dataset to the datasets array -// collection.chartData.datasets.push(newDatasetEntry); - -// // Return the updated collection object -// return collection; -// } diff --git a/src/context/CollectionContext/fmljsx b/src/context/CollectionContext/fmljsx deleted file mode 100644 index 1587fa4..0000000 --- a/src/context/CollectionContext/fmljsx +++ /dev/null @@ -1 +0,0 @@ -isMountedisMountedisMountedisMountedisMounted \ No newline at end of file diff --git a/src/context/CollectionContext/index.js b/src/context/CollectionContext/index.js deleted file mode 100644 index b622760..0000000 --- a/src/context/CollectionContext/index.js +++ /dev/null @@ -1,453 +0,0 @@ -// import React, { -// createContext, -// useState, -// useEffect, -// useCallback, -// useContext, -// useMemo, -// } from 'react'; -// import { useCookies } from 'react-cookie'; -// import { fetchWrapper, removeDuplicateCollections } from '../Helpers'; -// import { -// initialCollectionState, -// useUserId, -// useFetchAndSetCollections, -// useAddOrRemoveCard, -// useMakeContextValue, -// calculateAndUpdateTotalPrice, -// } from './SomeHelper'; - -// export const CollectionContext = createContext(undefined); - -// // Custom Hook to manage a collection -// const useCollection = (initialCollection = {}) => { -// const [collection, setCollection] = useState(initialCollection); -// const updateCollection = useCallback( -// (newData) => setCollection((prev) => ({ ...prev, ...newData })), -// [] -// ); -// const totalPrice = useMemo( -// () => calculateAndUpdateTotalPrice(collection), -// [collection] -// ); - -// return { collection, updateCollection, totalPrice }; -// }; - -// export const CollectionProvider = ({ children }) => { -// // const [cookies] = useCookies(['userCookie']); -// // const [collectionData, setCollectionData] = useState(initCollectionState()); -// // const [allCollections, setAllCollections] = useState([]); -// // const [allCardPrices, setAllCardPrices] = useState([]); -// // const [prevTotalQuantity, setPrevTotalQuantity] = useState(0); -// // const [selectedCollection, setSelectedCollection] = useState( -// // initCollectionState() -// // ); -// // const userId = cookies.userCookie?.id; -// const [collectionData, setCollectionData] = useState(initialCollectionState); -// const { collection, updateCollection } = useCollection( -// initialCollectionState -// ); -// const [allCollections, setAllCollections] = useState([]); -// const [allCardPrices, setAllCardPrices] = useState([]); -// const [prevTotalQuantity, setPrevTotalQuantity] = useState(0); -// const [selectedCollection, setSelectedCollection] = useState( -// initialCollectionState -// ); - -// const userId = useUserId(); - -// const fetchAndSetCollections = useFetchAndSetCollections( -// userId, -// setAllCollections, -// setSelectedCollection, -// setCollectionData -// ); - -// const addOrRemoveCard = useAddOrRemoveCard( -// userId, -// setSelectedCollection, -// setAllCollections, -// setAllCardPrices, -// allCollections, -// allCardPrices -// ); - -// const totalCost = useMemo( -// () => calculateTotalPrice(selectedCollection?.cards), -// [selectedCollection] -// ); - -// const contextValue = useMakeContextValue( -// collectionData, -// allCollections, -// allCardPrices, -// selectedCollection, -// totalCost, -// setSelectedCollection, -// fetchAndSetCollections, -// addOrRemoveCard, -// userId -// ); - -// const { collection: collectionData, updateCollection: updateCollectionData } = -// useCollection(initialCollectionData); -// const { -// collection: selectedCollection, -// updateCollection: updateSelectedCollection, -// } = useCollection(initialSelectedCollection); - -// const fetchCollectionsForUser = useCallback(async () => { -// try { -// const url = `${process.env.REACT_APP_SERVER}/api/users/${userId}/collections`; -// return await fetchWrapper(url, 'GET'); -// } catch (error) { -// console.error(`Failed to fetch collections for user: ${error.message}`); -// return null; -// } -// }, [userId]); - -// const updateCollectionData = useCallback((newData, updaterFn) => { -// updaterFn((prevCollections) => { -// const existingIndex = prevCollections -// ? prevCollections.findIndex( -// (collection) => collection._id === newData._id -// ) -// : -1; - -// // Ensure that prevCollections is an array or initialize it as an empty array -// if (!Array.isArray(prevCollections)) { -// prevCollections = []; -// } - -// console.log('existingIndex:', existingIndex); -// console.log('prevCollections:', prevCollections); - -// if (existingIndex === -1) return [...prevCollections, newData]; -// const updatedCollections = [...prevCollections]; -// updatedCollections[existingIndex] = newData; -// return updatedCollections; -// }); -// }, []); - -// const fetchAndSetCollections = useCallback(async () => { -// try { -// const userCollections = await fetchCollectionsForUser(); -// if (userCollections && userCollections.length > 0) { -// const uniqueCollections = removeDuplicateCollections(userCollections); - -// // Initialize totalPrice for each collection based on its cards -// uniqueCollections.forEach((collection) => { -// collection.totalPrice = calculateAndUpdateTotalPrice(collection); -// }); - -// setAllCollections(uniqueCollections); -// setCollectionData(uniqueCollections[0]); -// setSelectedCollection(uniqueCollections[0]); -// } -// } catch (error) { -// console.error(`Failed to fetch collections: ${error.message}`); -// } -// }, [fetchCollectionsForUser]); - -// const formatCardData = (card) => ({ -// id: card.id, -// ...Object.fromEntries( -// [ -// 'name', -// 'type', -// 'frameType', -// 'description', -// 'card_images', -// 'archetype', -// 'atk', -// 'def', -// 'level', -// 'race', -// 'attribute', -// 'quantity', -// ].map((key) => [key, card[key] || null]) -// ), -// }); - -// const createUserCollection = async ( -// userId, -// name, -// description, -// newCollectionInfo -// ) => { -// try { -// const url = `${process.env.REACT_APP_SERVER}/api/users/${userId}/collections/newCollection/${userId}`; - -// const data = await fetchWrapper(url, 'POST', { -// name, -// description, -// userId, -// totalPrice: 0, -// allCardPrices: [], -// cards: [], // Initialize with an empty array of cards -// }); - -// console.log('NEW COLLECTION DATA:', data.savedCollection); -// // Add the new collection to allCollections -// setAllCollections((prevCollections) => [...prevCollections, data]); - -// setCollectionData(data); -// setSelectedCollection(data); -// } catch (error) { -// console.error(`Failed to create a new collection: ${error.message}`); -// } -// }; - -// const getCardQuantity = (collectionId) => { -// const collection = allCollections?.find( -// (item) => item._id === collectionId -// ); -// if (!collection) return 0; -// return collection.cards.reduce((acc, card) => acc + card.quantity, 0); -// }; - -// const addOrRemoveCard = useCallback( -// async (card, cardInfo, isAdding) => { -// if ( -// !selectedCollection._id && -// (!allCollections[0] || !allCollections[0]._id) -// ) { -// console.error('No valid collection to add or remove a card.'); -// return; -// } -// const activeCollection = selectedCollection._id -// ? selectedCollection -// : allCollections[0]; - -// // GETS THE PRICE OF THE CARD -// let cardPrice = 0; - -// if ( -// card.card_prices && -// card.card_prices.length > 0 && -// card.card_prices[0].tcgplayer_price -// ) { -// cardPrice = parseFloat(card.card_prices[0].tcgplayer_price); -// } - -// // Create a copy of the current state -// const currentCards = [...(activeCollection?.cards || [])]; -// let currentTotalPrice = activeCollection.totalPrice || 0; -// // Find the card index in the current state -// const cardIndex = currentCards.findIndex((item) => item.id === card.id); -// if (isAdding) { -// setAllCardPrices([...allCardPrices, { cardId: card.id, cardPrice }]); -// if (cardIndex !== -1) { -// currentCards[cardIndex].quantity += 1; -// currentCards[cardIndex].price += cardPrice; // Add card price -// } else { -// currentCards.push({ ...card, quantity: 1, price: cardPrice }); -// } -// currentTotalPrice += cardPrice; -// } else { -// setAllCardPrices((prevCardValues) => -// prevCardValues.filter((val) => val !== cardPrice) -// ); - -// if (cardIndex !== -1) { -// currentCards[cardIndex].quantity -= 1; -// currentCards[cardIndex].price -= cardPrice; // Subtract card price -// if (currentCards[cardIndex].quantity <= 0) { -// currentCards.splice(cardIndex, 1); -// } -// currentTotalPrice -= cardPrice; -// } -// } -// currentTotalPrice = calculateAndUpdateTotalPrice({ -// ...activeCollection, -// cards: currentCards, -// }); - -// if (cardInfo) { -// console.log('COLLECTION NAME CHANGE:', cardInfo.name); -// console.log('COLLECTION DESC CHANGE:', cardInfo.description); - -// activeCollection.name = cardInfo.name; -// activeCollection.description = cardInfo.description; -// } - -// // Update the collection's totalPrice -// activeCollection.totalPrice = currentTotalPrice; - -// console.log('activeCollection:', activeCollection); -// console.log('currentTotalPrice:', currentTotalPrice); -// const collectionId = activeCollection._id; -// console.log('currentCards:', currentCards); -// console.log('collectionId:', collectionId); -// try { -// // Replace this URL with your server's URL and route for updating the collection -// const url = `${process.env.REACT_APP_SERVER}/api/users/${userId}/collections/${collectionId}`; - -// // Send a PUT request to the server to update the collection -// const updatedCollection = await fetchWrapper(url, 'PUT', { -// cards: currentCards, -// name: cardInfo.name, -// description: cardInfo.description, -// quantity: getCardQuantity(collectionId), -// userId: userId, -// totalPrice: currentTotalPrice, -// }); - -// // Update the state based on the server's response -// setSelectedCollection({ -// ...updatedCollection, -// totalPrice: currentTotalPrice, -// name: cardInfo.name, -// description: cardInfo.description, -// }); -// updateCollectionData( -// { -// ...updatedCollection, -// totalPrice: currentTotalPrice, -// name: cardInfo.name, -// description: cardInfo.description, -// }, -// setAllCollections -// ); -// } catch (error) { -// // If the PUT request fails, log the error and revert to the previous state -// console.error(`Failed to update the collection: ${error.message}`); -// } -// }, -// [ -// selectedCollection, -// allCollections, -// userId, -// updateCollectionData, -// allCardPrices, -// ] -// ); - -// const totalCost = selectedCollection?.cards -// ? selectedCollection.cards.reduce((total, card) => { -// if ( -// card.card_prices && -// card.card_prices[0] && -// card.card_prices[0].tcgplayer_price -// ) { -// return total + parseFloat(card.card_prices[0].tcgplayer_price); -// } -// return total; -// }, 0) -// : 0; - -// // const getCardQuantity: (collectionId) => { - -// // const collection = allCollections?.find( -// // (item) => item._id === collectionId -// // ); -// // if (!collection) return 0; -// // return collection.cards.reduce((acc, card) => acc + card.quantity, 0); -// // }, - -// const contextValue = useMemo( -// () => ({ -// collectionData, -// allCollections, -// allCardPrices, -// selectedCollection, -// totalPrice: calculateAndUpdateTotalPrice(selectedCollection), -// setSelectedCollection, -// fetchAllCollectionsForUser: fetchAndSetCollections, // fetchAndSetCollections should be defined -// addOneToCollection: (card, cardInfo) => -// addOrRemoveCard(card, cardInfo, true), // addOrRemoveCard should be defined -// removeOneFromCollection: (card) => addOrRemoveCard(card, null, false), -// createUserCollection: (name, description, newCollectionInfo) => -// createUserCollection(userId, name, description, newCollectionInfo), // createUserCollection should be defined -// getCardQuantity, -// getTotalCost: (collectionId) => { -// const collection = allCollections?.find( -// (item) => item._id === collectionId -// ); -// if (!collection) return 0; -// return collection.cards.reduce( -// (acc, card) => acc + card.price * card.quantity, -// 0 -// ); -// }, -// getCollectionCardDetails: (collectionId) => { -// const collection = allCollections?.find( -// (item) => item._id === collectionId -// ); -// if (!collection || !collection.cards) return [0, []]; - -// const totalQuantity = collection.cards.reduce( -// (acc, card) => acc + card.quantity, -// 0 -// ); -// const cardDetails = collection.cards.map((card) => ({ -// name: card.name, -// quantity: card.quantity, -// })); - -// return [totalQuantity, cardDetails]; -// }, -// }), -// [collectionData, allCollections, selectedCollection, allCardPrices] -// ); - -// useEffect(() => { -// console.log('collectionData has been updated:', collectionData); -// }, [collectionData]); - -// useEffect(() => { -// if (allCollections.length > 0) { -// const firstCollection = allCollections[0]; -// const totalQuantity = firstCollection.cards.reduce( -// (acc, card) => acc + card.quantity, -// 0 -// ); - -// // Only log when a new card has been added -// if (totalQuantity > prevTotalQuantity) { -// const cardDetails = firstCollection.cards.map((card) => ({ -// name: card.name, -// quantity: card.quantity, -// })); -// console.log( -// 'A new card has been added. Updated totals:', -// totalQuantity, -// cardDetails -// ); -// } - -// // Update the previous total quantity -// setPrevTotalQuantity(totalQuantity); -// } -// }, [allCollections]); - -// useEffect(() => { -// console.log('COLLECTIONCONTEXT:', contextValue); -// if (userId) fetchAndSetCollections(); -// }, [fetchAndSetCollections, userId]); - -// return ( -// -// {children} -// -// ); -// }; - -// export const useCollectionStore = () => { -// const context = useContext(CollectionContext); -// if (!context) { -// throw new Error( -// 'useCollectionStore must be used within a CollectionProvider' -// ); -// } -// return context; -// }; - -// // Initial states -// const initialCollectionState = { -// _id: '', -// cards: [], -// quantity: 0, -// totalPrice: 0, -// }; diff --git a/src/context/CollectionContext/validateCollection.jsx b/src/context/CollectionContext/validateCollection.jsx deleted file mode 100644 index 4e1b10c..0000000 --- a/src/context/CollectionContext/validateCollection.jsx +++ /dev/null @@ -1,68 +0,0 @@ -function validateCollection(collection) { - const warnings = []; - - // Utility to check if a value matches the type - function checkType(value, type, fieldName) { - if (typeof value !== type) { - warnings.push( - `Warning: Field "${fieldName}" should be of type "${type}".` - ); - } - } - - if ( - !collection.userId || - !mongoose.Types.ObjectId.isValid(collection.userId) - ) { - warnings.push('Warning: "userId" is missing or invalid.'); - } - if (typeof collection.name !== 'string') { - warnings.push('Warning: "name" is missing or not a string.'); - } - // ... continue with other fields - - // For nested objects like priceEntrySchema, you would have to check each field - if (collection.latestPrice) { - if (typeof collection.latestPrice.num !== 'number') { - warnings.push('Warning: "latestPrice.num" should be a number.'); - } - if (!(collection.latestPrice.timestamp instanceof Date)) { - warnings.push('Warning: "latestPrice.timestamp" should be a Date.'); - } - } - - // For arrays, you would check each element - if (Array.isArray(collection.priceHistory)) { - collection.priceHistory.forEach((entry, index) => { - if (typeof entry.num !== 'number') { - warnings.push( - `Warning: "priceHistory[${index}].num" should be a number.` - ); - } - if (!(entry.timestamp instanceof Date)) { - warnings.push( - `Warning: "priceHistory[${index}].timestamp" should be a Date.` - ); - } - }); - } - - // ... repeat for each field and nested object/array as needed - - if (warnings.length > 0) { - console.warn('Validation warnings:', warnings.join('\n')); - } else { - console.log('No validation warnings. The object is valid.'); - } - - return warnings.length === 0; -} - -// Example usage: -const collectionToValidate = { - userId: '507f191e810c19729de860ea', // This should be a valid ObjectId - name: 'My Collection', - // ... other fields -}; - -validateCollection(collectionToValidate); diff --git a/src/context/ColorModeProvider.jsx b/src/context/ColorModeProvider.jsx index d5b1a19..9084db9 100644 --- a/src/context/ColorModeProvider.jsx +++ b/src/context/ColorModeProvider.jsx @@ -1,12 +1,12 @@ import { useState, useMemo, createContext, useEffect } from 'react'; import { createTheme } from '@mui/material/styles'; import { useCookies } from 'react-cookie'; -import { themeSettings } from '../themeSettings'; +import { themeSettings } from '../assets/themeSettings'; export const ColorModeContext = createContext({ mode: 'dark', colorMode: {}, - theme: themeSettings('dark'), // default theme is light mode theme + theme: createTheme(themeSettings('dark')), // Default theme is dark mode // eslint-disable-next-line @typescript-eslint/no-empty-function toggleColorMode: () => {}, @@ -21,18 +21,17 @@ export const ColorModeProvider = ({ children }) => { useEffect(() => { // Set the cookie whenever the mode changes setCookie('colorMode', mode, { path: '/' }); - }, [mode]); + }, [mode, setCookie]); const colorMode = useMemo( () => ({ toggleColorMode: () => { - const newMode = mode === 'light' ? 'dark' : 'light'; + const newMode = mode === 'dark' ? 'dark' : 'light'; setMode(newMode); setCookie('colorMode', newMode); // also set the cookie here for immediate effect - // Cookies.set('colorMode', newMode, { expires: 365 }); // also set the cookie here for immediate effect }, }), - [mode] + [mode, setCookie] ); const theme = useMemo(() => createTheme(themeSettings(mode)), [mode]); diff --git a/src/context/CombinedProvider.jsx b/src/context/CombinedProvider.jsx index f7fa7d1..56fea34 100644 --- a/src/context/CombinedProvider.jsx +++ b/src/context/CombinedProvider.jsx @@ -9,18 +9,16 @@ import React, { import { useCookies } from 'react-cookie'; import { CollectionContext } from './CollectionContext/CollectionContext'; import { useSocketContext } from './SocketProvider'; -import CustomLogger from './CutstomLogger'; - +import { validateData } from './Helpers'; export const CombinedContext = createContext(); const initialState = { allData: {}, data: {}, messageTest: {}, - // chartData: {}, + userData: {}, existingChartData: {}, collectionData: {}, - // currentChartData: {}, allCollectionsUpdated: {}, simData: {}, allCollectionData: {}, @@ -33,7 +31,6 @@ const initialState = { dailyPriceChange: 0, priceDifference: 0, allCardPrices: {}, - handleCardPricesUpdated: {}, retrievedListOfMonitoredCards: {}, listOfMonitoredCards: {}, listOfSimulatedCards: {}, @@ -43,147 +40,19 @@ const initialState = { error: null, isDelaying: false, // Added isDelaying to initialState as it is referred in your code isCronJobTriggered: false, // Added isCronJobTriggered to initialState as it is referred in your code - // allCollectionsUpdated: {}, - // cardStats: {}, - // deckData: {}, - // updatedChartData: {}, - // allUpdatedPrices: {}, - // allItemTypeData: {}, - // allItemData: {}, - // allItemData2: {}, - // cardStatsArray: {}, -}; -const filterDuplicatePrices = (data) => { - const seen = {}; - - return data?.runs?.filter((run) => { - if ( - !run?.valuesUpdated || - !run?.valuesUpdated?.updatedPrices || - Object.keys(run?.valuesUpdated?.updatedPrices)?.length === 0 - ) { - // Remove the item if there's no updatedPrices or it's empty - return false; - } - - let isDuplicate = false; - for (const id in run?.valuesUpdated?.updatedPrices) { - const item = run?.valuesUpdated?.updatedPrices[id]; - const key = `${id}-${item?.previousPrice}-${item?.updatedPrice}`; - - if (seen[key]) { - isDuplicate = true; - break; - } - seen[key] = true; - } - - return !isDuplicate; - }); -}; - -// function processCardPrices(cardPrices, selectedCollection) { -// if (!cardPrices || !cardPrices.data || !Array.isArray(cardPrices.data.data)) { -// console.error('Invalid cardPrices data structure.'); -// return; -// } - -// console.log('Card prices retrieved:', cardPrices); -// const priceArray = []; -// let totalPrice = 0; - -// cardPrices.data.data.forEach((card) => { -// const { latestPrice, quantity } = card; - -// if (!latestPrice || !quantity) { -// console.error(`Missing price or quantity for card ID: ${card.id}`); -// return; -// } - -// for (let i = 0; i < quantity; i++) { -// priceArray.push(latestPrice.num); -// totalPrice += latestPrice.num; -// } -// }); - -// const filteredCards = cardPrices.data.data.filter((card) => { -// const cardIds = selectedCollection?.cards?.map((card) => card.id); -// return cardIds?.includes(card.id); -// }); - -// console.log('Price Array:', priceArray); -// console.log('Total Price:', totalPrice.toFixed(2)); -// // console.log('Filtered Cards:', filteredCards); - -// // Save priceArray and totalPrice as needed -// // For example, you might want to set them to your application's state - -// return { priceArray, totalPrice: totalPrice.toFixed(2) }; -// } - -const isEmpty = (obj) => { - return ( - [Object, Array].includes((obj || {}).constructor) && - !Object.entries(obj || {}).length - ); -}; - -const validateData = (data, eventName, functionName) => { - const dataType = typeof data || data.data || data.data.data || data.message; - console.log( - '----------------------------------------------------------------------------------------------------' - ); - console.log( - `| [SUCCESS] Received data of type: ${dataType} in ${functionName} triggered by event: ${eventName}] |` - ); - console.log( - '----------------------------------------------------------------------------------------------------' - ); - if (data === null || data === undefined) { - console.log( - '----------------------------------------------------------------------------------------------------' - ); - console.warn( - `[Warning] Received null or undefined data in ${functionName} triggered by event: ${eventName}` - ); - console.log( - '----------------------------------------------------------------------------------------------------' - ); - return false; - } - if (isEmpty(data) && isEmpty(data?.data) && isEmpty(data?.data?.data)) { - console.log( - '----------------------------------------------------------------------------------------------------' - ); - console.error( - `[Error] Received empty data object or array in ${functionName} triggered by event: ${eventName}` - ); - console.log( - '----------------------------------------------------------------------------------------------------' - ); - return false; - } - return true; }; export const CombinedProvider = ({ children }) => { - const { user } = useCookies(['user']); + const [cookies] = useCookies(['user']); + // const { user } = useCookies(['user']); const [state, setState] = useState(initialState); - const userId = user?.userId; + const user = cookies.user; + // console.log('USER ID:', user.id); const { selectedCollection, allCollections, getNewTotalPrice, - updateOneFromCollection, - updateCollection, - processAndUpdateCardPrices, - setAllCardPrices, - setTotalPrice, - setAllCollections, - updatedPricesFromCombinedContext, - officialCollectionDatasets, - setOfficialCollectionDatasets, - setUpdatedPricesFromCombinedContext, + getUpdatedCollection, } = useContext(CollectionContext); const socket = useSocketContext(); @@ -209,6 +78,7 @@ export const CombinedProvider = ({ children }) => { const setDataFunctions = { data: createStateUpdaterFunction('chartData'), + userData: createStateUpdaterFunction('userData'), messageTest: createStateUpdaterFunction('messageTest'), finalUpdateData: createStateUpdaterFunction('finalUpdateData'), chartData: createStateUpdaterFunction('chartData'), @@ -258,23 +128,12 @@ export const CombinedProvider = ({ children }) => { useEffect(() => { if (state.eventsTriggered) { console.log(`Handling event: ${state.eventsTriggered.eventName}`); - // Additional logic to handle the event } }, [state.eventsTriggered]); // ----------- XXX ----------- const generateListOfMonitoredCards = (allCollections) => { if (!allCollections) return []; - // // Ensure cardPrices is an array - // const cardPrices = Array.isArray(state.cardPrices) ? state.cardPrices : []; - - // // Flatten all cards from all collections, including collection ID - // const cardsWithCollectionId = allCollections.flatMap((collection) => - // collection?.cards?.map((card) => ({ - // ...card, - // collectionId: collection._id, - // })) - // ); const cardsWithCollectionId = allCollections.flatMap((collection) => collection?.cards?.map((card) => ({ ...card, @@ -282,18 +141,26 @@ export const CombinedProvider = ({ children }) => { })) ); - const uniqueCardIds = new Set(cardsWithCollectionId.map((card) => card.id)); + const uniqueCardIds = new Set( + cardsWithCollectionId.map((card) => card?.id) + ); return Array.from(uniqueCardIds).map((id) => { - const originalCard = cardsWithCollectionId.find((card) => card.id === id); + const originalCard = cardsWithCollectionId.find( + (card) => card?.id === id + ); return { ...originalCard, - priceHistory: originalCard.priceHistory || [], + priceHistory: originalCard?.priceHistory || [], }; }); }; const updateCardPricesInList = (listOfMonitoredCards, cardPrices) => { + // if (!data || !Array.isArray(data)) { + // console.error('Data is undefined or not an array'); + // return; // or handle the error appropriately + // } return listOfMonitoredCards.map((originalCard) => { const updatedCardInfo = cardPrices.find((price) => price.id === originalCard.id) || {}; @@ -303,16 +170,17 @@ export const CombinedProvider = ({ children }) => { return { ...originalCard, ...updatedCardInfo, - quantity: originalCard.quantity, - price: updatedCardInfo.latestPrice?.num || originalCard.price, - lastSavedPrice: - updatedCardInfo.lastSavedPrice || - updatedCardInfo.priceHistory[ - updatedCardInfo.priceHistory.length - 1 - ], + quantity: originalCard?.quantity, + price: updatedCardInfo?.latestPrice?.num || originalCard?.price, + lastSavedPrice: { + num: updatedCardInfo?.lastSavedPrice?.num || originalCard?.price, + timestamp: + updatedCardInfo?.latestPrice?.timestamp || + new Date().toISOString(), + }, priceHistory: [ ...originalCard.priceHistory, - updatedCardInfo.latestPrice, + updatedCardInfo?.latestPrice, ], }; } @@ -325,283 +193,14 @@ export const CombinedProvider = ({ children }) => { [allCollections] ); - // const listOfMonitoredCards = useMemo(() => { - // if (!allCollections) return []; - - // const cardPrices = Array.isArray(state.cardPrices) - // ? state.cardPrices - // : [state.cardPrices]; - - // const cardsWithCollectionId = allCollections.flatMap((collection) => - // collection?.cards?.map((card) => ({ - // ...card, - // collectionId: collection._id, - // })) - // ); - - // const uniqueCardIds = new Set(cardsWithCollectionId.map((card) => card.id)); - - // return Array.from(uniqueCardIds).map((id) => { - // const originalCard = cardsWithCollectionId.find((card) => card.id === id); - // const updatedCardInfo = cardPrices.find((price) => price.id === id) || {}; - - // const latestPrice = - // updatedCardInfo.latestPrice || originalCard.latestPrice; - // const lastSavedPrice = - // updatedCardInfo.lastSavedPrice || originalCard.lastSavedPrice; - - // return { - // ...originalCard, - // ...updatedCardInfo, - // latestPrice, - // lastSavedPrice, - // price: latestPrice?.num || originalCard.price, - // priceHistory: updatedCardInfo.priceHistory || originalCard.priceHistory, - // }; - // }); - // }, [allCollections, state.cardPrices]); - - // const updateCardPricesState = (currentCardPrices, updatedCardsList) => { - // // Ensure both currentCardPrices and updatedCardsList are arrays - // if (!Array.isArray(currentCardPrices)) currentCardPrices = []; - // if (!Array.isArray(updatedCardsList)) updatedCardsList = []; - - // // Create a map for easy lookup of current card prices by ID - // const currentCardPricesMap = new Map( - // currentCardPrices.map((card) => [card.id, card]) - // ); - - // // Update the card prices with new data from updatedCardsList - // return updatedCardsList.map((updatedCard) => { - // const currentCardPrice = currentCardPricesMap.get(updatedCard.id) || {}; - - // return { - // ...currentCardPrice, - // latestPrice: updatedCard.latestPrice || currentCardPrice.latestPrice, - // lastSavedPrice: - // updatedCard.lastSavedPrice || currentCardPrice.lastSavedPrice, - // price: updatedCard.latestPrice?.num || currentCardPrice.price, - // priceHistory: updatedCard.priceHistory || currentCardPrice.priceHistory, - // }; - // }); - // }; - const emitUpdatedCards = (socket, updatedCards) => { socket.emit('UPDATED_MONITORED_CARDS', updatedCards); }; - // Usage - // Now you can set the state with newCardPrices - - // const listOfMonitoredCards = useMemo(() => { - // if (!allCollections) return []; - - // const cardPrices = Array.isArray(state.cardPrices) ? state.cardPrices : []; - - // // Flatten all cards from all collections with additional collection ID - // const cardsWithCollectionId = allCollections.flatMap((collection) => - // collection?.cards?.map((card) => ({ - // ...card, - // collectionId: collection._id, - // })) - // ); - - // // Reduce the cards to a map, merging cards with the same ID - // const mergedCardsMap = cardsWithCollectionId.reduce((acc, card) => { - // const updatedCardInfo = cardPrices.find((price) => price.id === card.id) || {}; - // const existingCard = acc.get(card.id) || {}; - - // const mergedCard = { - // ...existingCard, - // ...card, - // ...updatedCardInfo, - // latestPrice: updatedCardInfo.latestPrice || card.latestPrice || existingCard.latestPrice, - // lastSavedPrice: updatedCardInfo.lastSavedPrice || card.lastSavedPrice || existingCard.lastSavedPrice, - // price: updatedCardInfo.latestPrice?.num || card.price || existingCard.price, - // }; - - // acc.set(card.id, mergedCard); - // return acc; - // }, new Map()); - - // // Convert the map values to an array - // return Array.from(mergedCardsMap.values()); - // }, [allCollections, state.cardPrices]); - - // const listOfMonitoredCards = useMemo(() => { - // if (!allCollections) return []; - - // // Ensure cardPrices is an array - // const cardPrices = Array.isArray(state.cardPrices) ? state.cardPrices : []; - - // // Flatten all cards from all collections, including collection ID - // const cardsWithCollectionId = allCollections.flatMap((collection) => - // collection?.cards?.map((card) => ({ - // ...card, - // collectionId: collection._id, - // })) - // ); - - // // Create a unique set of card IDs - // const uniqueCardIds = new Set(cardsWithCollectionId.map((card) => card.id)); - - // // Map over unique card IDs to find corresponding card and update with new prices if available - // return cardsWithCollectionId.map((card) => { - // const updatedCardInfo = - // cardPrices.find((price) => price.id === card.id) || {}; - - // return { - // ...card, - // latestPrice: updatedCardInfo?.latestPrice || card?.latestPrice, - // lastSavedPrice: updatedCardInfo?.lastSavedPrice || card?.lastSavedPrice, - // price: updatedCardInfo?.latestPrice?.num || card.price, // Assuming you want to update the price field as well - // _id: updatedCardInfo?._id || card?._id, - // id: updatedCardInfo?.id || card?.id, - // collectionId: updatedCardInfo?.collectionId || card?.collectionId, - // tag: updatedCardInfo?.tag || card?.tag, - // name: updatedCardInfo?.name || card?.name, - // quantity: updatedCardInfo?.quantity || card?.quantity, - // priceHistory: updatedCardInfo?.priceHistory || card?.priceHistory, - // // __v: updatedCardInfo?.__v || originalCard.__v, - // // _id: card?._id, - // // id: card?.id, - // // collectionId: card?.collectionId, // Include collection ID in the returned object - // // tag: 'monitored', - // // name: card?.name, - // // quantity: card?.quantity, - // // price: card?.price, - // // latestPrice: { - // // num: updatedLatestPrice, - // // timestamp: card?.latestPrice?.timestamp - // // ? card?.latestPrice?.timestamp - // // : new Date().toISOString(), - // // }, - // // lastSavedPrice: { - // // num: updatedLastSavedPrice, - // // timestamp: card?.lastSavedPrice?.timestamp - // // ? card?.lastSavedPrice?.timestamp - // // : new Date().toISOString(), - // // }, - // // priceHistory: card?.priceHistory, - // // __v: card?.__v, - // }; - // }); - // }, [allCollections, state.cardPrices]); - - // ----------- SOCKET EVENT HANDLERS ----------- - - // const safeEmit = useCallback( - // (event, data) => { - // try { - // if (!validateData(data, event, 'safeEmit')) { - // throw new Error(`Invalid data emitted for event: ${event}`); - // } - // if (socket) { - // socket.emit(event, data); - // console.log(`[Info] Emitted event: ${event}`); - // } else { - // console.warn('Socket is not connected. Cannot emit event:', event); - // } - // } catch (error) { - // console.error(`[Error] Failed to emit event: ${event}`, error); - // setDataFunctions.error({ - // message: error.message, - // source: 'safeEmit', - // }); - // } - // }, - // [socket] - // ); - - // const safeOn = useCallback( - // (event, handler) => { - // const wrapper = (data) => { - // try { - // if (!validateData(data, event, handler.name)) { - // throw new Error(`Invalid data received for event: ${event}`); - // } // Add this line to validate the data received - // // console.log(`[Info] Handling event: ${event}`); - // handler(data); - // } catch (error) { - // console.error(`[Error] Failed to handle event: ${event}`, error); - // setDataFunctions.error({ message: error.message, source: event }); - // } - // }; - - // socket.on(event, wrapper); // Add this line to register the event listener - - // return () => { - // socket.off(event, wrapper); // Add this line to unregister the event listener when the component unmounts - // }; - // }, - // [socket] - // ); - - // const mergeUpdates = (currentArray, updates) => { - // const updatedArray = [...currentArray]; - // updates.forEach((update) => { - // const index = updatedArray.findIndex((item) => item.id === update.id); - // if (index !== -1) { - // updatedArray[index] = { ...updatedArray[index], ...update }; - // } else { - // updatedArray.push(update); - // } - // }); - // return updatedArray; - // }; - - // const handleStatusUpdateCharts = (newData) => { - // const { updates } = newData.data; - // console.log('[STATUS_UPDATE_CHARTS] Data:', updates); - // const updatedList = mergeUpdates(state.listOfSimulatedCards, updates); - // setDataFunctions.listOfSimulatedCards(updatedList); - // }; const handleStatusUpdateCharts = async (newData) => { console.log('[STATUS_UPDATE_CHARTS] Data:', newData); - console.log('Card prices retrieved:', newData); - // const processedPrices = processCardPrices(newData, selectedCollection); - console.log('Card prices updated:', newData); - - // Filter out cards which are not in the selected collection - // const filteredCards = newData.data.data.filter((card) => { - // const cardIds = selectedCollection?.cards?.map((card) => card.id); - // return cardIds?.includes(card.id); - // }); - - // Merge the selectedCollection cards with the filteredCards by adding the latestPrice, lastSavedPrice and priceHistory - // selectedCollection should receive: price: latestPrice.num, priceHistory: [...priceHistory, latestPrice.num], lastSavedPrice: latestPrice - // monitoredCards should then be updated with the new values of quantity from the card data in selectedCollection - // const updatedAllCardPrices = processedPrices.priceArray; - // const updatedTotalPrice = processedPrices.totalPrice; - // const filteredCards = processedPrices.filteredCards; - // console.log('********** [--------------] **********'); - // console.log('********** [FILTERED CARDS] **********', filteredCards); - - // console.log('********** [--------------] **********'); - // console.log('********** [UPDATED PRICES] **********', updatedAllCardPrices); - - // console.log('********** [--------------] **********'); - // console.log('********** [UPDATED TOTAL] **********', updatedTotalPrice); - - // console.log('********** [--------------] **********'); - - // Start with the current collection's state let updatedCollection = { ...selectedCollection }; - // setTotalPrice(updatedTotalPrice); - // setAllCardPrices(updatedAllCardPrices); - // console.log('Updated collection in combined:', updatedCollection); - // Iterate over the array of processed prices - // for (const card of filteredCards) { - // updatedCollection = await updateOneFromCollection(card); - // } - - // return updatedCollection; - // const updatedCollection = await updateCollection({ - // ...selectedCollection, - // cards: filteredCards, - // }); - console.log('Updated collection in combined:', updatedCollection); return updatedCollection; }; @@ -628,7 +227,10 @@ export const CombinedProvider = ({ children }) => { const handleStatusUpdateCron = (newData) => { const { message, data } = newData; - console.log('[STATUS_UPDATE_CRON]', message, data); + // console.log('[STATUS_UPDATE_CRON]', message, data); + if (!Array.isArray(data) || !data.data || data.data.length === 0) { + return null; + } setDataFunctions.data(data); }; @@ -664,19 +266,10 @@ export const CombinedProvider = ({ children }) => { setDataFunctions.collectionData(collectionData); }; - // const handleExistingChartData = (chartData) => { - // console.log('Existing chart data:', chartData); - // setDataFunctions.existingChartData(chartData); - // }; - - // const handleChartDatasetsUpdated = (chartUpdate) => { - // console.log('Chart datasets updated:', chartUpdate); - // setDataFunctions.currentChartData(chartUpdate); - // }; - - const handleCardPricesUpdated = (priceData) => { + const handleCardPricesUpdated = async (priceData) => { console.log('Card prices retrieved:', priceData); - // Update listOfMonitoredCards based on the updated card prices + const updatedCardPrices = priceData.data.data; + const userId = user?.id; const currentListOfMonitoredCards = generateListOfMonitoredCards(allCollections); console.log( @@ -685,24 +278,6 @@ export const CombinedProvider = ({ children }) => { )}] | `, currentListOfMonitoredCards ); - const updatedCardPrices = priceData.data.data; - // updatedCardPrices.forEach( - // ( - // card // Update the price of each card in the listOfMonitoredCards - // ) => { - // card.price = currentListOfMonitoredCards.price || card.price; - // } - // ); - - setDataFunctions.cardPrices(updatedCardPrices); - // console.log( - // `[updatedCardPrices: $${getNewTotalPrice(updatedCardPrices)}] | `, - // updatedCardPrices - // ); - // console.log( - // `[state.cardPrices.data: $${getNewTotalPrice(state.cardPrices.data)}] | `, - // state.cardPrices.data - // ); const updatedListOfMonitoredCards = updateCardPricesInList( currentListOfMonitoredCards, updatedCardPrices @@ -714,22 +289,39 @@ export const CombinedProvider = ({ children }) => { updatedListOfMonitoredCards ); - // Now update the listOfMonitoredCards in your state - setDataFunctions.allCardPrices(updatedListOfMonitoredCards); - const updatedCollectionResult = updateCollection( - selectedCollection, - null, // since we're not updating a specific card - 'update', // Assuming 'update' is the operation when prices change - userId + // Update the selectedCollection with new card prices + const updatedSelectedCollectionCards = selectedCollection.cards.map( + (card) => { + const updatedCardPrice = updatedListOfMonitoredCards.find( + (updatedCard) => updatedCard.id === card.id + ); + return updatedCardPrice ? { ...card, ...updatedCardPrice } : card; + } ); - if (updatedCollectionResult) { - // Do something with the updated collection - console.log('Updated Collection:', updatedCollectionResult); - // Update your state/context or perform additional actions + const updatedCollection = { + ...selectedCollection, + cards: updatedSelectedCollectionCards, + }; + + try { + const updatedCollectionResult = await getUpdatedCollection( + updatedCollection, + null, // No specific card to update + 'update', // Operation type + userId + ); + + if (updatedCollectionResult) { + console.log('Updated Collection:', updatedCollectionResult); + // setDataFunctions.collectionData(updatedCollectionResult); + setDataFunctions.listOfSimulatedCards(updatedCollectionResult); + } + } catch (error) { + console.error('Failed to update collection:', error); } - // Additional processing if required - // ... + + setDataFunctions.allCardPrices(updatedListOfMonitoredCards); }; const handleNoPricesChanged = () => { @@ -809,7 +401,7 @@ export const CombinedProvider = ({ children }) => { socket.on(event, handler); }); - validateData(eventHandlers, 'eventHandlers', 'useEffect'); + // validateData(eventHandlers, 'eventHandlers', 'useEffect'); return () => { eventHandlers.forEach((_, event) => { socket.off(event); @@ -828,49 +420,20 @@ export const CombinedProvider = ({ children }) => { ); return; } - socket.emit('REQUEST_EXISTING_COLLECTION_DATA', { + socket?.emit('REQUEST_EXISTING_COLLECTION_DATA', { userId, data: selectedCollection, }); }, - chart: (userId, selectedCollection) => { - if (!userId) - return console.error('Missing userId for chart data request.'); - if (!selectedCollection) - return console.error( - 'Missing selectedCollection for chart data request.' - ); - if (selectedCollection.chartData === undefined || null) { - if (selectedCollection.chartData === undefined) - console.log('chartData is undefined'); - if (selectedCollection.chartData === null) - console.log('chartData is null'); - return console.error( - 'The selected collections chart data is missing, null or undefined.' - ); - } - - console.log( - 'Attempting to retrieve chart data', - selectedCollection?.chartData - ); - const chartData = selectedCollection?.chartData || {}; - socket.emit('REQUEST_EXISTING_CHART_DATA', { - data: { - userId, - data: chartData, - }, - }); - }, }, sendAction: { message: (message) => { if (!message) return console.error('Message content is missing.'); - socket.emit('MESSAGE_FROM_CLIENT', { message, data: message }); + socket?.emit('MESSAGE_FROM_CLIENT', { message, data: message }); }, stopCronJob: (userId) => { if (!userId) return console.error('Missing userId for cron job stop.'); - socket.emit('REQUEST_CRON_STOP', { userId }); + socket?.emit('REQUEST_CRON_STOP', { userId }); }, checkAndUpdateCardPrices: ( userId, @@ -887,7 +450,7 @@ export const CombinedProvider = ({ children }) => { listOfMonitoredCards ); const selectedList = listOfMonitoredCards; - socket.emit('REQUEST_CRON_UPDATED_CARDS_IN_COLLECTION', { + socket?.emit('REQUEST_CRON_UPDATED_CARDS_IN_COLLECTION', { userId, data: { selectedList, @@ -905,9 +468,6 @@ export const CombinedProvider = ({ children }) => { if (!listOfMonitoredCards) return console.log('Missing retrievedListOfMonitoredCards.'); if (!allCollections) return console.log('Missing allCollections.'); - // if (!cardsWithChangedPrice) - // return console.log('Missing cardsWithChangedPrice.'); - const selectedList = listOfMonitoredCards; socket.emit('REQUEST_PRICES_ACTIVATE_CRON', { userId, @@ -935,78 +495,32 @@ export const CombinedProvider = ({ children }) => { const confirm = (message) => window.confirm(message); - useEffect(() => { - if (userId && selectedCollection) { - handleSocketInteraction.requestData.collection( - userId, - selectedCollection - ); - handleSocketInteraction.requestData.chart(userId, selectedCollection); - handleSocketInteraction.sendAction.message('Hello from client!'); - // handleSocketInteraction.sendAction.updateCollection(); - // handleSocketInteraction.sendAction.updateChart(); - // handleSocketInteraction.sendAction.checkAndUpdateCardPrices( - // userId, - // listOfMonitoredCards - // // retrieveListOfMonitoredCards() - // ); - } - }, [userId, selectedCollection, socket]); useEffect(() => { // Update the collectionData state when selectedCollection changes setDataFunctions.collectionData(selectedCollection); }, [selectedCollection]); - // useEffect(() => { - // if (state.allCardPrices) { - // console.log('ALL PRICE DATA', state.allCardPrices); - // // const oldTotal = getNewTotalPrice(state.cardPrices); - // const oldTotal2 = getNewTotalPrice(listOfMonitoredCards); - - // console.log('OLD TOTAL', oldTotal2); - // if ( - // JSON.stringify(state.allCardPrices) !== JSON.stringify(state.cardPrices) - // ) { - // console.log('SETTING SELECTED COLLECTION'); - // const newTotal = getNewTotalPrice(state.allCardPrices); - // console.log('NEW TOTAL COMBINED', newTotal); - // setAllCollections(state.collectionData); - // } - // } - // }, [state.allCardPrices]); - useEffect(() => { if (allCollections) { - // console.log('allCollections', allCollections); - // console.log('listOfMonitoredCards', listOfMonitoredCards); - - console.log('ALLL', allCollections); + // console.log('ALLL', allCollections); if ( JSON.stringify(allCollections) !== JSON.stringify(state.allCollectionData) ) { setDataFunctions.allCollectionData(allCollections); } - - // setDataFunctions.retrievedListOfMonitoredCards( - // retrieveListOfMonitoredCards() - // ); } }, [allCollections]); + useEffect(() => { + if (user) { + // console.log('userId', user.userId); + setDataFunctions.userData(user); + } + }, [user]); const logError = (message) => console.error(message); // ----------- CONTEXT VALUE ----------- - // useEffect(() => { - // if (listOfMonitoredCards.length === 0) - // console.log('listOfMonitoredCards', listOfMonitoredCards); - - // setDataFunctions.listOfMonitoredCards({ listOfMonitoredCards }); - // // handleSocketInteraction.sendAction.checkAndUpdateCardPrices( - // // userId, - // // listOfMonitoredCards - // // ); - // }, [listOfMonitoredCards, allCollections]); const value = useMemo( () => ({ diff --git a/src/context/CronJobContext/CronJobContext.jsx b/src/context/CronJobContext/CronJobContext.jsx new file mode 100644 index 0000000..d28b60e --- /dev/null +++ b/src/context/CronJobContext/CronJobContext.jsx @@ -0,0 +1,52 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; +import { useUserContext } from '../UserContext/UserContext'; +import { useCollectionStore } from '../CollectionContext/CollectionContext'; +import { useCombinedContext } from '../CombinedProvider'; + +const CronJobContext = createContext(); + +export const useCronJobContext = () => useContext(CronJobContext); + +export const CronJobProvider = ({ children }) => { + const { user } = useUserContext(); + const { allCollections } = useCollectionStore(); + const { handleSendAllCardsInCollections } = useCombinedContext(); + const [lastCronJobTriggerTime, setLastCronJobTriggerTime] = useState( + new Date().getTime() + ); + + useEffect(() => { + const handleTriggerCronJob = () => { + const currentTime = new Date().getTime(); + const timeDifference = currentTime - lastCronJobTriggerTime; + + // Define your cron job logic here + // Example: Trigger an action if a certain time has elapsed + if (timeDifference >= 60000) { + // 60 seconds + setLastCronJobTriggerTime(currentTime); + if (user && user.userID) { + console.log('Triggering cron job actions'); + // Call your functions here + handleSendAllCardsInCollections(user.userID); + } + } + }; + + const interval = setInterval(handleTriggerCronJob, 1000); // Check every second + return () => clearInterval(interval); + }, [ + lastCronJobTriggerTime, + user, + allCollections, + handleSendAllCardsInCollections, + ]); + + return ( + + {children} + + ); +}; diff --git a/src/context/CutstomLogger.jsx b/src/context/CutstomLogger.jsx deleted file mode 100644 index fb746c0..0000000 --- a/src/context/CutstomLogger.jsx +++ /dev/null @@ -1,23 +0,0 @@ -let logCounter = 0; - -function CustomLogger(data) { - logCounter += 1; - const dataType = Array.isArray(data) ? 'Array' : typeof data; - let formattedData = ''; - - if (dataType === 'object') { - formattedData = JSON.stringify(data, null, 2); - } else if (dataType === 'Array') { - formattedData = data - .map((item) => JSON.stringify(item, null, 2)) - .join('\n'); - } else { - formattedData = data.toString(); - } - - console.log(`[LOG ${logCounter}] --- [Type: ${dataType}] -----------------`); - console.log(formattedData); - console.log(`[END OF LOG ${logCounter}] ---------------------------------`); -} - -export default CustomLogger; diff --git a/src/context/DeckContext/new.js b/src/context/DeckContext/new.js deleted file mode 100644 index 59d5dfc..0000000 --- a/src/context/DeckContext/new.js +++ /dev/null @@ -1,645 +0,0 @@ - // const FetchAllDeck = ({ setDeckData, deckData, setDeckOptions }) => { - // useEffect(() => { - - // return null; - // }; - - // const fetchUserDeck = useCallback( - // async (userId) => { - // setLoading(true); - // setError(null); - // try { - // const response = await fetch( - // `${process.env.REACT_APP_SERVER}/api/decks/userDeck/${userId}` - // ); - - // if (!response.ok) { - // if (response.status === 404) { - // return createUserDeck(userId); - // } else if (response.status === 500) { - // setError('An unexpected error occurred. Please try again later.'); - // } else { - // throw new Error(`HTTP error! status: ${response.status}`); - // } - // } else { - // const data = await response.json(); - // console.log('DECKLIST EXISTS:', data); - // setCookie('deck', Array.isArray(data.deck) ? data.deck : [], { - // path: '/', - // }); - // setDeckDataAndCookie( - // 'deck', - // Array.isArray(data.deck) ? data.deck : [], - // { - // path: '/', - // } - // ); - // setLoading(false); - // return data; - // } - - // // if (data && data.deck) { - // // // ...existing code to update deckData - // // // New code to update allDecks - // // const updatedAllDecks = allDecks.map((deck) => { - // // if (deck.creatorId === userId) { - // // return { ...deck, decks: data }; - // // } - // // return deck; - // // }); - // // setAllDecks(updatedAllDecks); - // // } - // } catch (error) { - // setError(error.message); - // setLoading(false); - // } - // }, - // [setCookie] - // ); - - // useEffect(() => { - // if (userId && typeof userId === 'string') { - // fetchUserDeck(userId).then((data) => { - // if (data && data.deck) { - // setDeckDataAndCookie(data); - // } else { - // console.error('Deck data was not retrieved for user', userId); - // } - // }); - // } - // }, [userId, fetchUserDeck]); - - import React, { - createContext, - useState, - useEffect, - useCallback, - useContext, - } from 'react'; - import { useCookies } from 'react-cookie'; - import { useCardStore } from '../CardContext/CardStore'; - - export const DeckContext = createContext(null); - - const apiBase = `${process.env.REACT_APP_SERVER}/api/decks`; - - const fetchWrapper = async (url, method, body = null) => { - const options = { - method, - headers: { 'Content-Type': 'application/json' }, - ...(body && { body: JSON.stringify(body) }), - }; - const response = await fetch(url, options); - if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); - return response.json(); - }; - - const removeDuplicateDecks = (decks) => { - const uniqueDecks = {}; - decks.forEach((deck) => (uniqueDecks[deck._id] = deck)); - return Object.values(uniqueDecks); - }; - - export const DeckProvider = ({ children }) => { - const { getCardData } = useCardStore(); - const [cookies, setCookie] = useCookies(['userCookie']); - const [deckData, setDeckData] = useState({}); - const [allDecks, setAllDecks] = useState([]); - const [selectedDeck, setSelectedDeck] = useState({}); - const userId = cookies.userCookie?.id; - - // Helper Functions - const setDeckDataAndCookie = useCallback( - (newDeckData) => { - setDeckData(newDeckData); - setCookie('deckData', newDeckData, { path: '/' }); - }, - [setCookie] - ); - - const filterUserDecks = useCallback( - (decks) => decks.filter((deck) => deck.userId === userId), - [userId] - ); - - const fetchAndSetDecks = useCallback(async () => { - const url = `${apiBase}/users/${userId}/decks`; - const fetchedDecks = await fetchWrapper(url, 'GET'); - const userDecks = filterUserDecks(fetchedDecks); - const uniqueDecks = removeDuplicateDecks(userDecks); - - setAllDecks((prevDecks) => - removeDuplicateDecks([...prevDecks, ...uniqueDecks]) - ); - setDeckDataAndCookie(uniqueDecks[0]); - }, [userId, setAllDecks, setDeckDataAndCookie, filterUserDecks]); - - useEffect(() => { - if (userId) fetchAndSetDecks(); - }, [userId, fetchAndSetDecks]); - - const fetchAllDecksForUser = useCallback( - async (userId) => { - // Fetch the initial set of decks - const initialDecks = await fetchAndSetDecks( - apiBase2, - userId, - setAllDecks, - setDeckDataAndCookie - ); - if (initialDecks === null) { - const shouldCreateDeck = window.confirm( - 'No decks found. Would you like to create a new one?' - ); - if (shouldCreateDeck) { - const deckName = prompt('Enter the deck name:'); - const deckDescription = prompt('Enter the deck description:'); - await createUserDeck(userId, { - name: deckName, - description: deckDescription, - }); - } - return; - } - - const fetchCount = initialDecks?.length || 0; - for (let i = 0; i < fetchCount; i++) { - await new Promise((res) => setTimeout(res, 1000)); - await fetchAndSetDecks( - apiBase1, - userId, - setAllDecks, - setDeckDataAndCookie - ); - } - }, - [setAllDecks, setDeckDataAndCookie] - ); - - const updateDeck = (newDeckData) => { - setDeckDataAndCookie(newDeckData); - setDeckData(newDeckData); - }; - - const updateAndSyncDeck = async (newDeckData) => { - updateDeck(newDeckData); - - setAllDecks((prevDecks) => { - const newAllDecks = prevDecks.map((deck) => - deck._id === newDeckData._id ? newDeckData : deck - ); - return prevDecks.some((deck) => deck._id === newDeckData._id) - ? newAllDecks - : [...newAllDecks, newDeckData]; - }); - - try { - const url = `${process.env.REACT_APP_SERVER}/api/users/${userId}/decks`; - await fetchWrapper( - url, - 'PUT', - formatDeckData(newDeckData._id, newDeckData.cards) - ); - } catch (error) { - console.error(`Failed to update deck in backend: ${error.message}`); - } - }; - - const formatDeckData = (deckId, updatedDeck) => { - return { - _id: deckId, // Changed "deckId" to "_id" to match the structure - userId: userId, - name: deckData.name, // assuming you have the name in your state - description: deckData.description, // assuming you have the description in your state - cards: updatedDeck.map((card) => formatCardData(card)), - }; - }; - - const formatCardData = (card) => ({ - id: card.id, - ...Object.fromEntries( - [ - 'name', - 'type', - 'frameType', - 'description', - 'card_images', - 'archetype', - 'atk', - 'def', - 'level', - 'race', - 'attribute', - 'quantity', - ].map((key) => [key, card[key] || null]) - ), - }); - - const addOrRemoveCard = async (card, isAdding) => { - try { - setDeckData((prevState) => { - const cardIndex = prevState.cards.findIndex( - (item) => item.id === card.id - ); - let newCards = [...prevState.cards]; - - if (cardIndex !== -1) { - newCards[cardIndex].quantity += isAdding ? 1 : -1; - if (newCards[cardIndex].quantity <= 0) { - newCards.splice(cardIndex, 1); - } - } else if (isAdding) { - newCards.push({ ...formatCardData(card), quantity: 1 }); - } - - const newDeckData = { ...prevState, cards: newCards }; - updateAndSyncDeck(newDeckData); - return newDeckData; - }); - } catch (error) { - console.error(`Failed to modify the deck: ${error.message}`); - } - }; - - const addOneToDeck = (card) => addOrRemoveCard(card, true); - const removeOneFromDeck = (cardId) => addOrRemoveCard({ id: cardId }, false); - - const createUserDeck = async (userId, newDeckInfo) => { - try { - const url = `${apiBase1}/newDeck/${userId}`; - const initialDeck = newDeckInfo.initialCard - ? [formatCardData(newDeckInfo.initialCard)] - : []; - const data = await fetchWrapper(url, 'POST', { - ...newDeckInfo, - cards: initialDeck, - userId, - }); - updateDeck(data); - } catch (error) { - console.error(`Failed to create a new deck: ${error.message}`); - } - }; - - // To get the quantity of a specific card in the deck - const getCardQuantity = (cardId) => { - // if (Array.isArray(deckData)) { - const card = deckData.cards?.find((item) => item.id === cardId); - // } - - return card?.quantity || 0; - }; - - const getTotalCost = () => - deckData.cards.reduce((acc, card) => acc + (card.cost || 0), 0); - - const value = { - deckData, - allDecks, - getCardQuantity, - addOneToDeck, - removeOneFromDeck, - getTotalCost, // Added - deleteFromDeck, - updateAndSyncDeck, // Added - selectedDeck, // Added - setSelectedDeck, // Added - fetchAllDecksForUser, - createUserDeck, - }; - - useEffect(() => { - console.log('DECKCONTEXT:', value); - const userId = cookies.userCookie?.id; - if (userId) { - fetchAllDecksForUser(userId); - } - }, [fetchAllDecksForUser, cookies.userCookie]); - - return {children}; - }; - - export const useDeckStore = () => { - const context = useContext(DeckContext); - if (!context) - throw new Error('useDeckStore must be used within a DeckProvider'); - return context; - }; - - // import React, { - // createContext, - // useState, - // useEffect, - // useCallback, - // useContext, - // } from 'react'; - // import { useCookies } from 'react-cookie'; - // import { useCardStore } from '../CardContext/CardStore'; - - // export const DeckContext = createContext(null); - - // const apiBase = `${process.env.REACT_APP_SERVER}/api/decks`; - - // const fetchWrapper = async (url, method, body = null) => { - // const options = { - // method, - // headers: { 'Content-Type': 'application/json' }, - // ...(body && { body: JSON.stringify(body) }), - // }; - // const response = await fetch(url, options); - // if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); - // return response.json(); - // }; - - // const removeDuplicateDecks = (decks) => { - // const uniqueDecks = {}; - // decks.forEach((deck) => (uniqueDecks[deck._id] = deck)); - // return Object.values(uniqueDecks); - // }; - - // export const DeckProvider = ({ children }) => { - // const { getCardData } = useCardStore(); - // const [cookies, setCookie] = useCookies(['userCookie']); - // const [deckData, setDeckData] = useState({}); - // const [allDecks, setAllDecks] = useState([]); - // const [selectedDeck, setSelectedDeck] = useState({}); - // const userId = cookies.userCookie?.id; - - // // Helper Functions - // const setDeckDataAndCookie = useCallback( - // (newDeckData) => { - // setDeckData(newDeckData); - // setCookie('deckData', newDeckData, { path: '/' }); - // }, - // [setCookie] - // ); - - // const filterUserDecks = useCallback( - // (decks) => decks.filter((deck) => deck.userId === userId), - // [userId] - // ); - - // const fetchAndSetDecks = useCallback(async () => { - // const url = `${apiBase}/users/${userId}/decks`; - // const fetchedDecks = await fetchWrapper(url, 'GET'); - // const userDecks = filterUserDecks(fetchedDecks); - // const uniqueDecks = removeDuplicateDecks(userDecks); - - // setAllDecks((prevDecks) => - // removeDuplicateDecks([...prevDecks, ...uniqueDecks]) - // ); - // setDeckDataAndCookie(uniqueDecks[0]); - // }, [userId, setAllDecks, setDeckDataAndCookie, filterUserDecks]); - - // useEffect(() => { - // if (userId) fetchAndSetDecks(); - // }, [userId, fetchAndSetDecks]); - - // const fetchAllDecksForUser = useCallback( - // async (userId) => { - // // Fetch the initial set of decks - // const initialDecks = await fetchAndSetDecks( - // apiBase2, - // userId, - // setAllDecks, - // setDeckDataAndCookie - // ); - // if (initialDecks === null) { - // const shouldCreateDeck = window.confirm( - // 'No decks found. Would you like to create a new one?' - // ); - // if (shouldCreateDeck) { - // const deckName = prompt('Enter the deck name:'); - // const deckDescription = prompt('Enter the deck description:'); - // await createUserDeck(userId, { - // name: deckName, - // description: deckDescription, - // }); - // } - // return; - // } - - // const fetchCount = initialDecks?.length || 0; - // for (let i = 0; i < fetchCount; i++) { - // await new Promise((res) => setTimeout(res, 1000)); - // await fetchAndSetDecks( - // apiBase1, - // userId, - // setAllDecks, - // setDeckDataAndCookie - // ); - // } - // }, - // [setAllDecks, setDeckDataAndCookie] - // ); - - // const updateAndSyncDeck = async (newDeckData) => { - // // Update the deck locally - // setDeckDataAndCookie(newDeckData); // Using the function to also set the cookie - - // // Update in the global allDecks state - // const existingDeck = allDecks.find((deck) => deck._id === newDeckData._id); - // if (existingDeck) { - // const newAllDecks = allDecks.map((deck) => - // deck._id === newDeckData._id ? newDeckData : deck - // ); - // setAllDecks(newAllDecks); - // } else { - // setAllDecks((prevDecks) => [...prevDecks, newDeckData]); - // } - - // // Sync with the backend - // try { - // // Corrected the way to include `deckId` in the URL - // const url = `${process.env.REACT_APP_SERVER}/api/users/${userId}/decks`; - // await fetchWrapper( - // url, - // 'PUT', - // formatDeckData(newDeckData._id, newDeckData.cards) - // ); - // } catch (error) { - // console.error(`Failed to update deck in backend: ${error.message}`); - // } - // }; - - // // To formatdeckId - // const formatDeckData = (deckId, updatedDeck) => { - // return { - // _id: deckId, // Changed "deckId" to "_id" to match the structure - // userId: userId, - // name: deckData.name, // assuming you have the name in your state - // description: deckData.description, // assuming you have the description in your state - // cards: updatedDeck.map((card) => formatCardData(card)), - // }; - // }; - - // // To format card data before sending to backend - // const formatCardData = (card) => { - // return { - // id: card.id, - // name: card.name || null, - // type: card.type || null, - // frameType: card.frameType || null, - // description: card.description || null, - // card_images: card.card_images || null, - // archetype: card.archetype || null, - // atk: card.atk || null, - // def: card.def || null, - // level: card.level || null, - // race: card.race || null, - // attribute: card.attribute || null, - // quantity: card.quantity || null, - // }; - // }; - - // // To get the quantity of a specific card in the deck - // const getCardQuantity = (cardId) => { - // // if (Array.isArray(deckData)) { - // const card = deckData.cards?.find((item) => item.id === cardId); - // // } - - // return card?.quantity || 0; - // }; - - // // To add one card to the deck - // // To add one card to the deck - // const addOneToDeck = async (card) => { - // try { - // setDeckData((prevState) => { - // if (!Array.isArray(prevState.cards)) { - // console.error('deckData.cards is not an array.'); - // return prevState; - // } - - // const existingCard = prevState.cards.find( - // (item) => item.id === card.id - // ); - // let newCards; - - // if (existingCard) { - // existingCard.quantity += 1; - // newCards = [...prevState.cards]; - // } else { - // newCards = [ - // ...prevState.cards, - // { - // ...formatCardData(card), - // quantity: 1, - // }, - // ]; - // } - - // const newDeckData = { ...prevState, cards: newCards }; - // // const newDeckData = { ...deckData }; // Take a copy of existing deckData - // updateAndSyncDeck(newDeckData); - - // // Update the cookie - // setCookie('deckData', newDeckData, { path: '/' }); - - // return newDeckData; - // }); - - // setAllDecks((prevState) => { - // const updatedAllDecks = prevState.map((deck) => { - // if (deck._id === deckData._id) { - // return { ...deck, cards: deckData.cards }; // Assuming deckData.cards is updated - // } - // return deck; - // }); - // return updatedAllDecks; - // }); - // } catch (error) { - // console.error(`Failed to add a card to the deck: ${error.message}`); - // } - // }; - - // const createUserDeck = async (userId, newDeckInfo) => { - // try { - // const { initialCard, ...rest } = newDeckInfo; - // const url = `${apiBase1}/newDeck/${userId}`; // Modified this line to use apiBase - - // const initialDeck = initialCard ? [formatCardData(initialCard)] : []; - - // const data = await fetchWrapper(url, 'POST', { - // ...rest, - // cards: initialDeck, - // userId, - // }); - - // setDeckData(data); - // updateAndSyncDeck(data); - // } catch (error) { - // console.error(`Failed to create a new deck: ${error.message}`); - // } - // }; - - // const removeOneFromDeck = async (cardId) => { - // try { - // const newDeckData = { ...deckData }; // Take a copy of existing deckData - // const cardToRemove = newDeckData.cards?.find( - // (item) => item.id === cardId - // ); - // if (!cardToRemove) return; - // cardToRemove.quantity -= 1; - - // if (cardToRemove.quantity === 0) { - // newDeckData.cards = newDeckData.cards.filter( - // (item) => item.id !== cardId - // ); - // } - - // // Update and sync the deck - // await updateAndSyncDeck(newDeckData); - // } catch (error) { - // console.error(`Failed to remove a card from the deck: ${error.message}`); - // } - // }; - - // // To delete a card entirely from the deck - // const deleteFromDeck = async (cardId) => { - // try { - // const newDeck = deckData.cards.filter((item) => item.id !== cardId); - // const updatedDeckData = await updateAndSyncDeck(deckData._id, newDeck); - // setDeckData(updatedDeckData); - // } catch (error) { - // console.error(`Failed to delete a card from the deck: ${error.message}`); - // } - // }; - - // const getTotalCost = () => { - // return deckData.cards.reduce((acc, card) => acc + (card.cost || 0), 0); - // }; - - // const value = { - // deckData, - // allDecks, - // getCardQuantity, - // addOneToDeck, - // removeOneFromDeck, - // getTotalCost, // Added - // deleteFromDeck, - // updateAndSyncDeck, // Added - // selectedDeck, // Added - // setSelectedDeck, // Added - // fetchAllDecksForUser, - // createUserDeck, - // }; - - // useEffect(() => { - // console.log('DECKCONTEXT:', value); - // const userId = cookies.userCookie?.id; - // if (userId) { - // fetchAllDecksForUser(userId); - // } - // }, [fetchAllDecksForUser, cookies.userCookie]); - - // return {children}; - // }; - - // export const useDeckStore = () => { - // const context = useContext(DeckContext); - // if (!context) - // throw new Error('useDeckStore must be used within a DeckProvider'); - // return context; - // }; - \ No newline at end of file diff --git a/src/context/Helpers.jsx b/src/context/Helpers.jsx index 35365bd..e71198e 100644 --- a/src/context/Helpers.jsx +++ b/src/context/Helpers.jsx @@ -5,7 +5,10 @@ import { useCookies } from 'react-cookie'; export const fetchWrapper = async (url, method, body = null) => { const options = { method, - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, }; if (body) { @@ -24,26 +27,6 @@ export const fetchWrapper = async (url, method, body = null) => { } }; -// const fetchWrapper = async (url, method, body = null) => { -// const options = { -// method, -// headers: { 'Content-Type': 'application/json' }, -// ...(body && { body: JSON.stringify(body) }), -// }; -// const response = await fetch(url, options); -// if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); -// return await response.json(); -// }; - -// const removeDuplicateCollections = (collections) => { -// const uniqueCollections = {}; -// collections.forEach((collection) => { -// uniqueCollections[collection._id] = collection; -// }); -// return Object.values(uniqueCollections); -// }; - -// Function to remove duplicate collections export const removeDuplicateCollections = (collections) => { const seen = new Set(); return collections.filter((collection) => { @@ -87,3 +70,48 @@ export const useUserId = () => { return userId; }; + +const isEmpty = (obj) => { + return ( + [Object, Array].includes((obj || {}).constructor) && + !Object.entries(obj || {}).length + ); +}; + +export const validateData = (data, eventName, functionName) => { + const dataType = typeof data || data.data || data.data.data || data.message; + console.log( + '----------------------------------------------------------------------------------------------------' + ); + console.log( + `| [SUCCESS] Received data of type: ${dataType} in ${functionName} triggered by event: ${eventName}] |` + ); + console.log( + '----------------------------------------------------------------------------------------------------' + ); + if (data === null || data === undefined) { + console.log( + '----------------------------------------------------------------------------------------------------' + ); + console.warn( + `[Warning] Received null or undefined data in ${functionName} triggered by event: ${eventName}` + ); + console.log( + '----------------------------------------------------------------------------------------------------' + ); + return false; + } + if (isEmpty(data) && isEmpty(data?.data) && isEmpty(data?.data?.data)) { + console.log( + '----------------------------------------------------------------------------------------------------' + ); + console.error( + `[Error] Received empty data object or array in ${functionName} triggered by event: ${eventName}` + ); + console.log( + '----------------------------------------------------------------------------------------------------' + ); + return false; + } + return true; +}; diff --git a/src/context/ModalContext/ModalContext.jsx b/src/context/ModalContext/ModalContext.jsx index 67debe6..1657960 100644 --- a/src/context/ModalContext/ModalContext.jsx +++ b/src/context/ModalContext/ModalContext.jsx @@ -4,21 +4,27 @@ export const ModalContext = createContext(); export const ModalProvider = ({ children }) => { const [modalContent, setModalContent] = useState(null); - const [isOpen, setIsOpen] = useState(false); + const [isModalOpen, setModalOpen] = useState(false); - const showModal = (content) => { - setModalContent(content); - setIsOpen(true); + const openModalWithCard = (card) => { + setModalContent(card); + setModalOpen(true); }; - const hideModal = () => { + const closeModal = () => { setModalContent(null); - setIsOpen(false); + setModalOpen(false); }; return ( {children} diff --git a/src/context/PopoverContext/PopoverContext.jsx b/src/context/PopoverContext/PopoverContext.jsx new file mode 100644 index 0000000..0204cd9 --- /dev/null +++ b/src/context/PopoverContext/PopoverContext.jsx @@ -0,0 +1,26 @@ +import React, { createContext, useState } from 'react'; + +export const PopoverContext = createContext({ + hoveredCard: null, + isPopoverOpen: false, + // eslint-disable-next-line @typescript-eslint/no-empty-function + setHoveredCard: () => {}, + // eslint-disable-next-line @typescript-eslint/no-empty-function + setIsPopoverOpen: () => {}, +}); + +export const PopoverProvider = ({ children }) => { + const [hoveredCard, setHoveredCard] = useState(null); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const value = { + hoveredCard, + isPopoverOpen, + setHoveredCard, + setIsPopoverOpen, + }; + + return ( + {children} + ); +}; diff --git a/src/context/SocketManager.jsx b/src/context/SocketManager.jsx deleted file mode 100644 index 625efc8..0000000 --- a/src/context/SocketManager.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import { useEffect } from 'react'; - -export const useSocketManager = (socket, eventHandlers) => { - useEffect(() => { - eventHandlers.forEach(({ event, handler }) => { - socket.on(event, handler); - }); - - return () => { - eventHandlers.forEach(({ event, handler }) => { - socket.off(event, handler); - }); - socket.disconnect(); - }; - }, [socket, eventHandlers]); -}; diff --git a/src/context/SocketProvider.jsx b/src/context/SocketProvider.jsx index 5a62a1f..f3d0953 100644 --- a/src/context/SocketProvider.jsx +++ b/src/context/SocketProvider.jsx @@ -9,10 +9,6 @@ import io from 'socket.io-client'; const SocketContext = createContext(); -// export const useSocket = () => { -// return useContext(SocketContext); -// }; - export const SocketProvider = ({ children }) => { const [socket, setSocket] = useState(null); diff --git a/src/context/UserContext/UserContext.js b/src/context/UserContext/UserContext.js index 148159b..2b462c4 100644 --- a/src/context/UserContext/UserContext.js +++ b/src/context/UserContext/UserContext.js @@ -13,12 +13,9 @@ const UserContext = createContext(); export const UserProvider = ({ children }) => { const [cookies, setCookie] = useCookies(['userCookie']); - // const [user, setUser] = useState(null); const [isCronJobTriggered, setIsCronJobTriggered] = useState(false); const [allCollections, setAllCollections] = useState([]); - const { user, setUser } = useAuthContext(); // Use the useAuthContext hook - // const { fetchCollections, fetchAllCollectionsForUser } = useCollectionStore(); const triggerCronJob = async () => { // Add your code here @@ -30,31 +27,21 @@ export const UserProvider = ({ children }) => { if (userID) { const updatedUser = { userID, username }; setUser(updatedUser); - // setAuthUser(updatedUser); // Update the user in AuthContext as well } }, [cookies]); const updateUser = (userData) => { setUser(userData); - // setAuthUser(userData); // Update the user in AuthContext setCookie('userCookie', userData, { path: '/' }); console.log('User Data Sent to Server and Cookie Updated:', userData); }; - // const fetchAndSetCollections = useCallback(async () => { - // const collections = fetchAllCollectionsForUser(user.id); - // if (collections) { - // setAllCollections(collections); // This is saved in the context - // } - // }, [user.id, fetchCollections]); - return ( { - const [isContextLoading, setIsContextLoading] = useState(true); - const [directedResponses, setDirectedResponses] = useState([]); // Initialize as an empty array - // const fetchWrapper = async (url, method, body = null) => { - // try { - // const options = { - // method, - // headers: { 'Content-Type': 'application/json' }, - // ...(body && { body: JSON.stringify(body) }), - // }; - // const response = await fetch(url, options); - // if (!response.ok) { - // throw new Error(`HTTP error! status: ${response.status}`); - // } - // return await response.json(); - // } catch (error) { - // if (error.message === 'Failed to fetch') { - // console.error('Network error: ', error); - // throw new Error( - // 'Network error. Please check your connection and try again.' - // ); - // } - // throw error; - // } - // }; - - const fetchDirectedResponses = async () => { - // let isMounted = true; // Added this flag - - try { - setIsContextLoading(true); - const response = await axios.get(`${BASE_API_URL}/directedResponses`); - const data = response.data; - - // if (isMounted) { - // Check if component is still mounted - Array.isArray(data) - ? setDirectedResponses(data) - : setDirectedResponses([]); - // } - } catch (error) { - // if (isMounted) { - // Check if component is still mounted - console.error('Error:', error); - setDirectedResponses([]); - // } - } finally { - // if (isMounted) { - // Check if component is still mounted - setIsContextLoading(false); - // } - } - }; - - // useEffect(() => { - // let isMounted = true; // Added this flag - - // if (isMounted && isContextLoading) { - // // console.log('Loading...'); - // } else if (isMounted && !isContextLoading) { - // // console.log('Finished Loading'); - // } - - // return () => { - // isMounted = false; // Cleanup - // }; - // }, [isContextLoading]); + const [isLoading, setIsLoading] = useState(true); const contextValue = { - isLoading: isContextLoading, - setIsContextLoading, - fetchDirectedResponses, - directedResponses: directedResponses, + isLoading, + setIsLoading, }; useEffect(() => { - if (isContextLoading) { + if (isLoading) { console.log('Loading...'); } else { console.log('Finished Loading', contextValue); } - }, [isContextLoading]); + }, [setIsLoading]); useEffect(() => { console.log('UTILITY CONTEXT VALUE:', contextValue); diff --git a/src/context/cleanUp/ApiServiceProvider.jsx b/src/context/cleanUp/ApiServiceProvider.jsx deleted file mode 100644 index 4105ddc..0000000 --- a/src/context/cleanUp/ApiServiceProvider.jsx +++ /dev/null @@ -1,139 +0,0 @@ -// // import { useCallback, useMemo } from 'react'; -// // import axios from 'axios'; -// // import { useCookies } from 'react-cookie'; -// // import { useCombinedContext } from './CombinedProvider'; -// // import useSocket from './SocketProvider'; - -// // export const useApiServiceProvider = () => { -// // const BASE_API_URL_CHARTS = `${process.env.REACT_APP_SERVER}/other/chart-data`; -// // const { userCookie } = useCookies(['userCookie']); -// // const { userId } = userCookie || {}; -// // const { setState, setChartData, setIsCronJobTriggered } = -// // useCombinedContext(); -// // const socket = useSocket(); - -// // const fetchData = useCallback(async () => { -// // if (!userId) return; - -// // setState((prevState) => ({ ...prevState, isLoading: true, error: null })); - -// // try { -// // const response = await axios.get( -// // `${BASE_API_URL_CHARTS}/charts/${userId}` -// // ); -// // setChartData(response?.data); -// // } catch (error) { -// // console.error('Error fetching updated data:', error); -// // setState((prevState) => ({ ...prevState, error: 'Error fetching data' })); -// // } finally { -// // setState((prevState) => ({ ...prevState, isLoading: false })); -// // } -// // }, [userId, setChartData, setState]); // Added `setState` to dependencies - -// // const updateServerData = useCallback( -// // async (updatedData) => { -// // if (!userId) return; - -// // const uniqueData = useMemo( -// // () => -// // Array.from( -// // new Set( -// // (updatedData || []) -// // .flatMap((obj) => obj.data || []) -// // .map(JSON.stringify) -// // ) -// // ).map(JSON.parse), -// // [updatedData] -// // ); - -// // try { -// // const chartId = updatedData?._id || 'all'; -// // const name = updatedData?.name || 'all'; - -// // const response = await axios.post( -// // `${BASE_API_URL_CHARTS}/charts/${userId}/${chartId}/updateChart`, -// // { userId, chartId, name, datasets: uniqueData } -// // ); - -// // socket.emit('RECEIVE_C2S_CHART_UPDATE', { -// // userId, -// // chartId, -// // name, -// // datasets: uniqueData, -// // }); - -// // setState((prevState) => ({ ...prevState, chartData: uniqueData })); -// // setIsCronJobTriggered(true); -// // } catch (error) { -// // console.error('Error updating server data:', error); -// // } -// // }, -// // [userId, setIsCronJobTriggered, setState, BASE_API_URL_CHARTS, socket] -// // ); - -// // return { fetchData, updateServerData }; -// // }; -// import React, { createContext, useContext, useCallback } from 'react'; -// import axios from 'axios'; - -// export const ApiContext = createContext(); - -// export const ApiServiceProvider = ({ children }) => { -// const BASE_API_URL = `${process.env.REACT_APP_SERVER}/other/cron`; - -// const getRequest = useCallback(async (endpoint) => { -// try { -// const response = await axios.get(`${BASE_API_URL}/${endpoint}`); -// return response.data; -// } catch (error) { -// console.error(`GET Error: ${error}`); -// throw error; -// } -// }, []); - -// const postRequest = useCallback(async (endpoint, data) => { -// try { -// const response = await axios.post(`${BASE_API_URL}/${endpoint}`, data); -// return response.data; -// } catch (error) { -// console.error(`POST Error: ${error}`); -// throw error; -// } -// }, []); - -// const putRequest = useCallback(async (endpoint, data) => { -// try { -// const response = await axios.put(`${BASE_API_URL}/${endpoint}`, data); -// return response.data; -// } catch (error) { -// console.error(`PUT Error: ${error}`); -// throw error; -// } -// }, []); - -// const deleteRequest = useCallback(async (endpoint) => { -// try { -// const response = await axios.delete(`${BASE_API_URL}/${endpoint}`); -// return response.data; -// } catch (error) { -// console.error(`DELETE Error: ${error}`); -// throw error; -// } -// }, []); - -// return ( -// -// {children} -// -// ); -// }; - -// export const useApiService = () => { -// const context = useContext(ApiContext); -// if (!context) { -// throw new Error('useApiService must be used within an ApiServiceProvider'); -// } -// return context; -// }; diff --git a/src/context/cleanUp/CombinedFetcher.jsx b/src/context/cleanUp/CombinedFetcher.jsx deleted file mode 100644 index 0cc21ae..0000000 --- a/src/context/cleanUp/CombinedFetcher.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import axios from 'axios'; -const BASE_API_URL_CRON = `${process.env.REACT_APP_SERVER}/other/cron`; -const BASE_API_URL_CHARTS = `${process.env.REACT_APP_SERVER}/other/chart-data`; - -export const fetchFromAPI = async (endpoint) => { - try { - return await axios.get(`${BASE_API_URL_CRON}/${endpoint}`); - } catch (error) { - console.error(`Error in request to ${endpoint}:`, error); - throw error; - } -}; - -export const fetchDataForUser = async (userId) => { - try { - const response = await axios.get(`${BASE_API_URL_CHARTS}/charts/${userId}`); - return response?.data; - } catch (error) { - console.error('Error fetching updated data:', error); - throw error; - } -}; - -export const updateServerDataForUser = async (userId, updatedData, io) => { - try { - const chartId = updatedData?._id || 'all'; - const name = updatedData?.name || 'all'; - const uniqueData = Array.from( - new Set( - (updatedData || []).flatMap((obj) => obj.data || []).map(JSON.stringify) - ) - ).map(JSON.parse); - - await axios.post( - `${BASE_API_URL_CHARTS}/charts/${userId}/${chartId}/updateChart`, - { userId, chartId, name, datasets: uniqueData } - ); - - io.emit('RECEIVE_C2S_CHART_UPDATE', { - userId, - chartId, - name, - datasets: uniqueData, - }); - return uniqueData; - } catch (error) { - console.error('Error updating server data:', error); - throw error; - } -}; diff --git a/src/context/cleanUp/SocketActions.jsx b/src/context/cleanUp/SocketActions.jsx deleted file mode 100644 index 60d8a7f..0000000 --- a/src/context/cleanUp/SocketActions.jsx +++ /dev/null @@ -1,171 +0,0 @@ -// import React, { -// createContext, -// useContext, -// useCallback, -// useEffect, -// useMemo, -// useState, -// } from 'react'; -// import { useSocket } from '../SocketProvider'; - -// const SocketActionsContext = createContext(); - -// export const useSocketActions = () => { -// return useContext(SocketActionsContext); -// }; -// const initialState = { -// chartData: {}, -// isLoading: false, -// data: {}, -// cronTriggerTimestamps: [], -// collectionData: [], -// deckData: [], -// allData: [], -// allUpdatedPrices: [], -// allItemTypeData: {}, -// error: null, -// prices: { -// totalCard: 0, -// updated: 0, -// allUpdated: [], -// totalDeck: 0, -// totalCollection: 0, -// }, -// cronData: {}, // Added cronData to initialState as it is referred in your code -// isDelaying: false, // Added isDelaying to initialState as it is referred in your code -// isCronJobTriggered: false, // Added isCronJobTriggered to initialState as it is referred in your code -// }; - -// export const SocketActionsProvider = ({ children }) => { -// const socket = useSocket(); -// const [receivedMessage, setReceivedMessage] = useState(null); -// // const [chartData, setChartData] = useState(); -// const [collectionData, setCollectionData] = useState(); - -// const [state, setState] = useState(initialState); -// /** -// * Custom hook to manage socket events -// // * @param {Socket} socket - the socket.io client socket -// // * @param {Array} events - array of event objects containing `event` and `handler` properties -// */ -// // const updateStateProperty = useCallback((property, value) => { -// // setState((prevState) => ({ ...prevState, [property]: value })); -// // }, []); -// // const setChartData = useCallback((newChartData) => { -// // setState((prevState) => ({ ...prevState, chartData: newChartData })); -// // }, []); -// // const setCollectionData = useCallback((newCollectionData) => { -// // setState((prevState) => ({ -// // ...prevState, -// // collectionData: newCollectionData, -// // })); -// // }, []); -// const sendMessage = useCallback( -// (message) => { -// if (socket) { -// console.log('Sending message:', message); -// const data = message; -// // socket.emit('connection'); -// socket.emit('MESSAGE_FROM_CLIENT', data); -// } else { -// console.error('Socket is not connected!'); -// } -// }, -// [socket] -// ); - -// const receiveMessage = useCallback((message) => { -// console.log('Received message:', message); -// setReceivedMessage(message); -// // handleHelloReply; -// // You might perform additional actions upon receiving a message here. -// }, []); - -// // const handleRequestCollectionData = useCallback(() => { -// // if (socket) socket.emit('EXISTING_COLLECTION_DATA', {}); -// // }, [socket]); - -// // const handleExistingCollectionData = (data) => { -// // console.log('Received existing collection data:', data.data); -// // setCollectionData(data.data); -// // }; - -// // Attach the event listener -// // socket.on('SEND_S2C_EXISTING_COLLECTION', handleExistingCollectionData); -// // const handleExistingChartData = useCallback( -// // (existingChartData) => { -// // setChartData(existingChartData); -// // }, -// // [setChartData] -// // ); - -// // const handleUpdateChartData = useCallback((data) => { -// // setState((prevState) => ({ -// // ...prevState, -// // chartData: [...prevState.chartData, ...data], -// // })); -// // }, []); - -// const handleUpdateCollectionData = useCallback((updatedCollectionData) => { -// setState((prevState) => ({ -// ...prevState, -// collectionData: updatedCollectionData, -// })); -// }, []); - -// // const handleDataUpdate = useCallback((dataUpdate) => { -// // console.log('HANDLING DATA UPDATE:', dataUpdate); -// // const { userId, chartId, name, datasets } = dataUpdate; -// // socket.emit('SEND_C2S_CHART_UPDATE', { -// // data: { userId, chartId, name, datasets }, -// // }); -// // }, []); - -// // const handleS2CChartUpdate = useCallback((s2cUpdate) => { -// // console.log('S2C UPDATE:', s2cUpdate); -// // socket.emit('RECEIVE_C2C_CHART_UPDATE', s2cUpdate); -// // }, []); - -// // const handleNewChartData = useCallback((newData) => { -// // setState((prevState) => ({ -// // ...prevState, -// // chartData: [...prevState.chartData, ...newData], -// // })); -// // }, []); - -// // useEffect(() => { -// // if (!socket) return; - -// // const handleNewChartData = (newData) => { -// // setState((prevState) => ({ -// // ...prevState, -// // chartData: [...prevState.chartData, ...newData], -// // })); -// // }; - -// // socket.on('NEW_CHART_DATA', handleNewChartData); - -// // return () => { -// // socket.off('NEW_CHART_DATA', handleNewChartData); -// // }; -// // }, [socket]); - -// return ( -// -// {children} -// -// ); -// }; diff --git a/src/context/cleanUp/SocketHandler.js b/src/context/cleanUp/SocketHandler.js deleted file mode 100644 index 6f9431f..0000000 --- a/src/context/cleanUp/SocketHandler.js +++ /dev/null @@ -1,115 +0,0 @@ -// import React, { createContext, useCallback, useEffect, useState } from 'react'; -// import io from 'socket.io-client'; - -// const SocketContext = createContext(); - -// const SocketHandler = ({ children }) => { -// const { userCookie } = useCookies(['userCookie']); -// const [state, setState] = useState(initialState); -// const userId = userCookie?.id; - -// // const { fetchData, updateServerData } = useApiServiceProvider(); -// const updateStateProperty = useCallback((property, value) => { -// setState((prevState) => ({ ...prevState, [property]: value })); -// }, []); -// const setChartData = useCallback((newChartData) => { -// setState((prevState) => ({ ...prevState, chartData: newChartData })); -// }, []); -// const setCollectionData = useCallback((newCollectionData) => { -// setState((prevState) => ({ -// ...prevState, -// collectionData: newCollectionData, -// })); -// }, []); -// const handleExistingCollectionData = useCallback( -// (existingCollectionData) => { -// setCollectionData(existingCollectionData); -// }, -// [setChartData] -// ); - -// const handleExistingChartData = useCallback( -// (existingChartData) => { -// setChartData(existingChartData); -// }, -// io.emit('SEND_C2S_CHART_UPDATE', { -// data: { data: existingChartData }, -// }), -// [setChartData] -// ); - -// const handleUpdateChartData = useCallback((data) => { -// setState((prevState) => ({ -// ...prevState, -// chartData: [...prevState.chartData, ...data], -// })); -// }, []); - -// const handleUpdateCollectionData = useCallback((updatedCollectionData) => { -// setState((prevState) => ({ -// ...prevState, -// collectionData: updatedCollectionData, -// })); -// }, []); - -// const handleDataUpdate = useCallback((dataUpdate) => { -// console.log('HANDLING DATA UPDATE:', dataUpdate); -// const { userId, chartId, name, datasets } = dataUpdate; - -// io.emit('SEND_C2S_CHART_UPDATE', { -// data: { userId, chartId, name, datasets }, -// }); -// }, []); - -// const handleReturnValue = useCallback((returnValue) => { -// // Logic to handle 'returnvalue' event -// // Example: Maybe update a state or log to console -// }, []); - -// const handleAllItemsUpdated = useCallback((updatedItems) => { -// state.setAllItems(updatedItems); -// // Additional logic as needed -// }, []); - -// const handleUpdateError = useCallback((errorInfo) => { -// // Logic to handle errors during the update -// // E.g., show a notification to the user -// }, []); - -// const handleS2CChartUpdate = useCallback((s2cUpdate) => { -// console.log('S2C UPDATE:', s2cUpdate); -// io.emit('RECEIVE_C2C_CHART_UPDATE', (s2cUpdate) => { -// console.log('S2C UPDATE:', s2cUpdate); -// }); -// }, []); -// }; - -// const value = { -// handleExistingCollectionData, -// handleExistingChartData, -// handleUpdateChartData, -// handleUpdateCollectionData, -// handleDataUpdate, -// handleReturnValue, -// handleAllItemsUpdated, -// handleUpdateError, -// handleS2CChartUpdate -// }; - -// return ( -// -// {children} -// -// ); -// }; - -// // 3. Export the provider and a custom hook to access the context -// export const useSocket = () => { -// const context = useContext(SocketContext); -// if (!context) { -// throw new Error("useSocket must be used within a SocketProvider"); -// } -// return context; -// }; - -// export { SocketProvider }; diff --git a/src/context/cleanUp/useSocketEvent.js b/src/context/cleanUp/useSocketEvent.js deleted file mode 100644 index 6bc4cb3..0000000 --- a/src/context/cleanUp/useSocketEvent.js +++ /dev/null @@ -1,16 +0,0 @@ -// useSocketEvent.js -import { useEffect } from 'react'; - -const useSocketEvent = (socket, event, handler) => { - useEffect(() => { - if (!socket) return; - - socket.on(event, handler); - - return () => { - socket.off(event, handler); - }; - }, [socket, event, handler]); -}; - -export default useSocketEvent; diff --git a/src/context/cleanUp/useUpdateChartData.jsx b/src/context/cleanUp/useUpdateChartData.jsx deleted file mode 100644 index 9e1d67c..0000000 --- a/src/context/cleanUp/useUpdateChartData.jsx +++ /dev/null @@ -1,72 +0,0 @@ -// import { useCallback, useEffect } from 'react'; -// import moment from 'moment'; -// import { useCollectionStore } from './hooks/collection'; -// import { useCombinedContext } from './CombinedProvider'; - -// const transformChartData = (chartData) => { -// let pointsArray = []; - -// if (Array.isArray(chartData?.datasets)) { -// chartData?.datasets.forEach((dataset) => { -// dataset.data?.forEach((dataEntry) => { -// const { x, y } = dataEntry.xy; -// if (x && y !== undefined) { -// pointsArray.push(dataEntry.xy); -// } -// }); -// }); -// } else { -// console.error( -// 'Expected chartData.datasets to be an array, but got:', -// chartData -// ); -// } - -// return pointsArray; -// }; -// const useUpdateChartData = () => { -// const { selectedCollection } = useCollectionStore() || {}; -// const { -// updateServerData, -// isCronJobTriggered, -// setIsCronJobTriggered, -// chartData, -// } = useCombinedContext() || {}; - -// const transformedData = transformChartData(selectedCollection?.chartData); - -// // const createDataset = (label, priceData) => ({ -// // name: label, -// // color: 'blue', -// // data: priceData?.map(({ x, y }) => ({ x, y })), -// // }); - -// // const newDataPoint = useCallback(() => { -// // return { -// // x: moment().format('YYYY-MM-DD HH:mm'), -// // y: totalCost ? parseFloat(totalCost).toFixed(2) : null, -// // }; -// // }, [totalCost]); - -// // const updateChart = useCallback(() => { -// // // let updatedDatasets = []; -// // const updatedDatasets = selectedCollection?.chartData?.datasets || []; -// // if (updatedDatasets.length) { -// // updateServerData(updatedDatasets); -// // } -// // // if (updatedDatasets.length) { -// // // updateServerData(updatedDatasets); -// // // } -// // }, []); - -// useEffect(() => { -// if (isCronJobTriggered) { -// // updateChart(); -// setIsCronJobTriggered(false); -// } -// }, [isCronJobTriggered, setIsCronJobTriggered]); -// console.log('transformedData', transformedData); -// return { datasets: transformedData }; -// }; - -// export default useUpdateChartData; diff --git a/src/context/hooks/collection.jsx b/src/context/hooks/collection.jsx deleted file mode 100644 index 9e1fc10..0000000 --- a/src/context/hooks/collection.jsx +++ /dev/null @@ -1,13 +0,0 @@ -// // useCollectionStore.js -// import { useContext } from 'react'; -// import { CollectionContext } from '../CollectionContext/CollectionContext'; - -// export const useCollectionStore = () => { -// const context = useContext(CollectionContext); -// if (!context) { -// throw new Error( -// 'useCollectionStore must be used within a CollectionProvider' -// ); -// } -// return context; -// }; diff --git a/src/context/hooks/useAppContext.jsx b/src/context/hooks/useAppContext.jsx index 125e6b7..dde49a6 100644 --- a/src/context/hooks/useAppContext.jsx +++ b/src/context/hooks/useAppContext.jsx @@ -1,16 +1,16 @@ // customHooks/useAppContext.js import { useContext } from 'react'; -import { AppContext } from '../AppContextProvider'; +import { AppContext } from '../AppContext/AppContextProvider'; const useAppContext = () => { const context = useContext(AppContext); if (!context) { console.error("The component isn't wrapped with AppContextProvider"); - return [null, false]; + return {}; // return an empty object } - return [context, true]; + return context; // return context as an object }; export default useAppContext; diff --git a/src/context/hooks/useUpdateContext.jsx b/src/context/hooks/useUpdateContext.jsx new file mode 100644 index 0000000..0ebc00c --- /dev/null +++ b/src/context/hooks/useUpdateContext.jsx @@ -0,0 +1,29 @@ +// import { useEffect } from 'react'; +// import { useLocation } from 'react-router-dom'; +// import useAppContext from './useAppContext'; + +// const useUpdateAppContext = () => { +// const location = useLocation(); +// const { setContext } = useAppContext(); // Destructuring from object + +// useEffect(() => { +// switch (location.pathname) { +// case '/deckbuilder': +// setContext('Deck'); +// break; +// case '/collection': +// setContext('Collection'); +// break; +// case '/cart': +// setContext('Cart'); +// break; +// default: +// setContext('Home'); +// break; +// } +// }, [location, setContext]); + +// return null; +// }; + +// export default useUpdateAppContext; diff --git a/src/components/headings/navigation/useWindowSize.jsx b/src/context/hooks/useWindowSize.jsx similarity index 100% rename from src/components/headings/navigation/useWindowSize.jsx rename to src/context/hooks/useWindowSize.jsx diff --git a/src/index.js b/src/index.js index 6d584fe..e6a9146 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import GlobalStyles from './assets/GlobalStyles'; +import ErrorBoundary from './context/ErrorBoundary'; import AuthProvider from './context/Auth/authContext'; import { CartProvider } from './context/CartContext/CartContext'; import { DeckProvider } from './context/DeckContext/DeckContext'; @@ -11,61 +12,58 @@ import { ModalProvider } from './context/ModalContext/ModalContext'; import { UserProvider } from './context/UserContext/UserContext'; import { CombinedProvider } from './context/CombinedProvider'; import { ThemeProvider } from '@mui/styles'; -import { useMode } from './context/hooks/colormode'; import { ColorModeProvider } from './context/ColorModeProvider'; -// import { ApiServiceProvider } from './context/cleanUp/ApiServiceProvider'; -import ErrorBoundary from './context/ErrorBoundary'; import { SocketProvider } from './context/SocketProvider'; import { SidebarProvider } from './context/SideBarProvider'; import { ChartProvider } from './context/ChartContext/ChartContext'; import { UtilityProvider } from './context/UtilityContext/UtilityContext'; -import { AppContextProvider } from './context/AppContextProvider'; -import { createTheme } from '@mui/material'; +import { AppContextProvider } from './context/AppContext/AppContextProvider'; +import { useMode } from './context/hooks/colormode'; +import { PopoverProvider } from './context/PopoverContext/PopoverContext'; +import { CronJobProvider } from './context/CronJobContext/CronJobContext'; +import { CardImagesProvider } from './context/CardImagesContext/CardImagesContext'; const root = document.getElementById('root'); function Main() { const { theme } = useMode(); - // const darkTheme = createTheme({ - // palette: { - // mode: 'dark', - // }, - // }); - // const theme = darkTheme; + return ( - {/* */} - {/* */} - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - {/* */} - {/* */} diff --git a/src/pages/CartPage.js b/src/pages/CartPage.js index 1b40e9e..85a85ec 100644 --- a/src/pages/CartPage.js +++ b/src/pages/CartPage.js @@ -7,15 +7,17 @@ import { Container, } from '@mui/material'; import { useCartStore } from '../context/CartContext/CartContext'; -import LoadingIndicator from '../components/indicators/LoadingIndicator'; -import ErrorIndicator from '../components/indicators/ErrorIndicator'; -import CartContentContainer from '../containers/CartContentContainer'; -import CustomerFormContainer from '../containers/CustomerFormContainer'; +import LoadingIndicator from '../components/reusable/indicators/LoadingIndicator'; +import ErrorIndicator from '../components/reusable/indicators/ErrorIndicator'; +import CartContentContainer from '../containers/cartPageContainers/CartContentContainer'; +import CustomerFormContainer from '../containers/cartPageContainers/CustomerFormContainer'; +import useUpdateAppContext from '../context/hooks/useUpdateContext'; const CartPage = () => { - const [cookies] = useCookies(['userCookie']); - const user = cookies.userCookie; + const [cookies] = useCookies(['user']); + const user = cookies.user; const userId = user?.id; + // useUpdateAppContext(); // This will set the context to 'Deck' when this page is rendered const { cartData, diff --git a/src/pages/CollectionPage.js b/src/pages/CollectionPage.js index 6e427ec..cc4aac6 100644 --- a/src/pages/CollectionPage.js +++ b/src/pages/CollectionPage.js @@ -1,20 +1,24 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { useCookies } from 'react-cookie'; -import LoadingIndicator from '../components/indicators/LoadingIndicator'; -import ErrorIndicator from '../components/indicators/ErrorIndicator'; +import LoadingIndicator from '../components/reusable/indicators/LoadingIndicator'; +import ErrorIndicator from '../components/reusable/indicators/ErrorIndicator'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; -import CollectionBanner from './pageStyles/CollectionBanner'; import CardPortfolio from '../components/collection/CardPortfolio'; import Subheader from '../components/reusable/Subheader'; import { useCollectionStore } from '../context/CollectionContext/CollectionContext'; +import { ModalContext } from '../context/ModalContext/ModalContext'; +import GenericCardModal from '../components/modals/GenericCardModal'; +import { CollectionBanner } from './pageStyles/StyledComponents'; +import useUpdateAppContext from '../context/hooks/useUpdateContext'; // Hero section with customizable props const HeroCenter = ({ decorative, title, subtitle }) => ( { // Destructuring the first element directly from the useCookies hook for cleaner code - const [{ userCookie }] = useCookies(['userCookie']); + const [{ user }] = useCookies(['user']); const { allCollections, selectedCollection, loading, error } = useCollectionStore(); - - const userId = userCookie?.id; + const { openModalWithCard, closeModal, isModalOpen, modalContent } = + useContext(ModalContext); + const userId = user?.id; + // useUpdateAppContext(); // This will set the context to 'Deck' when this page is rendered // Handling loading and error states upfront for better user experience if (loading) return ; if (error) return ; return ( - - - - - + + + + + + + + + + {' '} + {isModalOpen && ( + + )} + ); }; diff --git a/src/pages/DeckBuilderPage.js b/src/pages/DeckBuilderPage.js index 43629d5..4b9cdb1 100644 --- a/src/pages/DeckBuilderPage.js +++ b/src/pages/DeckBuilderPage.js @@ -1,20 +1,67 @@ import React, { useEffect, useState, useContext } from 'react'; -import DeckBuilderBanner from './pageStyles/DeckBuilderBanner'; -import DeckBuilderTitle from './pageStyles/DeckBuilderTitle'; import { useCookies } from 'react-cookie'; import { DeckContext } from '../context/DeckContext/DeckContext'; -import LoadingIndicator from '../components/indicators/LoadingIndicator'; -import ErrorIndicator from '../components/indicators/ErrorIndicator'; +import LoadingIndicator from '../components/reusable/indicators/LoadingIndicator'; +import ErrorIndicator from '../components/reusable/indicators/ErrorIndicator'; import DeckBuilderContainer from '../containers/deckBuilderPageContainers/DeckBuilderContainer'; -import { Grid } from '@mui/material'; +import { Box, Grid, Typography } from '@mui/material'; +import { ModalContext } from '../context/ModalContext/ModalContext'; +import GenericCardModal from '../components/modals/GenericCardModal'; +import HeaderTitle from '../components/reusable/HeaderTitle'; +import { useMode } from '../context/hooks/colormode'; +import { DeckBuilderBanner } from './pageStyles/StyledComponents'; +import useUpdateAppContext from '../context/hooks/useUpdateContext'; + +const HeroCenter2 = ({ title, subtitle }) => ( + + + {title} + + + {subtitle} + + +); + +HeroCenter2.defaultProps = { + title: 'Welcome to Deck Builder', + subtitle: 'Craft, refine, and explore your deck strategies in one place.', +}; const DeckBuilderPage = () => { const [userDecks, setUserDecks] = useState([]); - const { userCookie } = useCookies(['userCookie'])[0]; + const { user } = useCookies(['user'])[0]; + const { theme } = useMode(); const { fetchAllDecksForUser, allDecks, loading, error } = useContext(DeckContext); - const userId = userCookie?.id; + const { openModalWithCard, closeModal, isModalOpen, modalContent } = + useContext(ModalContext); + const userId = user?.id; + // useUpdateAppContext(); // This will set the context to 'Deck' when this page is rendered + console.log('userid', userId); useEffect(() => { fetchAllDecksForUser().catch((err) => console.error('Failed to get all decks:', err) @@ -25,7 +72,6 @@ const DeckBuilderPage = () => { if (allDecks && userId) { const filteredDecks = allDecks.filter((deck) => deck.userId === userId); setUserDecks(filteredDecks); - // console.log('(DECK PAGE) -- (USERDECKS):', userDecks); } }, [allDecks, userId]); @@ -33,14 +79,23 @@ const DeckBuilderPage = () => { if (error) return ; return ( - - Deck Builder - - - + + + + + + + - - + + {isModalOpen && ( + + )} + ); }; diff --git a/src/pages/HomePage.js b/src/pages/HomePage.js index 8affe01..6fcc033 100644 --- a/src/pages/HomePage.js +++ b/src/pages/HomePage.js @@ -1,42 +1,32 @@ import React, { useContext, useEffect, useRef } from 'react'; import { Carousel } from 'react-responsive-carousel'; import 'react-responsive-carousel/lib/styles/carousel.min.css'; -import { Container, Typography, Box, Stack, Button } from '@mui/material'; -import { makeStyles, useTheme } from '@mui/styles'; -import { ColorModeContext } from '../context/ColorModeProvider'; +import { + Container, + Typography, + Box, + Stack, + Button, + CssBaseline, + CardActions, + CardContent, + CardHeader, + Card, + Grid, + Paper, +} from '@mui/material'; import { useMode } from '../context/hooks/colormode'; -import HeaderTitle from '../components/reusable/HeaderTitle'; -import useStyles from './styles'; -// import Hero from './pageStyles/Hero'; - -const carouselImages = [ - { image: '/images/yugioh.jpeg', caption: 'Yu-Gi-Oh!' }, - { image: '/images/pokemon.jpeg', caption: 'Pokemon' }, - { - image: '/images/magic-the-gathering.jpeg', - caption: 'Magic: The Gathering', - }, -]; - -const HomeBanner = ({ children }) => { - return ( - - `linear-gradient(to right, ${theme.palette.primary.light}, ${theme.palette.secondary.main})`, - Height: '100vh', - padding: 4, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }} - > - {children} - - ); -}; - +import useStyles from './pageStyles/styles'; +import DeckOfCardsIcon from '../components/icons/DeckOfCardsIcon'; +import { ModalContext } from '../context/ModalContext/ModalContext'; +import GenericCardModal from '../components/modals/GenericCardModal'; +import { + TertiaryContainer, + SecondaryContainer, +} from './pageStyles/StyledComponents'; +import LoadingIndicator2 from '../components/reusable/indicators/LoadingIndicator2'; +import LoadingCardAnimation from '../assets/animations/LoadingCardAnimation'; +import pages from './pages.json'; const CarouselImage = ({ image, caption }) => { return (
@@ -49,7 +39,7 @@ const CarouselImage = ({ image, caption }) => { sx={{ position: 'absolute', bottom: 0, - backgroundColor: (theme) => theme.palette.secondary.main, + // backgroundColor: (theme) => theme.palette.secondary.main, color: (theme) => theme.palette.secondary.contrastText || 'common.white', width: '100%', @@ -66,111 +56,194 @@ const CarouselImage = ({ image, caption }) => { ); }; -const CarouselContainer = ({ isMounted }) => { - // if (!isMounted.current) { - // return; - // } - const classes = useStyles(); - return ( - - {carouselImages?.map(({ image, caption }, index) => ( - - ))} - - ); -}; - const HomePage = () => { - const classes = useStyles(); - const theme = useTheme(); - const isMounted = useRef(true); - - // useEffect(() => { - // return () => { - // isMounted.current = false; - // }; - // }, []); + // const theme = useTheme(); + const { theme, setMode } = useMode(); + const classes = useStyles(theme); + const { openModalWithCard, closeModal, isModalOpen, modalContent } = + useContext(ModalContext); + const { initialState, carouselImages, tiers } = pages; return ( - - - {/* */} + // + + + + // `linear-gradient(to right, ${theme.palette.success.light}, ${theme.palette.success.dark})`, + background: theme.palette.primary.light, + p: 8, + m: 4, + borderRadius: 2, }} > - - - Album layout - - + Album layout + + + Something short and leading about the collection below—its contents, + the creator, etc. Make it short and sweet, but not too short so + folks don't simply skip over it entirely. + + + - - - + Secondary action + + + {/* */} - - {carouselImages.map(({ image, caption }, index) => ( - + + + + {carouselImages.map(({ image, caption }, index) => ( + + ))} + + + + + + + + {tiers.map((tier) => ( + + + {' '} + : null} + subheaderTypographyProps={{ + align: 'center', + }} + sx={{ + background: theme.palette.success.main, + // backgroundColor: (theme) => + // theme.palette.mode === 'light' + // ? theme.palette.grey[200] + // : theme.palette.grey[700], + }} + /> + + + + ${tier.price} + + + /mo + + +
    + {tier.description.map((line) => ( + + {line} + + ))} +
+
+ + + +
+
))} -
+ -
+ {isModalOpen && ( + + )} + + // ); }; diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 0e641f2..a2e8ee0 100644 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -23,35 +23,13 @@ import { TypographyStyled, IconButtonStyled, ButtonStyled, -} from './StyledComponents'; + ButtonsContainer, + CustomButton, + ProfileFormContainer, +} from './pageStyles/StyledComponents'; import ProfileForm from '../components/forms/ProfileForm'; import { useCollectionStore } from '../context/CollectionContext/CollectionContext'; -const CustomButton = styled(Button)({ - margin: '10px', // Add margin around each button - width: '100%', // Make all buttons take full width of their parent - padding: '10px 0', // Add vertical padding - fontSize: '16px', // Set a consistent font size - // Any other styling you want to apply to all buttons -}); - -const ButtonsContainer = styled(Box)({ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - padding: '20px', - margin: '10px 0', - // Add additional styles as needed -}); - -const ProfileFormContainer = styled(Box)({ - marginTop: '20px', - padding: '20px', - boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)', // Example shadow - borderRadius: '8px', - // Add additional styles as needed -}); - const ProfilePage = () => { const { selectedCollection } = useCollectionStore(); const { user, updateUser } = useUserContext(); @@ -75,7 +53,8 @@ const ProfilePage = () => { const openSnackbar = (message) => { setSnackbarData({ open: true, message }); }; - const userId = user?.id; + // const userId = user?.id; + const userId = cookies.user?.id; console.log('USER ID:', userId); // console.log('USER:', user); const handleSaveChanges = useCallback( diff --git a/src/pages/Splash.css b/src/pages/Splash.css deleted file mode 100644 index efba8b5..0000000 --- a/src/pages/Splash.css +++ /dev/null @@ -1,113 +0,0 @@ -:root { - --calendar-width: 200px; - --calendar-height: 100px; -} - -/* Combine styles for splash_wrapper */ -.splash_wrapper { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - width: 100vw; - height: 100vh; - background: linear-gradient( - 45deg, - #305270, - #284b60, - #20444f, - #173d3e, - #0e362e, - #052f1d - ); - background-size: 600% 600%; - animation: Gradient 16s ease infinite; -} - -.logo_container { - margin-bottom: 2rem; -} - -.loading_text { - font-size: 1.5rem; - font-weight: bold; - text-transform: uppercase; - color: #284b60; /* Using the Adjusted Indigo dye from your pentenary color theme */ - letter-spacing: 1.5px; -} - -@keyframes Gradient { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } -} - -/* splash calendar animation */ - -.github_calendar_container { - position: relative; - display: flex; - align-items: center; - justify-content: center; - margin-top: 60px; /* More space from the animation */ - padding: 20px; - /* width: 200px; - height: 100px; */ - width: var(--calendar-width); - height: var(--calendar-height); -} - -/* - .rotating-border { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: -1; /* Place it below the GitHub calendar -} -*/ - -.rotating-border rect { - width: var(--calendar-width); - height: var(--calendar-height); - stroke-dasharray: 1000; - stroke-dashoffset: 1000; - animation: - rotateBorder 9s infinite linear, - dashOffset 9s infinite linear; -} - -.calendar-box { - position: relative; - z-index: 1; /* Place it above the rotating border */ -} - -@keyframes rotateBorder { - 0% { - /* stroke: #4db0a8; */ - stroke: red; - } - 50% { - stroke: #052f1d; - } - 100% { - /* stroke: #4db0a8; */ - stroke: red; - } -} - -@keyframes dashOffset { - 0% { - stroke-dashoffset: 1000; - } - 100% { - stroke-dashoffset: 0; - } -} diff --git a/src/pages/Splash.js b/src/pages/Splash.js deleted file mode 100644 index 5a4ed53..0000000 --- a/src/pages/Splash.js +++ /dev/null @@ -1,242 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import './Splash.css'; -// import { motion } from 'framer-motion'; -// import { theme } from '../../assets/theme'; -// import Particle from '../utils/Particle'; -// import logo from '../../assets/logo.png'; // Import the logo -// import GitHubCalendar from 'react-github-calendar'; -// import { Box } from '@chakra-ui/react'; -import CardDeckAnimation from './CardDeckAnimation'; - -// const logoVariants = { -// hidden: { opacity: 0 }, -// visible: { -// opacity: 1, -// rotate: [0, 360], // Add rotation animation -// transition: { -// duration: 4, -// repeat: Infinity, -// ease: 'linear', -// }, -// }, -// exit: { opacity: 0, transition: { duration: 0.5 } }, -// }; - -// const ellipsisVariants = { -// hidden: { opacity: 0 }, -// visible: { -// opacity: [0, 1, 1, 1, 0], // This makes the ellipsis fade in and out -// x: ['0%', '5%', '10%', '15%', '20%'], // Move the ellipsis to the right -// transition: { -// duration: 2, -// repeat: Infinity, -// repeatType: 'loop', -// }, -// }, -// exit: { opacity: 0, transition: { duration: 0.5 } }, -// }; - -// const textVariants = { -// hidden: { opacity: 0 }, -// visible: { -// opacity: 1, -// color: [ -// theme.colors.quaternary[400], -// theme.colors.quaternary[500], -// theme.colors.quaternary[600], -// theme.colors.quaternary[700], -// theme.colors.quaternary[800], -// theme.colors.quaternary[900], -// ], -// textShadow: ['0px 0px', '5px 5px', '3px 3px', '0px 0px'], -// transition: { -// duration: 6, // Increased due to more colors -// repeat: Infinity, -// repeatType: 'loop', -// }, -// }, -// exit: { opacity: 0, transition: { duration: 0.5 } }, -// }; - -// function AnimatedSplash() { -// const [calendarSize, setCalendarSize] = useState({ width: 0, height: 0 }); -// const calendarRef = useRef(null); -// // const rect = document.getElementById('myElement').getBoundingClientRect(); - -// // Declare minimum dimensions here -// const minWidth = window.innerWidth * 0.65; -// const minHeight = 100; - -// useEffect(() => { -// if (calendarRef.current) { -// // Check if the ref is assigned to a DOM element -// // const rect = calendarRef.current.getBoundingClientRect(); - -// const breakpoints = { -// xs: 20 * 16, -// sm: 28 * 16, -// md: 36 * 16, -// lg: 45 * 16, -// xl: 60 * 16, -// }; - -// const adjustSize = () => { -// const width = window.innerWidth; -// let newWidth, newHeight; -// if (width <= breakpoints.xs) { -// newWidth = '40px'; -// newHeight = '20px'; -// } else if (width <= breakpoints.sm) { -// newWidth = '50px'; -// newHeight = '25px'; -// } else if (width <= breakpoints.md) { -// newWidth = '60px'; -// newHeight = '30px'; -// } else if (width <= breakpoints.lg) { -// newWidth = '70px'; -// newHeight = '35px'; -// } else { -// newWidth = '200px'; -// newHeight = '40px'; -// } -// document.documentElement.style.setProperty( -// '--calendar-width', -// newWidth -// ); -// document.documentElement.style.setProperty( -// '--calendar-height', -// newHeight -// ); -// }; -// adjustSize(); -// window.addEventListener('resize', adjustSize); - -// if (calendarRef.current) { -// const rect = calendarRef.current.getBoundingClientRect(); -// setCalendarSize({ -// width: Math.max(rect.width + 15, minWidth), -// height: Math.max(rect.height + 15, minHeight), -// }); -// } -// console.log('Updated calendar size:', calendarSize); - -// return () => { -// window.removeEventListener('resize', adjustSize); -// }; -// } -// }, [calendarSize]); - -// return ( -// -// -//

-// ReedVogt.com -//

-//
- -// {Particle && } -// -//
-// -// Loading -// -// -// . -// . -// . -// -//
- -//
-// -// -// -// -// -// -//
-//
-// ); -// } - -function Splash() { - const [redirected, setRedirected] = useState(false); - const navigate = useNavigate(); - - useEffect(() => { - const id = setTimeout(() => setRedirected(true), 10000); - return () => clearTimeout(id); - }, []); - - useEffect(() => { - if (redirected) { - navigate('/'); - } - }, [redirected, navigate]); - - return redirected ? null : ; - // return redirected ? null : ; -} - -export default Splash; diff --git a/src/pages/StorePage.js b/src/pages/StorePage.js index 81e8ddb..f4b27e4 100644 --- a/src/pages/StorePage.js +++ b/src/pages/StorePage.js @@ -1,31 +1,13 @@ import React, { useState, useContext, useEffect } from 'react'; -import styled from 'styled-components'; import { Grid } from '@mui/material'; import { useCookies } from 'react-cookie'; import SearchBar from '../components/search/SearchBar'; import ProductGrid from '../components/grids/storeSearchResultsGrid/ProductGrid'; import { useCardStore } from '../context/CardContext/CardStore'; import { useCartStore } from '../context/CartContext/CartContext'; -import LoadingIndicator from '../components/indicators/LoadingIndicator'; -import ErrorIndicator from '../components/indicators/ErrorIndicator'; - -const StoreBanner = styled.div` - display: flex; - max-width: 100%; - justify-content: center; - margin: 0 auto; - flex-direction: column; - align-items: center; - padding: 20px; - background-color: #f7f7f7; -`; - -const StoreTitle = styled.h2` - color: #333; - font-size: 1.5rem; - text-align: center; - margin-bottom: 20px; -`; +import LoadingIndicator from '../components/reusable/indicators/LoadingIndicator'; +import ErrorIndicator from '../components/reusable/indicators/ErrorIndicator'; +import { StoreBanner, StoreTitle } from './pageStyles/StyledComponents'; const SearchContainer = () => { return ( diff --git a/src/pages/StyledComponents.jsx b/src/pages/StyledComponents.jsx deleted file mode 100644 index 54975ef..0000000 --- a/src/pages/StyledComponents.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import styled from '@emotion/styled'; -import { Avatar, Box, Button, IconButton, Typography } from '@mui/material'; -export const AvatarStyled = styled(Avatar)({ - width: 60, - height: 60, - marginBottom: 15, -}); - -export const TypographyStyled = styled(Typography)({ - marginBottom: 15, -}); - -export const IconButtonStyled = styled(IconButton)({ - marginBottom: 20, -}); - -export const DataBoxStyled = styled(Box)({ - margin: '10px 0', - padding: '10px', - border: '1px solid #ddd', - borderRadius: '8px', - textAlign: 'center', - width: '100%', -}); - -export const ButtonStyled = styled(Button)({ - margin: '15px 0', - padding: '10px', - color: '#fff', - backgroundColor: '#3f51b5', - '&:hover': { - backgroundColor: '#303f9f', - }, -}); - -export const DataTextStyled = styled(Typography)({ - margin: '5px 0', - fontSize: '0.9rem', -}); - -export const DataTextBoldStyled = styled(Typography)({ - margin: '5px 0', - fontSize: '0.9rem', - fontWeight: 'bold', -}); diff --git a/src/pages/pageStyles/CollectionBanner.jsx b/src/pages/pageStyles/CollectionBanner.jsx deleted file mode 100644 index ee3dd21..0000000 --- a/src/pages/pageStyles/CollectionBanner.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import styled from 'styled-components'; - -const CollectionBanner = styled.div` - display: flex; - max-width: 100vw; - ${'' /* min-width: '100vw'; */} - justify-content: center; - margin: 0 auto; - flex-direction: column; - align-items: center; - padding: 20px; - background-color: #f7f7f7; -`; - -export default CollectionBanner; diff --git a/src/pages/pageStyles/CollectionTitle.jsx b/src/pages/pageStyles/CollectionTitle.jsx deleted file mode 100644 index 394e73c..0000000 --- a/src/pages/pageStyles/CollectionTitle.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import styled from 'styled-components'; - -const CollectionTitle = styled.h2` - color: #333; - font-size: 1.5rem; - text-align: center; - margin-bottom: 20px; -`; - -export default CollectionTitle; diff --git a/src/pages/pageStyles/DeckBuilderBanner.jsx b/src/pages/pageStyles/DeckBuilderBanner.jsx deleted file mode 100644 index 4abda8b..0000000 --- a/src/pages/pageStyles/DeckBuilderBanner.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import styled from 'styled-components'; - -const DeckBuilderBanner = styled.div` - display: flex; - flex-direction: column; - align-items: center; - background-color: #f9f9f9; - width: 100%; - max-width: 1600px; - margin: auto; -`; - -export default DeckBuilderBanner; diff --git a/src/pages/pageStyles/DeckBuilderTitle.jsx b/src/pages/pageStyles/DeckBuilderTitle.jsx deleted file mode 100644 index 0f5c25d..0000000 --- a/src/pages/pageStyles/DeckBuilderTitle.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import styled from 'styled-components'; - -const DeckBuilderTitle = styled.h2` - color: #333; - font-size: 1.5rem; - text-align: center; - margin-bottom: 20px; -`; - -export default DeckBuilderTitle; diff --git a/src/pages/pageStyles/Hero.jsx b/src/pages/pageStyles/Hero.jsx deleted file mode 100644 index 45ca420..0000000 --- a/src/pages/pageStyles/Hero.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import { Box, Container, Typography } from '@mui/material'; -import { makeStyles } from '@mui/styles'; -import { Canvas } from '@react-three/fiber'; -import Cube from './Cube'; - -const useStyles = makeStyles((theme) => ({ - bannerBox: { - backgroundImage: `linear-gradient(to right, ${theme.palette.primary.light}, ${theme.palette.primary.main})`, - minHeight: '100vh', - padding: theme.spacing(4), - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, -})); - -const Hero = () => { - const classes = useStyles(); - return ( - - - - {/* eslint-disable-next-line react/no-unknown-property */} - - - - - - Buy Amazing Cards - - - - ); -}; - -export default Hero; diff --git a/src/pages/pageStyles/StyledComponents.jsx b/src/pages/pageStyles/StyledComponents.jsx new file mode 100644 index 0000000..8b3013e --- /dev/null +++ b/src/pages/pageStyles/StyledComponents.jsx @@ -0,0 +1,218 @@ +import { Avatar, Box, Button, IconButton, Typography } from '@mui/material'; +import { styled } from '@mui/styles'; + +// export const AppContainer = styled.div` +export const AppContainer = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + height: '100vh', +})); +export const MainContainer = styled('div')(({ theme }) => ({ + background: '#222', // Dark background + padding: theme.spacing(2), + marginBottom: theme.spacing(2), + maxHeight: '100%', + width: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + boxShadow: theme.shadows[3], + borderRadius: theme.shape.borderRadius, +})); +export const MainContainer2 = styled('div')(({ theme }) => ({ + background: '#333', // Dark background + padding: theme.spacing(2), + marginBottom: theme.spacing(2), + maxHeight: '100%', + width: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + boxShadow: theme.shadows[3], + borderRadius: theme.shape.borderRadius, +})); +export const MainContainer3 = styled('div')(({ theme }) => ({ + background: '#444', // Dark background + padding: theme.spacing(2), + marginBottom: theme.spacing(2), + maxHeight: '100%', + width: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + boxShadow: theme.shadows[3], + borderRadius: theme.shape.borderRadius, +})); +export const SecondaryContainer = styled('div')(({ theme }) => ({ + background: '#b7ebde', + padding: theme.spacing(2), + marginBottom: theme.spacing(2), + maxHeight: '100%', + width: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + boxShadow: theme.shadows[3], + borderRadius: theme.shape.borderRadius, +})); +export const TertiaryContainer = styled('div')(({ theme }) => ({ + padding: 3, + borderRadius: 2, + background: theme.palette.success.main, + boxShadow: theme.shadows[10], + mb: 4, +})); +export const MainPaperContainer = styled('div')(({ theme }) => ({ + elevation: 3, + borderRadius: 2, + margin: 0, + marginBottom: theme.spacing(2), + background: theme.palette.background.main, + width: '100%', + padding: theme.spacing(2), +})); + +export const SecondaryPaperContainer = styled('div')(({ theme }) => ({ + elevation: 3, + borderRadius: 2, + margin: 0, + marginBottom: theme.spacing(2), + background: '#70d8bd', + width: '100%', + padding: theme.spacing(2), +})); + +export const DeckBuilderBanner = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + backgroundColor: theme.palette.background.paper, + width: '100%', + maxWidth: '1600px', + margin: 'auto', + padding: theme.spacing(6, 2), + boxShadow: theme.shadows[4], + textAlign: 'center', +})); +// export const DeckBuilderBanner = styled(Box)(({ theme }) => ({ +// backgroundColor: theme.palette.background.paper, +// padding: theme.spacing(6, 2), +// boxShadow: theme.shadows[4], +// textAlign: 'center', +// })); +// export const DeckBuilderBanner = styled.div` +// display: flex; +// flex-direction: column; +// align-items: center; +// background-color: #f9f9f9; +// width: 100%; +// max-width: 1600px; +// margin: auto; +// `; +export const AvatarStyled = styled(Avatar)({ + width: 60, + height: 60, + marginBottom: 15, +}); + +export const TypographyStyled = styled(Typography)({ + marginBottom: 15, +}); + +export const IconButtonStyled = styled(IconButton)({ + marginBottom: 20, +}); + +export const DataBoxStyled = styled(Box)({ + margin: '10px 0', + padding: '10px', + border: '1px solid #ddd', + borderRadius: '8px', + textAlign: 'center', + width: '100%', +}); + +export const ButtonStyled = styled(Button)({ + margin: '15px 0', + padding: '10px', + color: '#fff', + backgroundColor: '#3f51b5', + '&:hover': { + backgroundColor: '#303f9f', + }, +}); + +export const DataTextStyled = styled(Typography)({ + margin: '5px 0', + fontSize: '0.9rem', +}); + +export const DataTextBoldStyled = styled(Typography)({ + margin: '5px 0', + fontSize: '0.9rem', + fontWeight: 'bold', +}); + +export const DeckBuilderTitle = styled(Typography)(({ theme }) => ({ + color: '#333', + fontSize: '1.5rem', + textAlign: 'center', + marginBottom: theme.spacing(2.5), +})); + +export const CollectionTitle = styled(Typography)(({ theme }) => ({ + color: '#333', + fontSize: '1.5rem', + textAlign: 'center', + marginBottom: theme.spacing(2.5), +})); + +export const CollectionBanner = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'center', + margin: '0 auto', + flexDirection: 'column', + alignItems: 'center', + padding: theme.spacing(2.5), + backgroundColor: '#f7f7f7', +})); + +export const StoreBanner = styled(Box)(({ theme }) => ({ + display: 'flex', + maxWidth: '100%', + justifyContent: 'center', + margin: '0 auto', + flexDirection: 'column', + alignItems: 'center', + padding: theme.spacing(2.5), + backgroundColor: '#f7f7f7', +})); + +export const StoreTitle = styled(Typography)(({ theme }) => ({ + color: '#333', + fontSize: '1.5rem', + textAlign: 'center', + marginBottom: theme.spacing(2.5), +})); + +export const CustomButton = styled(Button)({ + margin: '10px', + width: '100%', + padding: '10px 0', + fontSize: '16px', +}); + +export const ButtonsContainer = styled(Box)({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + padding: '20px', + margin: '10px 0', +}); + +export const ProfileFormContainer = styled(Box)({ + marginTop: '20px', + padding: '20px', + boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)', + borderRadius: '8px', +}); diff --git a/src/pages/styles.jsx b/src/pages/pageStyles/styles.jsx similarity index 67% rename from src/pages/styles.jsx rename to src/pages/pageStyles/styles.jsx index 330a361..91c27ca 100644 --- a/src/pages/styles.jsx +++ b/src/pages/pageStyles/styles.jsx @@ -1,6 +1,52 @@ import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles((theme) => ({ + mainContainer: { + padding: theme.spacing(2), + marginBottom: theme.spacing(2), + background: '#222', // Dark background + // background: theme.palette.primary.light, + maxHeight: '100%', // Set the container height to 25vh + width: '100%', // Set the container width to 100% + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + boxShadow: theme.shadows[3], + borderRadius: theme.shape.borderRadius, + }, + secondaryContainer: { + padding: theme.spacing(2), + marginBottom: theme.spacing(2), + // background: '#222', // Dark background + background: '#b7ebde', + // background: theme.palette.primary.light, + maxHeight: '100%', // Set the container height to 25vh + width: '100%', // Set the container width to 100% + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + boxShadow: theme.shadows[3], + borderRadius: theme.shape.borderRadius, + }, + mainPaperContainer: { + elevation: 3, + borderRadius: 2, + margin: 0, + marginBottom: theme.spacing(2), + background: theme.palette.background.main, + width: '100%', // Set the container width to 100% + padding: theme.spacing(2), + }, + secondaryPaperContainer: { + elevation: 3, + borderRadius: 2, + margin: 0, + marginBottom: theme.spacing(2), + // background: theme.palette.background.main, + background: '#70d8bd', + width: '100%', // Set the container width to 100% + padding: theme.spacing(2), + }, // return { banner: { backgroundImage: `linear-gradient(to right, ${theme.palette.primary.light}, ${theme.palette.secondary.main})`, @@ -110,6 +156,17 @@ const useStyles = makeStyles((theme) => ({ paddingBottom: '30px', fontSize: '18px', }, + cardHoverEffect: { + '&:hover': { + transform: 'scale(1.05)', // Enlarge card on hover + transition: 'transform 0.3s ease-in-out', // Smooth transition for the hover effect + }, + }, + // carouselContainer: { + // '& .carousel .slide': { + // backgroundColor: (theme) => theme.palette.background.paper, + // }, + // }, title: { paddingBottom: '15px', }, diff --git a/src/pages/pages.json b/src/pages/pages.json new file mode 100644 index 0000000..eedc150 --- /dev/null +++ b/src/pages/pages.json @@ -0,0 +1,49 @@ +{ + "carouselImages": [ + { "image": "/images/yugioh.jpeg", "caption": "Yu-Gi-Oh!" }, + { "image": "/images/pokemon.jpeg", "caption": "Pokemon" }, + { + "image": "/images/magic-the-gathering.jpeg", + "caption": "Magic: The Gathering" + } + ], + "tiers": [ + { + "title": "Deck Builder", + "price": "0", + "description": [ + "10 users included", + "2 GB of storage", + "Help center access", + "Email support" + ], + "buttonText": "Sign up for free", + "buttonVariant": "outlined" + }, + { + "title": "Store", + "subheader": "Most popular", + "price": "15", + "description": [ + "20 users included", + "10 GB of storage", + "Help center access", + "Priority email support" + ], + "buttonText": "Get started", + "buttonVariant": "contained" + }, + { + "title": "Collection Tracker", + "price": "30", + "description": [ + "50 users included", + "30 GB of storage", + "Help center access", + "Phone & email support" + ], + "buttonText": "Contact us", + "buttonVariant": "outlined" + } + ] +} diff --git a/src/setupProxy.js b/src/setupProxy.js new file mode 100644 index 0000000..9f0c65b --- /dev/null +++ b/src/setupProxy.js @@ -0,0 +1,18 @@ +// // eslint-disable-next-line @typescript-eslint/no-var-requires +// const { createProxyMiddleware } = require('http-proxy-middleware'); + +// module.exports = function (app) { +// app.use( +// '/api', +// createProxyMiddleware({ +// target: 'https://images.ygoprodeck.com', +// headers: { +// // 'Access-Control-Allow-Origin': '*', +// crossorigin: 'anonymous', +// // 'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE,PATCH,OPTIONS', +// }, +// changeOrigin: true, +// pathRewrite: { '^/api': '' }, +// }) +// ); +// };