diff --git a/package.json b/package.json index 1f140a9..045fe72 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@babel/plugin-proposal-optional-chaining": "^7.21.0", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", + "@floating-ui/react": "^0.26.9", "@fortawesome/fontawesome-free": "^6.4.0", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.14.5", @@ -57,6 +58,7 @@ "react-table": "^7.8.0", "react-transition-group": "^4.4.5", "react-virtualized": "^9.22.5", + "recharts": "^2.12.3", "sass": "^1.69.7", "shortid": "^2.2.16", "slick-carousel": "^1.8.1", diff --git a/src/App.js b/src/App.js index 20a69a0..e176420 100644 --- a/src/App.js +++ b/src/App.js @@ -5,23 +5,19 @@ import Main from './Main'; import { FormProvider, ModalProvider, - PopoverProvider, UserProvider, useMode, CollectionProvider, CardProvider, DeckProvider, CartProvider, - CardImagesProvider, ChartProvider, StatisticsProvider, SidebarProvider, AppContextProvider, useAuthContext, - usePageContext, ErrorBoundary, ConfiguratorProvider, - VisibilityProvider, } from './context'; import { ThemeProvider } from 'styled-components'; import { SnackbarProvider, useSnackbar } from 'notistack'; @@ -37,14 +33,10 @@ const App = () => { const navigate = useNavigate(); const { resetLogoutTimer, logout, authUser, userId, isLoggedIn } = useAuthContext(); - const { returnDisplay } = usePageContext(); const { isLoading, isPageLoading, error } = useLoading(); - // useEffect(() => { - // if (!isLoggedIn && !isPageLoading) navigate('/login'); - // }, [isLoggedIn, navigate, isPageLoading]); if (isPageLoading || error) { - return returnDisplay(); + return
Loading...
; } return ( @@ -53,37 +45,29 @@ const App = () => { - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - + + + + + + + + + + + +
+ + + + + + + + + + + diff --git a/src/Main.jsx b/src/Main.jsx index f58dade..b4768d0 100644 --- a/src/Main.jsx +++ b/src/Main.jsx @@ -1,17 +1,19 @@ import React, { Suspense, lazy } from 'react'; import { Route, Routes } from 'react-router-dom'; import { useMediaQuery } from '@mui/material'; -import PrivateRoute from './components/reusable/PrivateRoute'; +import PrivateRoute from './layout/PrivateRoute.jsx'; import LoginDialog from './components/dialogs/LoginDialog'; import { useAuthContext, useConfiguratorContext, useMode } from './context'; -import PageLayout from './layout/Containers/PageLayout.jsx'; +import PageLayout from './layout/REUSABLE_COMPONENTS/PageLayout.jsx'; import Navigation from './layout/navigation/Navigation.jsx'; -import LoadingIndicator from './components/reusable/indicators/LoadingIndicator.js'; +import LoadingIndicator from './layout/LoadingIndicator.js'; import Configurator from './layout/REUSABLE_COMPONENTS/Configurator/index.jsx'; -import { useCardStoreHook } from './context/hooks/useCardStore.jsx'; -import { AppContainer } from './pages/pageStyles/StyledComponents.jsx'; +import { CSSTransition, TransitionGroup } from 'react-transition-group'; +import LoadingOverlay from './layout/LoadingOverlay.jsx'; +import SelectionErrorDialog from './components/dialogs/SelectionErrorDialog.jsx'; +import useDialogState from './context/hooks/useDialogState.jsx'; +import useSelectedCollection from './context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx'; -// Define all routes in a single array including the component name for laziness const ROUTE_CONFIG = [ { path: '/', componentName: 'HomePage', isPrivate: false }, { path: '/home', componentName: 'HomePage', isPrivate: false }, @@ -29,7 +31,6 @@ const ROUTE_CONFIG = [ { path: '*', componentName: 'NotFoundPage', isPrivate: false }, ]; -// Dynamically import page components based on route configuration const LazyRoute = ({ componentName, ...rest }) => { const Component = lazy(() => import(`./pages/${componentName}`)); return ; @@ -39,6 +40,8 @@ const Main = () => { const isMobileView = useMediaQuery(theme.breakpoints.down('sm')); const { isLoggedIn } = useAuthContext(); const { isConfiguratorOpen, toggleConfigurator } = useConfiguratorContext(); + const { dialogState, openDialog, closeDialog } = useDialogState({}); + const { selectedCollection } = useSelectedCollection(); return ( <> {!isLoggedIn ? ( @@ -49,27 +52,40 @@ const Main = () => { backgroundColor: '#3D3D3D', }} > - + {isConfiguratorOpen && } - }> - - {ROUTE_CONFIG.map(({ path, componentName, isPrivate }, index) => ( - - - - ) : ( - + + + }> + + {ROUTE_CONFIG.map( + ({ path, componentName, isPrivate }, index) => ( + + + + ) : ( + + ) + } + /> ) - } - /> - ))} - - + )} + + + + + {dialogState.isSelectionErrorDialogOpen && ( + closeDialog('isSelectionErrorDialogOpen')} + selectedValue={selectedCollection?.name} + /> + )} )} diff --git a/src/assets/animations/Cube.jsx b/src/assets/animations/Cube.jsx deleted file mode 100644 index 9812023..0000000 --- a/src/assets/animations/Cube.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { Canvas } from '@react-three/fiber'; - -const Cube = () => { - return ( - - {/* eslint-disable-next-line react/no-unknown-property */} - - - - ); -}; - -export default Cube; diff --git a/src/assets/animations/NavMotion.jsx b/src/assets/animations/NavMotion.jsx deleted file mode 100644 index e807625..0000000 --- a/src/assets/animations/NavMotion.jsx +++ /dev/null @@ -1,48 +0,0 @@ -// third party -import { motion } from 'framer-motion'; - -// types -import PropTypes from 'prop-types'; - -// ==============================|| ANIMATION FOR CONTENT ||============================== // - -const NavMotion = ({ children }) => { - const motionVariants = { - initial: { - opacity: 0, - scale: 0.99, - }, - in: { - opacity: 1, - scale: 1, - }, - out: { - opacity: 0, - scale: 1.01, - }, - }; - - const motionTransition = { - type: 'tween', - ease: 'anticipate', - duration: 0.4, - }; - - return ( - - {children} - - ); -}; - -NavMotion.propTypes = { - children: PropTypes.node, -}; - -export default NavMotion; diff --git a/src/assets/animations/SplashPage3.jsx b/src/assets/animations/SplashPage3.jsx deleted file mode 100644 index 24c0878..0000000 --- a/src/assets/animations/SplashPage3.jsx +++ /dev/null @@ -1,28 +0,0 @@ -// SplashPage.js -import React from 'react'; -import { Box, Typography } from '@mui/material'; -import CardStormAnimation from './CardStormAnimation'; - -const SplashPage3 = () => { - return ( - - - Welcome to the Card Storm - - - {/* Include the Card Animation */} - - - ); -}; - -export default SplashPage3; diff --git a/src/assets/animations/ThreeJsCube.js b/src/assets/animations/ThreeJsCube.js deleted file mode 100644 index 3c0435c..0000000 --- a/src/assets/animations/ThreeJsCube.js +++ /dev/null @@ -1,54 +0,0 @@ -import React, { useEffect } from 'react'; -import * as THREE from 'three'; - -const ThreeJsCube = () => { - useEffect(() => { - // Three.js code for rendering the cube - const scene = new THREE.Scene(); - const camera = new THREE.PerspectiveCamera( - 75, - window.innerWidth / window.innerHeight, - 0.1, - 1000 - ); - const renderer = new THREE.WebGLRenderer(); - - // Set the size of the rendering window - renderer.setSize(window.innerWidth, window.innerHeight); - - // Append the renderer to the container - document - .getElementById('threejs-container') - .appendChild(renderer.domElement); - - // Create a cube with a basic material - const geometry = new THREE.BoxGeometry(); - const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // Green color - const cube = new THREE.Mesh(geometry, material); - - // Add the cube to the scene - scene.add(cube); - - // Position the camera - camera.position.z = 5; - - // Create an animation loop - const animate = () => { - requestAnimationFrame(animate); - - // Rotate the cube - cube.rotation.x += 0.01; - cube.rotation.y += 0.01; - - // Render the scene with the camera - renderer.render(scene, camera); - }; - - // Start the animation loop - animate(); - }, []); - - return
; -}; - -export default ThreeJsCube; diff --git a/src/assets/animations/ZenEnso.jsx b/src/assets/animations/ZenEnso.jsx deleted file mode 100644 index e659de6..0000000 --- a/src/assets/animations/ZenEnso.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useRef, useEffect } from 'react'; -import * as THREE from 'three'; - -const ZenEnso = () => { - const mountRef = useRef(null); - - useEffect(() => { - const width = mountRef.current.clientWidth; - const height = mountRef.current.clientHeight; - const scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); - camera.position.z = 5; - - const renderer = new THREE.WebGLRenderer(); - renderer.setSize(width, height); - mountRef.current.appendChild(renderer.domElement); - - const points = []; - const detail = 100; - const radius = 2; - const incompleteFactor = 0.85; // Making the circle incomplete - const variance = 0.2; // Increased randomness - - for (let i = 0; i <= detail * incompleteFactor; i++) { - const ratio = i / detail; - const angle = 2 * Math.PI * ratio; - const randomVariance = 1 + Math.random() * variance - variance / 2; - const x = radius * Math.cos(angle) * randomVariance; - const y = radius * Math.sin(angle) * randomVariance; - points.push(new THREE.Vector3(x, y, 0)); - } - - const geometry = new THREE.BufferGeometry().setFromPoints(points); - const material = new THREE.LineBasicMaterial({ - color: 0x000000, - linewidth: 2, - }); // Slight width - const enso = new THREE.Line(geometry, material); - - scene.add(enso); - - const animate = () => { - requestAnimationFrame(animate); - enso.rotation.z += 0.01; // Rotation - renderer.render(scene, camera); - }; - - animate(); - - return () => { - mountRef.current.removeChild(renderer.domElement); - }; - }, []); - - return
; -}; - -export default ZenEnso; diff --git a/src/assets/css/index.css b/src/assets/css/index.css index 5ff2073..234d377 100644 --- a/src/assets/css/index.css +++ b/src/assets/css/index.css @@ -23,6 +23,24 @@ html { scroll-behavior: smooth; } +.fade-enter { + opacity: 0; +} + +.fade-enter-active { + opacity: 1; + transition: opacity 300ms ease-in; +} + +.fade-exit { + opacity: 1; +} + +.fade-exit-active { + opacity: 0; + transition: opacity 300ms ease-in; +} + /* @media (min-width: 1440px) { html { zoom: 1.5; @@ -147,7 +165,7 @@ button { /* padding: 60px; */ } -.hero-section-container { +/* .hero-section-container { background: rgba(189, 181, 181, 0.1); box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); backdrop-filter: blur(20px); @@ -155,9 +173,32 @@ button { border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 25px; /* padding: 30px 0px; */ - width: '100vw'; +/* width: '100vw'; */ +/* width: min(1200px, 100%); */ +/* } */ +.hero-section-container { + background: rgba(189, 181, 181, 0.1); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 25px; + /* padding: 30px 0px; */ + /* width: '100vw'; */ /* width: min(1200px, 100%); */ } +.hero-section-container-left { + position: absolute; + top: 20px; /* Adjust as needed */ + left: 20px; /* Adjust as needed */ + width: 200px; /* Adjust as needed */ + height: 100px; /* Adjust as needed */ + /* background-color: rgba(255, 255, 255, 0.8); Semi-transparent white */ + display: flex; + justify-content: center; + align-items: center; + z-index: 2; /* Ensure it's above the swiper slides */ +} /* .hero-section h1 { font-size: 3.5rem; @@ -216,6 +257,14 @@ button { width: 100vw; /* Adjust based on your design needs */ overflow: hidden; /* Prevent overflow */ } + +/* .swiper-wrapper { + display: flex; /* Ensure the slides are in a row */ +/* transition: transform 0.3s; /* Smooth transition */ +/* align-items: center; + justify-content: center; +} */ + .swiper-scrollbar { --swipeer-scrollbar-bottom: 0; --swipeer-scrollbar-drag-bg-color: #dda3b6; @@ -241,14 +290,14 @@ button { @media (max-width: 500px) { .swiper_container { - height: 47rem; + /* height: 47rem; */ } .swiper-slide { width: 28rem !important; - height: 36rem !important; + /* height: 36rem !important; */ } .swiper-slide img { - width: 28rem !important; + /* width: 28rem !important; */ height: 36rem !important; } } @@ -259,13 +308,13 @@ button { } .swiper-slide { width: 30rem; - height: 36rem; + /* height: 36rem; */ /* height: auto; */ /* Adjust the height as needed */ } .swiper-slide img { width: 30rem; - height: 36rem; + /* height: 36rem; */ } } @@ -333,7 +382,7 @@ button { } .swiper-slide-active { - transform: scale(2); /* Make the active slide larger */ + transform: scale(8); /* Make the active slide larger */ transition: transform 0.3s; /* Smooth transition */ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Add a shadow */ } diff --git a/src/assets/currentlyUnused/AddCollectionForm.jsx b/src/assets/currentlyUnused/AddCollectionForm.jsx new file mode 100644 index 0000000..80ca8ea --- /dev/null +++ b/src/assets/currentlyUnused/AddCollectionForm.jsx @@ -0,0 +1,35 @@ +// import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; +// import RCZodForm from './reusable/RCZodForm'; + +// const AddCollectionForm = () => { +// // Assuming 'addCollectionForm' is the key/name for this form schema in your context +// const schemaName = 'addCollectionForm'; +// const buttonLabel = 'Create Collection'; +// const startIcon = ; +// const addCollectionFields = [ +// { +// name: 'name', +// label: 'Name', +// type: 'text', +// required: true, +// }, +// { +// name: 'description', +// label: 'Description', +// type: 'text', +// required: true, +// multiline: true, +// rows: 4, +// }, +// ]; +// return ( +// +// ); +// }; + +// export default AddCollectionForm; diff --git a/src/assets/currentlyUnused/AddDeckForm.jsx b/src/assets/currentlyUnused/AddDeckForm.jsx new file mode 100644 index 0000000..95d6071 --- /dev/null +++ b/src/assets/currentlyUnused/AddDeckForm.jsx @@ -0,0 +1,39 @@ +// import React from 'react'; +// import { useFormContext } from '../../context'; +// import RCZodForm from './RCZodForm'; +// import SaveIcon from '@mui/icons-material/Save'; + +// const AddDeckForm = () => { +// const { setFormSchema } = useFormContext(); +// const formId = 'addDeckForm'; // Assuming this is the formId for creating decks +// const fields = [ +// { name: 'name', label: 'Name', type: 'text', required: true }, +// { +// name: 'description', +// label: 'Description', +// type: 'text', +// required: true, +// multiline: true, +// }, +// ]; + +// const handleSubmit = (data) => { +// console.log('Add Deck Data:', data); +// }; + +// React.useEffect(() => { +// setFormSchema(formId); +// }, [setFormSchema, formId]); + +// return ( +// } +// /> +// ); +// }; + +// export default AddDeckForm; diff --git a/src/assets/animations/CardDeckAnimation.js b/src/assets/currentlyUnused/CardDeckAnimation.js similarity index 100% rename from src/assets/animations/CardDeckAnimation.js rename to src/assets/currentlyUnused/CardDeckAnimation.js diff --git a/src/assets/currentlyUnused/CardImagesContext/CardImagesContext.jsx b/src/assets/currentlyUnused/CardImagesContext/CardImagesContext.jsx new file mode 100644 index 0000000..0dd2c1c --- /dev/null +++ b/src/assets/currentlyUnused/CardImagesContext/CardImagesContext.jsx @@ -0,0 +1,114 @@ +// 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 [imageSrc, setImageSrc] = useState(null); +// const [isLoading, setIsLoading] = useState(false); +// const [error, setError] = useState(null); +// // const BASE_API_URL = 'http://localhost:3001/api/card-image'; +// const BASE_API_URL = `${process.env.REACT_APP_SERVER}/api/card-image`; + +// // Function to download card images +// const downloadCardImages = async () => { +// setIsLoading(true); +// try { +// const response = await fetchWrapper(BASE_API_URL + '/download', 'GET'); +// const fetchedCards = response.data; +// const imageUrls = fetchedCards +// .map((card) => { +// if (card.card_images && card.card_images.length > 0) { +// return card.card_images[0].image_url + '?dummy=' + Date.now(); +// } +// return null; // or some placeholder image URL +// }) +// .filter(Boolean); // Remove any nulls + +// setCards(fetchedCards); +// setImages(imageUrls); // Set all image URLs at once +// } catch (error) { +// console.error('Error in downloadCardImages:', error); +// setError(error.message); +// } finally { +// setIsLoading(false); +// } +// }; + +// // useEffect(() => { +// // downloadCardImages(); +// // }, []); + +// // Handle random card image selection +// useEffect(() => { +// if (cards && cards.length > 0) { +// const randomCardIndex = Math.floor(Math.random() * cards.length); +// const randomCard = cards[randomCardIndex]; +// if ( +// randomCard && +// randomCard.card_images && +// randomCard.card_images.length > 0 +// ) { +// setRandomCardImage(randomCard.card_images[0].image_url); +// } +// } +// }, [cards]); // Dependency on cards means this will rerun when cards are fetched/updated + +// 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} +// +// ); +// }; diff --git a/src/assets/currentlyUnused/CardImagesContext/useCardManager.jsx b/src/assets/currentlyUnused/CardImagesContext/useCardManager.jsx new file mode 100644 index 0000000..8c2c4e3 --- /dev/null +++ b/src/assets/currentlyUnused/CardImagesContext/useCardManager.jsx @@ -0,0 +1,39 @@ +// import { useState } from 'react'; + +// const useCardManager = (initialCards = []) => { +// const [cards, setCards] = useState(initialCards); + +// // General function to update a card +// const updateCard = (cardId, updatedData) => { +// setCards((currentCards) => +// currentCards.map((card) => +// card.id === cardId ? { ...card, ...updatedData } : card +// ) +// ); +// }; + +// // Add a new card +// const addCard = (newCardData) => { +// setCards([...cards, newCardData]); +// }; + +// // General utility to update a specific field of a card +// const updateField = (cardId, field, value) => { +// updateCard(cardId, { [field]: value }); +// }; + +// // Update a nested field like priceHistory or card_images +// const updateNestedField = (cardId, field, newValue) => { +// const card = cards.find((card) => card.id === cardId); +// updateCard(cardId, { [field]: [...card[field], newValue] }); +// }; + +// return { +// cards, +// addCard, +// updateCard: updateField, // for updating single fields +// updateNestedField, // for updating nested fields like arrays +// }; +// }; + +// export default useCardManager; diff --git a/src/assets/currentlyUnused/CardImagesContext/useCardVariantManager.jsx b/src/assets/currentlyUnused/CardImagesContext/useCardVariantManager.jsx new file mode 100644 index 0000000..88a8f22 --- /dev/null +++ b/src/assets/currentlyUnused/CardImagesContext/useCardVariantManager.jsx @@ -0,0 +1,73 @@ +// import { useState } from 'react'; + +// const useCardVariantManager = (initialCards = []) => { +// const [cards, setCards] = useState(initialCards); + +// // Function to identify the variant index +// const findVariantIndex = (cardId, setCode) => +// cards.findIndex( +// (card) => card.id === cardId && card.card_set.set_code === setCode +// ); + +// // Function to determine overlay based on rarity +// const getOverlayByRarity = (rarity) => { +// // Define overlays for different rarities +// const overlays = { +// Common: 'commonOverlay', +// Rare: 'rareOverlay', +// 'Ultra Rare': 'ultraRareOverlay', +// // ... other rarities +// }; +// return overlays[rarity] || 'defaultOverlay'; +// }; + +// // Function to update a specific variant field +// const updateVariantField = (cardId, setCode, field, value) => { +// const index = findVariantIndex(cardId, setCode); +// if (index < 0) return; // Variant not found + +// const updatedCards = [...cards]; +// updatedCards[index] = { +// ...updatedCards[index], +// [field]: value, +// // Update overlay based on rarity +// overlay: getOverlayByRarity(updatedCards[index].card_set.set_rarity), +// }; +// setCards(updatedCards); +// }; + +// // Function to add a new variant +// const addVariant = (cardId, newVariantData) => { +// const updatedCards = [...cards]; +// updatedCards.push({ +// ...newVariantData, +// id: cardId, +// // Assign overlay based on rarity +// overlay: getOverlayByRarity(newVariantData.card_set.set_rarity), +// }); +// setCards(updatedCards); +// }; + +// // Function to update nested fields like priceHistory for a specific variant +// const updateNestedField = (cardId, setCode, field, newValue) => { +// const index = findVariantIndex(cardId, setCode); +// if (index < 0) return; // Variant not found + +// const updatedCards = [...cards]; +// const variant = updatedCards[index]; +// updatedCards[index] = { +// ...variant, +// [field]: [...variant[field], newValue], +// }; +// setCards(updatedCards); +// }; + +// return { +// cards, +// addVariant, +// updateVariantField, +// updateNestedField, +// }; +// }; + +// export default useCardVariantManager; diff --git a/src/assets/animations/CardStormAnimation.jsx b/src/assets/currentlyUnused/CardStormAnimation.jsx similarity index 100% rename from src/assets/animations/CardStormAnimation.jsx rename to src/assets/currentlyUnused/CardStormAnimation.jsx diff --git a/src/context/MISC_CONTEXT/CombinedContext/CombinedProvider.jsx b/src/assets/currentlyUnused/CombinedContext/CombinedProvider.jsx similarity index 100% rename from src/context/MISC_CONTEXT/CombinedContext/CombinedProvider.jsx rename to src/assets/currentlyUnused/CombinedContext/CombinedProvider.jsx diff --git a/src/context/MISC_CONTEXT/CombinedContext/helpers.jsx b/src/assets/currentlyUnused/CombinedContext/helpers.jsx similarity index 100% rename from src/context/MISC_CONTEXT/CombinedContext/helpers.jsx rename to src/assets/currentlyUnused/CombinedContext/helpers.jsx diff --git a/src/assets/currentlyUnused/LoginForm.jsx b/src/assets/currentlyUnused/LoginForm.jsx new file mode 100644 index 0000000..68ab149 --- /dev/null +++ b/src/assets/currentlyUnused/LoginForm.jsx @@ -0,0 +1,36 @@ +// import React from 'react'; +// import LoginIcon from '@mui/icons-material/Login'; +// import PersonIcon from '@mui/icons-material/Person'; +// import LockIcon from '@mui/icons-material/Lock'; +// import RCZodForm from './reusable/RCZodForm'; + +// const loginFields = [ +// { +// name: 'username', +// label: 'Username', +// type: 'text', +// icon: , +// field: 'username', +// }, +// { +// name: 'password', +// label: 'Password', +// type: 'password', +// icon: , +// field: 'password', +// }, +// ]; + +// const LoginForm = () => { +// const startIcon = ; +// return ( +// +// ); +// }; + +// export default LoginForm; diff --git a/src/assets/currentlyUnused/PageContext/PageContext.jsx b/src/assets/currentlyUnused/PageContext/PageContext.jsx new file mode 100644 index 0000000..3a5b9f7 --- /dev/null +++ b/src/assets/currentlyUnused/PageContext/PageContext.jsx @@ -0,0 +1,67 @@ +// import React, { +// createContext, +// useState, +// useContext, +// useEffect, +// useMemo, +// } from 'react'; +// import LoadingIndicator from '../../../layout/LoadingIndicator'; +// import ErrorIndicator from '../../../layout/ErrorIndicator'; +// import SplashPage2 from '../../../layout/REUSABLE_COMPONENTS/SplashPage2'; +// // import useSnackBar from '../../hooks/useSnackBar'; +// import { defaultContextValue } from '../../constants'; +// import { useLoading } from '../../hooks/useLoading'; + +// const PageContext = createContext(defaultContextValue.PAGE_CONTEXT); + +// export const PageProvider = ({ children }) => { +// const { +// isLoading, +// isAnyLoading, +// startLoading, +// stopLoading, +// setError, +// error, +// clearLoading, +// } = useLoading(); +// const returnDisplay = () => { +// if (error) { +// return ; +// } +// if (isLoading('isPageLoading')) { +// return ; +// } else if (isAnyLoading()) { +// return ; +// } +// return null; +// }; +// const contextValue = useMemo( +// () => ({ +// startLoading, +// stopLoading, +// setError, +// error, +// isLoading, +// clearLoading, +// returnDisplay, +// // You can expose any additional functionalities from useLoading as needed. +// }), +// [ +// startLoading, +// stopLoading, +// setError, +// error, +// isLoading, +// clearLoading, +// returnDisplay, +// ] +// ); + +// return ( +// +// {children}{' '} +// +// ); +// }; + +// export const usePageContext = () => useContext(PageContext); diff --git a/src/assets/currentlyUnused/ProfileForm.jsx b/src/assets/currentlyUnused/ProfileForm.jsx new file mode 100644 index 0000000..4dd6dc3 --- /dev/null +++ b/src/assets/currentlyUnused/ProfileForm.jsx @@ -0,0 +1,75 @@ +// import React, { useState, useEffect } from 'react'; +// import PropTypes from 'prop-types'; +// import { TextField, Button } from '@mui/material'; +// import { useFormContext } from '../../context'; +// import FormField from './reusable/FormField'; + +// const ProfileForm = ({ userName, name, age, status, onSave }) => { +// const { forms, handleChange, handleSubmit } = useFormContext(); +// const profileValues = forms?.updateUserDataForm || {}; +// const formType = 'updateUserDataForm'; +// const handleFormSubmit = (event) => { +// event.preventDefault(); // Prevent the default form submission behavior +// handleSubmit(formType)(event); // Pass the event to your form handler +// }; + +// return ( +//
+// +// +// +// +// +// +// +// ); +// }; + +// ProfileForm.propTypes = { +// username: PropTypes.string, +// firstName: PropTypes.string, +// lastName: PropTypes.string, + +// age: PropTypes.string, +// status: PropTypes.string, +// onSave: PropTypes.func.isRequired, +// }; + +// export default ProfileForm; diff --git a/src/assets/currentlyUnused/SignupForm.jsx b/src/assets/currentlyUnused/SignupForm.jsx new file mode 100644 index 0000000..4bc256f --- /dev/null +++ b/src/assets/currentlyUnused/SignupForm.jsx @@ -0,0 +1,57 @@ +// import React from 'react'; +// import PersonAddIcon from '@mui/icons-material/PersonAdd'; +// import PersonIcon from '@mui/icons-material/Person'; +// import EmailIcon from '@mui/icons-material/Email'; +// import LockIcon from '@mui/icons-material/Lock'; +// import RCZodForm from './reusable/RCZodForm'; + +// const signupFields = [ +// { +// name: 'firstName', +// label: 'First Name', +// type: 'text', +// icon: , +// field: 'firstName', +// }, +// { +// name: 'lastName', +// label: 'Last Name', +// type: 'text', +// icon: , +// field: 'lastName', +// }, +// { +// name: 'email', +// label: 'Email', +// type: 'email', +// icon: , +// field: 'email', +// }, +// { +// name: 'username', +// label: 'Username', +// type: 'text', +// icon: , +// field: 'username', +// }, +// { +// name: 'password', +// label: 'Password', +// type: 'password', +// icon: , +// field: 'password', +// }, +// ]; + +// const SignupForm = () => { +// return ( +// } +// /> +// ); +// }; + +// export default SignupForm; diff --git a/src/assets/animations/SingleCardRowAnimation.jsx b/src/assets/currentlyUnused/SingleCardRowAnimation.jsx similarity index 100% rename from src/assets/animations/SingleCardRowAnimation.jsx rename to src/assets/currentlyUnused/SingleCardRowAnimation.jsx diff --git a/src/assets/currentlyUnused/SocketContext/SocketProvider.jsx b/src/assets/currentlyUnused/SocketContext/SocketProvider.jsx new file mode 100644 index 0000000..46cdcc2 --- /dev/null +++ b/src/assets/currentlyUnused/SocketContext/SocketProvider.jsx @@ -0,0 +1,40 @@ +// import React, { +// createContext, +// useContext, +// useEffect, +// useMemo, +// useState, +// } from 'react'; +// import io from 'socket.io-client'; + +// const SocketContext = createContext(); + +// export const SocketProvider = ({ children }) => { +// const [socket, setSocket] = useState(null); + +// useEffect(() => { +// const socketInstance = io( +// process.env.REACT_APP_SERVER || 'ws://localhost:3001', +// { +// transports: ['websocket'], +// } +// ); +// setSocket(socketInstance); + +// return () => { +// socketInstance.disconnect(); +// }; +// }, []); + +// return ( +// {children} +// ); +// }; + +// export const useSocketContext = () => { +// const context = useContext(SocketContext); +// if (context === undefined) { +// throw new Error('useSocketContext must be used within a SocketProvider'); +// } +// return context; +// }; diff --git a/src/assets/animations/SplashPage.js b/src/assets/currentlyUnused/SplashPage.js similarity index 100% rename from src/assets/animations/SplashPage.js rename to src/assets/currentlyUnused/SplashPage.js diff --git a/src/assets/currentlyUnused/TopCardsDisplay.jsx b/src/assets/currentlyUnused/TopCardsDisplay.jsx new file mode 100644 index 0000000..4fea8c6 --- /dev/null +++ b/src/assets/currentlyUnused/TopCardsDisplay.jsx @@ -0,0 +1,188 @@ +// import React, { useEffect, useRef, useState } from 'react'; +// import SwipeableViews from 'react-swipeable-views'; +// import { +// Box, +// Button, +// Container, +// Grid, +// MobileStepper, +// useMediaQuery, +// useTheme, +// } from '@mui/material'; +// import { KeyboardArrowLeft, KeyboardArrowRight } from '@mui/icons-material'; +// import { useCollectionStore } from '../../../context/MAIN_CONTEXT/CollectionContext/CollectionContext'; +// import { styled } from 'styled-components'; + +// import { MainContainer } from '../../../pages/pageStyles/StyledComponents'; +// import CarouselCard from '../../../components/cards/CarouselCard'; +// import LoadingIndicator from '../../../components/reusable/indicators/LoadingIndicator'; +// import { useMode } from '../../../context'; + +// const StyledStepper = styled(MobileStepper)(({ theme }) => ({ +// background: theme.palette.backgroundB.dark, +// border: `1px solid ${theme.palette.backgroundB.lighter}`, +// borderRadius: theme.shape.borderRadiusLarge, +// color: theme.palette.backgroundA.light, +// overflow: 'hidden', +// padding: theme.spacing(1), +// height: '100%', +// '@media (max-width: 600px)': { +// width: '100%', // Full width on mobile screens +// padding: theme.spacing(0.5), // Reduced padding on mobile +// }, +// })); + +// const StyledCardDetails = styled(Box)(({ theme }) => ({ +// display: 'flex', +// flexDirection: 'column', +// justifyContent: 'center', +// alignItems: 'center', +// })); + +// const StyledSwipeableView = styled(SwipeableViews)(({ theme }) => ({ +// '@media (max-width: 600px)': { +// width: '100%', // Full width on mobile screens +// overflow: 'hidden', // Hide overflow on mobile +// }, +// })); + +// const StyledContainer = styled(Container)(({ theme }) => ({ +// display: 'flex', +// flexDirection: 'column', +// // Set a maximum height to prevent expansion. Adjust according to your needs. +// maxHeight: '100vh', +// overflow: 'auto', // Add scroll if content exceeds container size +// alignItems: 'center', +// background: theme.palette.backgroundB.darker, +// borderRadius: theme.shape.borderRadiusLarge, +// padding: theme.spacing(3), +// color: '#fff', +// '@media (max-width: 600px)': { +// padding: theme.spacing(1), // Reduced padding on mobile +// }, +// })); + +// // Assuming SwipeableViews and CarouselCard do not need custom styling here +// // If they do, use styled in a similar manner + +// const TopCardsDisplay = () => { +// const { theme } = useMode(); +// const theme2 = useTheme(); +// const { selectedCollection } = useCollectionStore(); +// const [top5Cards, setTop5Cards] = useState([]); +// const [activeStep, setActiveStep] = useState(0); +// const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + +// useEffect(() => { +// const sortedCards = selectedCollection?.cards +// ?.map((card) => { +// const latestPrice = card?.latestPrice?.num ?? 0; +// const lastSavedPrice = card?.lastSavedPrice?.num ?? 0; +// if (latestPrice === undefined) { +// console.warn(`Price missing for card: ${card.name}`); +// return { ...card, diff: 0 }; +// } +// return { ...card, diff: Math.abs(latestPrice - lastSavedPrice) }; +// }) +// .sort((a, b) => b.diff - a.diff || b.price - a.price) +// .slice(0, 5); +// setTop5Cards(sortedCards); +// }, [selectedCollection]); + +// const maxSteps = top5Cards?.length; +// const handleNext = () => setActiveStep((prev) => prev + 1); +// const handleBack = () => setActiveStep((prev) => prev - 1); +// const isSmall = useMediaQuery(theme2.breakpoints.down('sm')); +// if (!selectedCollection) { +// return ( +// +// +// +// ); +// } + +// return ( +// +// {/* */} +// +// +// +// {top5Cards?.map((card, index) => ( +// +// +// +// ))} +// +// +// {theme.direction === 'rtl' ? ( +// +// ) : ( +// +// )}{' '} +// Next +// +// } +// backButton={ +// +// } +// /> +// +// +// {/* */} +// +// ); +// }; + +// export default TopCardsDisplay; diff --git a/src/assets/currentlyUnused/UpdateCollectionForm.jsx b/src/assets/currentlyUnused/UpdateCollectionForm.jsx new file mode 100644 index 0000000..fdf6970 --- /dev/null +++ b/src/assets/currentlyUnused/UpdateCollectionForm.jsx @@ -0,0 +1,55 @@ +// import React, { useEffect } from 'react'; +// import { useFormContext, useMode } from '../../context'; +// import useSnackbarManager from '../../context/hooks/useSnackbarManager'; +// import RCZodForm from './reusable/RCZodForm'; + +// const updateCollectionFields = [ +// { +// name: 'name', +// label: 'Name', +// type: 'text', +// required: true, +// }, +// { +// name: 'description', +// label: 'Description', +// type: 'text', +// required: true, +// multiline: true, +// rows: 4, +// }, +// ]; + +// const UpdateCollectionForm = ({ collectionData }) => { +// const { setFormSchema, onSubmit } = useFormContext(); +// const { theme } = useMode(); +// const { showSuccess, showError } = useSnackbarManager(); + +// useEffect(() => { +// if (collectionData) { +// setFormSchema('updateCollectionForm', collectionData); +// } +// }, [collectionData, setFormSchema]); + +// const handleFormSubmit = async (data) => { +// try { +// await onSubmit(data, 'updateCollectionForm', collectionData?._id); +// showSuccess("You've successfully updated the collection."); +// } catch (error) { +// showError('Failed to update collection. Please try again.'); +// } +// }; + +// return ( +// +// ); +// }; + +// export default UpdateCollectionForm; diff --git a/src/assets/currentlyUnused/UpdateDeckForm.jsx b/src/assets/currentlyUnused/UpdateDeckForm.jsx new file mode 100644 index 0000000..6d4fbcf --- /dev/null +++ b/src/assets/currentlyUnused/UpdateDeckForm.jsx @@ -0,0 +1,65 @@ +// import React from 'react'; +// import RCZodForm from './RCZodForm'; +// import SaveIcon from '@mui/icons-material/Save'; +// import DeleteIcon from '@mui/icons-material/Delete'; +// import { useFormContext } from '../../context'; + +// // Assume the schema is defined elsewhere and imported here +// // import { updateDeckFormSchema } from './yourSchemaDefinitions'; + +// const updateDeckFields = [ +// { +// name: 'name', +// label: 'Name', +// type: 'text', +// icon: null, // No icons used for these fields +// }, +// { +// name: 'description', +// label: 'Description', +// type: 'text', +// multiline: true, +// rows: 4, +// icon: null, +// }, +// ]; + +// const UpdateDeckForm = ({ selectedDeck }) => { +// const { onSubmit } = useFormContext(); // Assuming useFormContext provides onSubmit + +// // Placeholder for the submission logic within the component or from props +// const handleSubmit = (data) => { +// console.log('Submitting update deck data:', data); +// onSubmit(data, 'updateDeckForm'); // This should align with your context action +// }; + +// // Placeholder for the deletion logic within the component or from props +// const handleDelete = () => { +// console.log('Deleting deck:', selectedDeck._id); +// // Here you would call a context function or similar to delete the deck +// onSubmit({ _id: selectedDeck._id, delete: true }, 'updateDeckForm'); +// }; + +// return ( +// } +// additionalButtons={[ +// { +// label: 'Delete Deck', +// onClick: handleDelete, +// startIcon: , +// color: 'error', +// variant: 'contained', +// disabled: !selectedDeck, +// }, +// ]} +// defaultValues={selectedDeck} // Assuming this prop is structured correctly for the form +// /> +// ); +// }; + +// export default UpdateDeckForm; diff --git a/src/assets/currentlyUnused/collectionUtility.jsx b/src/assets/currentlyUnused/collectionUtility.jsx new file mode 100644 index 0000000..5c22c81 --- /dev/null +++ b/src/assets/currentlyUnused/collectionUtility.jsx @@ -0,0 +1,21 @@ +// /* eslint-disable @typescript-eslint/no-empty-function */ +// export const calculateCollectionValue = (collection) => { +// if (!collection) return 0; + +// const cards = collection?.cards; + +// if (!Array.isArray(cards)) { +// console.warn('Invalid collection format', collection, cards); +// return 0; +// } + +// return cards.reduce((totalValue, card) => { +// const cardPrice = card?.price || 0; +// const cardQuantity = card?.quantity || 0; +// return totalValue + cardPrice * cardQuantity; +// }, 0); +// }; + +// export const shouldFetchCollections = (prevUserId, currentUserId) => { +// return prevUserId !== currentUserId && currentUserId != null; +// }; diff --git a/src/assets/themes/colors/Azure.jsx b/src/assets/currentlyUnused/colors/Azure.jsx similarity index 100% rename from src/assets/themes/colors/Azure.jsx rename to src/assets/currentlyUnused/colors/Azure.jsx diff --git a/src/assets/themes/colors/Cyan.jsx b/src/assets/currentlyUnused/colors/Cyan.jsx similarity index 100% rename from src/assets/themes/colors/Cyan.jsx rename to src/assets/currentlyUnused/colors/Cyan.jsx diff --git a/src/assets/themes/colors/Dark.jsx b/src/assets/currentlyUnused/colors/Dark.jsx similarity index 100% rename from src/assets/themes/colors/Dark.jsx rename to src/assets/currentlyUnused/colors/Dark.jsx diff --git a/src/assets/themes/colors/DeepPurple.jsx b/src/assets/currentlyUnused/colors/DeepPurple.jsx similarity index 100% rename from src/assets/themes/colors/DeepPurple.jsx rename to src/assets/currentlyUnused/colors/DeepPurple.jsx diff --git a/src/assets/themes/colors/Default.jsx b/src/assets/currentlyUnused/colors/Default.jsx similarity index 100% rename from src/assets/themes/colors/Default.jsx rename to src/assets/currentlyUnused/colors/Default.jsx diff --git a/src/assets/themes/colors/Indigo.jsx b/src/assets/currentlyUnused/colors/Indigo.jsx similarity index 100% rename from src/assets/themes/colors/Indigo.jsx rename to src/assets/currentlyUnused/colors/Indigo.jsx diff --git a/src/assets/themes/colors/Light.jsx b/src/assets/currentlyUnused/colors/Light.jsx similarity index 100% rename from src/assets/themes/colors/Light.jsx rename to src/assets/currentlyUnused/colors/Light.jsx diff --git a/src/assets/themes/colors/LightBlue.jsx b/src/assets/currentlyUnused/colors/LightBlue.jsx similarity index 100% rename from src/assets/themes/colors/LightBlue.jsx rename to src/assets/currentlyUnused/colors/LightBlue.jsx diff --git a/src/context/MAIN_CONTEXT/CollectionContext/helpers.jsx b/src/assets/currentlyUnused/helpers.jsx similarity index 100% rename from src/context/MAIN_CONTEXT/CollectionContext/helpers.jsx rename to src/assets/currentlyUnused/helpers.jsx diff --git a/src/assets/currentlyUnused/menuItemsData.jsx b/src/assets/currentlyUnused/menuItemsData.jsx new file mode 100644 index 0000000..fc5a409 --- /dev/null +++ b/src/assets/currentlyUnused/menuItemsData.jsx @@ -0,0 +1,137 @@ +// import React, { useCallback, useState } from 'react'; +// import { +// Home as HomeIcon, +// Store as StoreIcon, +// ShoppingCart as CartIcon, +// Assessment as CollectionIcon, +// Person as ProfileIcon, +// } from '@mui/icons-material'; +// import DeckBuilderIcon from '../REUSABLE_COMPONENTS/icons/DeckBuilderIcon'; +// import { Badge } from '@mui/material'; +// import { useCartStore } from '../../context/MAIN_CONTEXT/CartContext/CartContext'; +// import { IconButton } from '@mui/joy'; +// import MenuIcon from '@mui/icons-material/Menu'; + +// // const iconWrapper = (icon) => { +// // return ( +// // +// // {icon} +// // +// // ); +// // }; +// export const getMenuItemsData = ({ +// isLoggedIn, +// cartCardQuantity, +// // iconColor, +// // isOpen, +// // setIsOpen, +// }) => { +// // const { cartCardQuantity } = useCartStore(); +// // const [isOpen, setIsOpen] = useState(false); // Manage open state locally +// // const toggleSidebar = useCallback(() => setIsOpen(!isOpen), [isOpen]); +// // const iconColor = isMobileView ? theme.palette.primary.main : 'white'; + +// try { +// const baseMenuItems = [ +// { +// name: 'Menu', +// icon: , +// to: '/', +// requiresLogin: false, +// }, +// { name: 'Home', icon: , to: '/home', requiresLogin: false }, +// { +// name: 'Deck', +// // icon: , +// icon: , +// to: '/deckbuilder', +// requiresLogin: false, +// }, +// { +// name: 'Collection', +// icon: , +// to: '/collection', +// requiresLogin: !isLoggedIn, +// }, +// { +// name: 'Store', +// icon: , +// to: '/store', +// requiresLogin: !isLoggedIn, +// }, +// { +// name: 'Cart', +// icon: ( +// +// +// +// ), +// to: '/cart', +// requiresLogin: !isLoggedIn, +// }, +// { +// name: 'Profile', +// icon: , +// to: '/profile', +// requiresLogin: isLoggedIn, +// }, +// // { +// // name: 'Login', +// // icon: , +// // to: '/login', +// // requiresLogin: false, +// // }, +// ]; + +// if (isLoggedIn) { +// return baseMenuItems.map((item) => ({ +// ...item, +// requiresLogin: false, +// })); +// } + +// // Processed menu items +// const processedMenuItems = isLoggedIn +// ? baseMenuItems.map((item) => ({ ...item, requiresLogin: false })) +// : baseMenuItems; + +// return { +// baseMenuItems, +// menuItems: processedMenuItems, +// }; +// } catch (error) { +// console.error('An error occurred in getMenuItemsData:', error); +// return { +// baseMenuItems: [], +// menuItems: [], +// }; +// } +// }; + +// export default getMenuItemsData; diff --git a/src/assets/currentlyUnused/useCollectionStats.jsx b/src/assets/currentlyUnused/useCollectionStats.jsx new file mode 100644 index 0000000..d4de332 --- /dev/null +++ b/src/assets/currentlyUnused/useCollectionStats.jsx @@ -0,0 +1,84 @@ +// import { useEffect, useState } from 'react'; +// import useSelectedCollection from './useSelectedCollection'; + +// const useCollectionStats = () => { +// const { allCollections } = useSelectedCollection(); +// const [collectionStats, setCollectionStats] = useState({}); +// const [metaStats, setMetaStats] = useState({}); +// const totals = []; +// const quantities = []; +// useEffect(() => { +// const stats = {}; +// for (const collection of allCollections) { +// const { +// totalPrice, +// totalQuantity, // Fixed typo from 'toalQuantity' to 'totalQuantity' +// nivoChartData, +// newNivoChartData, +// nivoTestData, +// averagedChartData, +// chartData, +// muiChartData, +// name, +// descriptions, +// lastUpdated, +// collectionPriceHistory, +// dailyCollectionPriceHistory, +// createdAt, +// collectionStatistics, +// id, // Assuming 'id' is available in 'collection' for mapping +// } = collection; + +// const { avgPrice, highPoint, lowPoint, percentageChange, priceChange } = +// collectionStatistics; + +// totals.push(totalPrice); +// quantities.push(totalQuantity); + +// stats[collection.id] = { +// totalPrice, +// totalQuantity, +// nivoChartData, +// newNivoChartData, +// nivoTestData, +// averagedChartData, +// chartData, +// muiChartData, +// name, +// descriptions, +// lastUpdated, +// collectionPriceHistory, +// dailyCollectionPriceHistory, +// createdAt, +// avgPrice, +// highPoint, +// lowPoint, +// percentageChange, +// priceChange, +// collectionStatistics, +// }; +// } + +// setCollectionStats(stats); +// console.log('COLLECTION STATS RECORDED: ', stats); +// }, []); // Dependency array ensures this effect runs only when allCollections changes + +// const calculateAndSetMetaStats = () => { +// const metaStats = {}; +// metaStats.totalValue = totals.reduce((acc, total) => acc + total, 0); +// metaStats.totalQuantity = quantities.reduce( +// (acc, quantity) => acc + quantity, +// 0 +// ); +// setMetaStats(metaStats); +// console.log('META STATS RECORDED: ', metaStats); +// return metaStats; +// }; + +// useEffect(() => { +// calculateAndSetMetaStats(); +// }, []); +// return { collectionStats, metaStats }; +// }; + +// export default useCollectionStats; diff --git a/src/assets/themes/GlobalStyles.js b/src/assets/themes/GlobalStyles.js index cd518fd..1c12c75 100644 --- a/src/assets/themes/GlobalStyles.js +++ b/src/assets/themes/GlobalStyles.js @@ -24,10 +24,6 @@ const MyGlobalStyles = () => ( textDecoration: 'none', // remove underline from all links color: 'inherit', // links will inherit their parent color }, - // '*::-webkit-scrollbar': { - // visibility: 'hidden', - // // width: '8px', - // }, '*::-webkit-scrollbar-track': { backgroundColor: 'gray.200', }, @@ -35,29 +31,6 @@ const MyGlobalStyles = () => ( backgroundColor: 'gray.500', borderRadius: '20px', }, - // h1: { - // fontSize: '2.5rem', // styling for h1 tags - // fontWeight: 'bold', - // }, - // h2: { - // fontSize: '2rem', // styling for h2 tags - // fontWeight: 'bold', - // }, - // h3: { - // fontSize: '1.75rem', // styling for h3 tags - // }, - // h4: { - // fontSize: '1.5rem', // styling for h4 tags - // }, - // h5: { - // fontSize: '1.25rem', // styling for h5 tags - // }, - // h6: { - // fontSize: '1rem', // styling for h6 tags - // }, - // p: { - // fontSize: '1rem', // styling for p tags - // }, }} /> ); diff --git a/src/assets/themes/base/colors.jsx b/src/assets/themes/base/colors.jsx index 3bd6619..3bd79c6 100644 --- a/src/assets/themes/base/colors.jsx +++ b/src/assets/themes/base/colors.jsx @@ -15,9 +15,15 @@ import { text, divider, action, + primary, + secondary, } from './customColorPalettes'; const colors = { chartTheme: chartTheme, + background: { + default: '#f0f2f5', + paper: '#fff', + }, // PRIMARY COLORS backgroundA, backgroundD: { @@ -67,9 +73,6 @@ const colors = { qcr: hexToRgba(rarity.qcr, 0.5), // Add more rarities as needed }, - background: { - default: '#f0f2f5', - }, // TEXT COLORS text, // DIVIDER COLORS @@ -117,20 +120,9 @@ const colors = { focus: '#05386B', }, - primary: { - main: '#5CDB95', - focus: '#379683', - }, + primary, - // primary: { - // main: '#e91e63', - // focus: '#e91e63', - // }, - - secondary: { - main: '#7b809a', - focus: '#8f93a9', - }, + secondary, light: { main: '#f0f2f5', focus: '#f0f2f5', @@ -182,6 +174,9 @@ const colors = { error: { main: '#EF5350', state: '#E53935', + dark: '#C62828', + darker: '#D32F2F', + evenDarker: '#DC143C', }, light: { diff --git a/src/assets/themes/base/customColorPalettes.jsx b/src/assets/themes/base/customColorPalettes.jsx index 931a134..21ca42f 100644 --- a/src/assets/themes/base/customColorPalettes.jsx +++ b/src/assets/themes/base/customColorPalettes.jsx @@ -1,8 +1,10 @@ import hexToRgba from '../functions/hexToRgba'; const error = { + light: '#e57373', main: '#f44336', focus: '#f44336', dark: '#d32f2f', + darkest: '#7f2e2eff', // --persian-plum-- contrastText: '#fff', hoverContrastText: '#111', }; @@ -112,7 +114,6 @@ const backgroundF = { lighter: hexToRgba(backgroundE.lighter, 0.4), lightest: hexToRgba(backgroundE.lightest, 0.3), }; - const backgroundG = { darkest: '#073b4cff', // --midnight-green-- darker: '#0c637fff', // --cerulean-- @@ -133,7 +134,6 @@ const backgroundGSecondary = { lightest: '#facbb0ff', // --navajo-white-- contrastText: '#fff', }; - const grey = { darkest: '#141414', darkert: '#292929', @@ -145,18 +145,23 @@ const grey = { evenLighter: '#c2c2c2', contrastText: '#e0e0e0', }; - const primary = { darkest: '#040509', darker: '#040509', dark: '#040509', default: '#f2f0f0', + main: '#5CDB95', + focus: '#379683', light: '#141b2d', lighter: '#1F2A40', lightest: '#727681', - evenLighter: '#a1a4ab', + evenLighter: '#8c8c8c', contrastText: '#e0e0e0', }; +const secondary = { + main: '#8c8c8c', + focus: '#8f93a9', +}; const greenAccent = { darkest: '#0f2922', // Assuming this is the darkest @@ -169,7 +174,6 @@ const greenAccent = { evenLighter: '#b7ebde', // Even lighter than the lightest contrastText: '#dbf5ee', // Most contrasting or lightest, could be adjusted }; - const redAccent = { darkest: '#2c100f', darker: '#58201e', @@ -181,7 +185,6 @@ const redAccent = { evenLighter: '#f1b9b7', contrastText: '#f8dcdb', }; - const blueAccent = { darkest: '#151632', darker: '#2a2d64', @@ -193,7 +196,6 @@ const blueAccent = { evenLighter: '#c3c6fd', contrastText: '#e1e2fe', }; - const chartTheme = { primary, grey, @@ -201,7 +203,6 @@ const chartTheme = { redAccent, blueAccent, }; - const rarity = { common: '#C0C0C0', // Silver uncommon: '#B8860B', // DarkGoldenRod @@ -218,7 +219,6 @@ const rarity = { qcr: '#FF4500', // OrangeRed // Add more rarities as needed }; - export { chartTheme, backgroundA, @@ -237,6 +237,8 @@ export { text, divider, action, + primary, + secondary, }; // error: { // main: colorsA.redAccent[500], diff --git a/src/assets/themes/base/typography.jsx b/src/assets/themes/base/typography.jsx index 846a45e..7bd293d 100644 --- a/src/assets/themes/base/typography.jsx +++ b/src/assets/themes/base/typography.jsx @@ -5,32 +5,16 @@ import colors from './colors'; const { dark } = colors; -// const baseProperties = { -// fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', -// fontWeightLighter: 100, -// fontWeightLight: 300, -// fontWeightRegular: 400, -// fontWeightMedium: 600, -// fontWeightBold: 700, -// fontSizeXXS: pxToRem(10.4), -// fontSizeXS: pxToRem(12), -// fontSizeSM: pxToRem(14), -// fontSizeMD: pxToRem(16), -// fontSizeLG: pxToRem(18), -// fontSizeXL: pxToRem(20), -// fontSize2XL: pxToRem(24), -// fontSize3XL: pxToRem(30), -// }; const baseProperties = { - // fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', fontFamily: 'Poppins, sans-serif', fontWeightLighter: 100, fontWeightLight: 300, fontWeightRegular: 400, fontWeightMedium: 600, fontWeightBold: 700, - // Define font sizes as functions to include responsive behavior - fontSizeXXS: () => `clamp(${pxToRem(8)}, 1vw, ${pxToRem(10)})`, + + fontSize3XS: () => `clamp(${pxToRem(6)}, 1vw, ${pxToRem(8)})`, + fontSize2XS: () => `clamp(${pxToRem(8)}, 1vw, ${pxToRem(10)})`, fontSizeXS: () => `clamp(${pxToRem(10)}, 1.25vw, ${pxToRem(12)})`, fontSizeSM: () => `clamp(${pxToRem(12)}, 1.5vw, ${pxToRem(14)})`, fontSizeMD: () => `clamp(${pxToRem(14)}, 1.75vw, ${pxToRem(16)})`, @@ -38,17 +22,15 @@ const baseProperties = { fontSizeXL: () => `clamp(${pxToRem(18)}, 2.25vw, ${pxToRem(20)})`, fontSize2XL: () => `clamp(${pxToRem(20)}, 2.5vw, ${pxToRem(24)})`, fontSize3XL: () => `clamp(${pxToRem(24)}, 2.75vw, ${pxToRem(30)})`, + fontSize4XL: () => `clamp(${pxToRem(30)}, 3vw, ${pxToRem(36)})`, + fontSize5XL: () => `clamp(${pxToRem(36)}, 3.25vw, ${pxToRem(42)})`, + fontSize6XL: () => `clamp(${pxToRem(42)}, 3.5vw, ${pxToRem(48)})`, }; -// const baseHeadingProperties = { -// fontFamily: baseProperties.fontFamily, -// color: dark.main, -// fontWeight: baseProperties.fontWeightBold, -// }; + const baseHeadingProperties = { fontFamily: baseProperties.fontFamily, color: dark.main, fontWeight: baseProperties.fontWeightBold, - // Include responsive font sizes in base properties fontSize: baseProperties.fontSizeXL(), // Call the function for default size '@media (max-width:600px)': { fontSize: baseProperties.fontSizeMD(), // Adjust size for smaller screens @@ -72,53 +54,76 @@ const typography = { h1: { ...baseHeadingProperties, - fontSize: baseProperties.fontSize3XL(), + fontSize: baseProperties.fontSize6XL(), '@media (max-width:900px)': { - fontSize: baseProperties.fontSize2XL(), + fontSize: baseProperties.fontSize5XL(), }, }, h2: { - fontSize: baseProperties.fontSize2XL(), ...baseHeadingProperties, + fontSize: baseProperties.fontSize5XL(), + '@media (max-width:900px)': { + fontSize: baseProperties.fontSize4XL(), + }, }, h3: { - fontSize: baseProperties.fontSizeXL(), ...baseHeadingProperties, + fontSize: baseProperties.fontSize4XL(), '@media (max-width:600px)': { - fontSize: baseProperties.fontSizeLG(), + fontSize: baseProperties.fontSize3XL(), }, }, - // h2: { - // fontSize: pxToRem(36), - // lineHeight: 1.3, - // ...baseHeadingProperties, - // }, - - // h3: { - // fontSize: pxToRem(30), - // lineHeight: 1.375, - // ...baseHeadingProperties, - // }, - h4: { - fontSize: pxToRem(24), - lineHeight: 1.375, ...baseHeadingProperties, + fontSize: baseProperties.fontSize3XL(), + '@media (max-width:600px)': { + fontSize: baseProperties.fontSize2XL(), + }, }, h5: { - fontSize: pxToRem(20), - lineHeight: 1.375, ...baseHeadingProperties, + fontSize: baseProperties.fontSize2XL(), + '@media (max-width:600px)': { + fontSize: baseProperties.fontSizeXL(), + }, }, h6: { - fontSize: pxToRem(16), - lineHeight: 1.625, ...baseHeadingProperties, + fontSize: baseProperties.fontSizeXL(), + '@media (max-width:600px)': { + fontSize: baseProperties.fontSizeLG(), + }, + }, + + p1: { + fontFamily: baseProperties.fontFamily, + fontWeight: baseProperties.fontWeightLight, + fontSize: baseProperties.fontSizeSM(), + lineHeight: 1.5, + '@media (max-width:600px)': { + fontSize: baseProperties.fontSizeXS(), + }, + '@media (max-width:900px)': { + fontSize: baseProperties.fontSizeSM(), + }, + }, + + p2: { + fontFamily: baseProperties.fontFamily, + fontWeight: baseProperties.fontWeightLight, + fontSize: baseProperties.fontSizeSM(), + lineHeight: 1.5, + '@media (max-width:600px)': { + fontSize: baseProperties.fontSizeXS(), + }, + '@media (max-width:900px)': { + fontSize: baseProperties.fontSizeSM(), + }, }, subtitle1: { @@ -210,10 +215,146 @@ const typography = { }, lineHeight: { - sm: 1.25, - md: 1.5, + xs: 1.25, + sm: 1.5, + md: 1.75, lg: 2, + xl: 2, }, }; export default typography; + +// /** +// * Typography used in theme +// * @param {JsonObject} theme theme customization object +// */ + +// export default function themeTypography(theme) { +// return { +// fontFamily: theme?.customization?.fontFamily, +// h6: { +// fontWeight: 500, +// color: theme.heading, +// fontSize: '0.75rem', +// }, +// h5: { +// fontSize: '0.875rem', +// color: theme.heading, +// fontWeight: 500, +// }, +// h4: { +// fontSize: '1rem', +// color: theme.heading, +// fontWeight: 600, +// }, +// h3: { +// fontSize: '1.25rem', +// color: theme.heading, +// fontWeight: 600, +// }, +// h2: { +// fontSize: '1.5rem', +// color: theme.heading, +// fontWeight: 700, +// }, +// h1: { +// fontSize: '2.125rem', +// color: theme.heading, +// fontWeight: 700, +// }, +// subtitle1: { +// fontSize: '0.875rem', +// fontWeight: 500, +// color: theme.textDark, +// }, +// subtitle2: { +// fontSize: '0.75rem', +// fontWeight: 400, +// color: theme.darkTextSecondary, +// }, +// caption: { +// fontSize: '0.75rem', +// color: theme.darkTextSecondary, +// fontWeight: 400, +// }, +// body1: { +// fontSize: '0.875rem', +// fontWeight: 400, +// lineHeight: '1.334em', +// }, +// body2: { +// letterSpacing: '0em', +// fontWeight: 400, +// lineHeight: '1.5em', +// color: theme.darkTextPrimary, +// }, +// button: { +// textTransform: 'capitalize', +// }, +// customInput: { +// marginTop: 1, +// marginBottom: 1, +// '& > label': { +// top: 23, +// left: 0, +// color: theme.grey500, +// '&[data-shrink="false"]': { +// top: 5, +// }, +// }, +// '& > div > input': { +// padding: '30.5px 14px 11.5px !important', +// }, +// '& legend': { +// display: 'none', +// }, +// '& fieldset': { +// top: 0, +// }, +// }, +// mainContent: { +// backgroundColor: theme.background, +// width: '100%', +// minHeight: 'calc(100vh - 88px)', +// flexGrow: 1, +// padding: '20px', +// marginTop: '88px', +// marginRight: '20px', +// borderRadius: `${theme?.customization?.borderRadius}px`, +// }, +// menuCaption: { +// fontSize: '0.875rem', +// fontWeight: 500, +// color: theme.heading, +// padding: '6px', +// textTransform: 'capitalize', +// marginTop: '10px', +// }, +// subMenuCaption: { +// fontSize: '0.6875rem', +// fontWeight: 500, +// color: theme.darkTextSecondary, +// textTransform: 'capitalize', +// }, +// commonAvatar: { +// cursor: 'pointer', +// borderRadius: '8px', +// }, +// smallAvatar: { +// width: '22px', +// height: '22px', +// fontSize: '1rem', +// }, +// mediumAvatar: { +// width: '34px', +// height: '34px', +// fontSize: '1.2rem', +// }, +// largeAvatar: { +// width: '44px', +// height: '44px', +// fontSize: '1.5rem', +// }, +// }; +// } diff --git a/src/assets/themes/components/index.jsx b/src/assets/themes/components/index.jsx index d4f730c..faf74cc 100644 --- a/src/assets/themes/components/index.jsx +++ b/src/assets/themes/components/index.jsx @@ -20,83 +20,10 @@ const cssbaseline = { `, }; -// const container = { -// styleOverrides: { -// root: { -// margin: 0, -// padding: 0, -// }, -// }, -// }; - -const listItemButton = { - styleOverrides: { - root: { - color: '#4cceac', - paddingTop: '10px', - paddingBottom: '10px', - '&.Mui-selected': { - color: '#3da58a', - backgroundColor: '#4cceac', - '&:hover': { - backgroundColor: '#3da58a', - }, - '& .MuiListItemIcon-root': { - color: '#3da58a', - }, - }, - '&:hover': { - backgroundColor: '#3da58a', - color: '#3da58a', - '& .MuiListItemIcon-root': { - color: '#3da58a', - }, - }, - }, - }, -}; - -const outlinedInput = { - styleOverrides: { - root: { - background: '#ffffff', - borderRadius: '4px', - '& .MuiOutlinedInput-notchedOutline': { - borderColor: '#333', - }, - '&:hover $notchedOutline': { - borderColor: '#4cceac', - }, - '&.MuiInputBase-multiline': { - padding: '1px', - }, - }, - - input: { - fontWeight: 500, - background: '#ffffff', - padding: '15.5px 14px', - borderRadius: '4px', - '&.MuiInputBase-inputSizeSmall': { - padding: '10px 14px', - '&.MuiInputBase-inputAdornedStart': { - paddingLeft: 0, - }, - }, - }, - inputAdornedStart: { - paddingLeft: '4px', - }, - notchedOutline: { - borderRadius: '4px', - }, - }, -}; - export const components = { MuiCssBaseline: cssbaseline, MuiButton: button, - MuiListItemButton: listItemButton, + // MuiListItemButton: listItemButton, MuiContainer: container, MuiDivider: divider, @@ -104,7 +31,7 @@ export const components = { MuiTableCell: tableCell, MuiTableHead: tableHead, - MuiOutlinedInput: outlinedInput, + // MuiOutlinedInput: outlinedInput, MuiInput: input, MuiInputLabel: inputLabel, @@ -114,64 +41,3 @@ export const components = { }; export default components; -// MuiListItemButton: { -// styleOverrides: { -// root: { -// color: colorsA.greenAccent[500], -// paddingTop: '10px', -// paddingBottom: '10px', -// '&.Mui-selected': { -// color: colorsA.greenAccent[200], -// backgroundColor: colorsA.greenAccent[300], -// '&:hover': { -// backgroundColor: colorsA.greenAccent[700], -// }, -// '& .MuiListItemIcon-root': { -// color: colorsA.greenAccent[400], -// }, -// }, -// '&:hover': { -// backgroundColor: colorsA.greenAccent[400], -// color: colorsA.greenAccent[200], -// '& .MuiListItemIcon-root': { -// color: colors.greenAccent[400], -// }, -// }, -// }, -// }, -// }, -// MuiOutlinedInput: { -// styleOverrides: { -// root: { -// background: backgroundA.default, -// borderRadius: `${themeColors?.borderRadius}px`, -// '& .MuiOutlinedInput-notchedOutline': { -// borderColor: themeColors.colors?.grey400, -// }, -// '&:hover $notchedOutline': { -// borderColor: themeColors?.primaryLight, -// }, -// '&.MuiInputBase-multiline': { -// padding: 1, -// }, -// }, -// input: { -// fontWeight: 500, -// background: backgroundA.default, -// padding: '15.5px 14px', -// borderRadius: `${themeColors?.borderRadius}px`, -// '&.MuiInputBase-inputSizeSmall': { -// padding: '10px 14px', -// '&.MuiInputBase-inputAdornedStart': { -// paddingLeft: 0, -// }, -// }, -// }, -// inputAdornedStart: { -// paddingLeft: 4, -// }, -// notchedOutline: { -// borderRadius: `${themeColors?.customization?.borderRadius}px`, -// }, -// }, -// }, diff --git a/src/assets/themes/styleOverride/compStyleOverride.jsx b/src/assets/themes/styleOverride/compStyleOverride.jsx deleted file mode 100644 index e5b65ab..0000000 --- a/src/assets/themes/styleOverride/compStyleOverride.jsx +++ /dev/null @@ -1,187 +0,0 @@ -export default function componentStyleOverrides(theme) { - const bgColor = theme.colors?.grey50; - return { - MuiButton: { - styleOverrides: { - root: { - fontWeight: 500, - borderRadius: '4px', - }, - }, - }, - MuiPaper: { - defaultProps: { - elevation: 0, - }, - styleOverrides: { - root: { - backgroundImage: 'none', - }, - rounded: { - borderRadius: `${theme?.customization?.borderRadius}px`, - }, - }, - }, - MuiCardHeader: { - styleOverrides: { - root: { - color: theme.colors?.textDark, - padding: '24px', - }, - title: { - fontSize: '1.125rem', - }, - }, - }, - MuiCardContent: { - styleOverrides: { - root: { - padding: '24px', - }, - }, - }, - MuiCardActions: { - styleOverrides: { - root: { - padding: '24px', - }, - }, - }, - MuiListItemButton: { - styleOverrides: { - root: { - color: theme.darkTextPrimary, - paddingTop: '10px', - paddingBottom: '10px', - '&.Mui-selected': { - color: theme.menuSelected, - backgroundColor: theme.menuSelectedBack, - '&:hover': { - backgroundColor: theme.menuSelectedBack, - }, - '& .MuiListItemIcon-root': { - color: theme.menuSelected, - }, - }, - '&:hover': { - backgroundColor: theme.menuSelectedBack, - color: theme.menuSelected, - '& .MuiListItemIcon-root': { - color: theme.menuSelected, - }, - }, - }, - }, - }, - MuiListItemIcon: { - styleOverrides: { - root: { - color: theme.darkTextPrimary, - minWidth: '36px', - }, - }, - }, - MuiListItemText: { - styleOverrides: { - primary: { - color: theme.textDark, - }, - }, - }, - MuiInputBase: { - styleOverrides: { - input: { - color: theme.textDark, - '&::placeholder': { - color: theme.darkTextSecondary, - fontSize: '0.875rem', - }, - }, - }, - }, - MuiOutlinedInput: { - styleOverrides: { - root: { - background: bgColor, - borderRadius: `${theme?.customization?.borderRadius}px`, - '& .MuiOutlinedInput-notchedOutline': { - borderColor: theme.colors?.grey400, - }, - '&:hover $notchedOutline': { - borderColor: theme.colors?.primaryLight, - }, - '&.MuiInputBase-multiline': { - padding: 1, - }, - }, - input: { - fontWeight: 500, - background: bgColor, - padding: '15.5px 14px', - borderRadius: `${theme?.customization?.borderRadius}px`, - '&.MuiInputBase-inputSizeSmall': { - padding: '10px 14px', - '&.MuiInputBase-inputAdornedStart': { - paddingLeft: 0, - }, - }, - }, - inputAdornedStart: { - paddingLeft: 4, - }, - notchedOutline: { - borderRadius: `${theme?.customization?.borderRadius}px`, - }, - }, - }, - MuiSlider: { - styleOverrides: { - root: { - '&.Mui-disabled': { - color: theme.colors?.grey300, - }, - }, - mark: { - backgroundColor: theme.paper, - width: '4px', - }, - valueLabel: { - color: theme?.colors?.primaryLight, - }, - }, - }, - MuiDivider: { - styleOverrides: { - root: { - borderColor: theme.divider, - opacity: 1, - }, - }, - }, - MuiAvatar: { - styleOverrides: { - root: { - color: theme.colors?.primaryDark, - background: theme.colors?.primary200, - }, - }, - }, - MuiChip: { - styleOverrides: { - root: { - '&.MuiChip-deletable .MuiChip-deleteIcon': { - color: 'inherit', - }, - }, - }, - }, - MuiTooltip: { - styleOverrides: { - tooltip: { - color: theme.paper, - background: theme.colors?.grey700, - }, - }, - }, - }; -} diff --git a/src/assets/themes/themeSettings.jsx b/src/assets/themes/themeSettings.jsx index 7d2f144..678147c 100644 --- a/src/assets/themes/themeSettings.jsx +++ b/src/assets/themes/themeSettings.jsx @@ -1,12 +1,5 @@ -import { tokens } from './tokens'; -// import themeTypography from './typography/typography'; -// import themeColors from '../scss/_theme-vars.modules.scss'; -import DeckOfCardsIcon from '../../components/reusable/icons/DeckOfCardsIcon'; -import MoneyIcon from '../../components/reusable/icons/MoneyIcon'; -import ChartsIcon from '../../components/reusable/icons/ChartsIcon'; import colors from './base/colors'; import components from './components/index'; -import { backgroundA, backgroundC } from './base/customColorPalettes'; import borders from './base/borders'; import boxShadows from './base/boxShadows'; import typography from './base/typography'; @@ -15,27 +8,10 @@ import hexToRgb from './functions/hexToRgb'; import linearGradient from './functions/linearGradient'; import pxToRem from './functions/pxToRem'; import rgba from './functions/rgba'; +import breakpoints from './base/breakpoints'; +import Transitions from './Transitions'; export const themeSettings = (mode) => { - const colorsA = tokens(mode); - const specialBreakpoints = { - isSmUp: (breakpoints) => breakpoints.up('sm'), - isMdUp: (breakpoints) => breakpoints.up('md'), - isLgUp: (breakpoints) => breakpoints.up('lg'), - isXlUp: (breakpoints) => breakpoints.up('xl'), - isSmDown: (breakpoints) => breakpoints.down('sm'), - isMdDown: (breakpoints) => breakpoints.down('md'), - isLgDown: (breakpoints) => breakpoints.down('lg'), - isXlDown: (breakpoints) => breakpoints.down('xl'), - - isXSmall: (breakpoints) => breakpoints.down('xs'), - isSmall: (breakpoints) => breakpoints.down('sm'), - isSmallMedium: (breakpoints) => breakpoints.up('sm'), - isMedium: (breakpoints) => breakpoints.down('md'), - isMediumLarge: (breakpoints) => breakpoints.up('md'), - isLarge: (breakpoints) => breakpoints.up('lg'), - isXLarge: (breakpoints) => breakpoints.up('xl'), - }; return { components: components, functions: { @@ -47,13 +23,13 @@ export const themeSettings = (mode) => { }, palette: { ...colors, - ...colorsA, mode: mode, }, - specialBreakpoints: specialBreakpoints, - // components, - borders, - boxShadows, + breakpoints: breakpoints.values, + Transitions: Transitions, + borders: borders, + boxShadows: boxShadows, + typography: typography, spacing: (factor) => `${0.25 * factor}rem`, shape: { borderRadius: 4, @@ -63,21 +39,19 @@ export const themeSettings = (mode) => { appBar: 1200, drawer: 1100, }, - shadows: [ - 'none', - '0px 2px 1px -1px rgba(0,0,0,0.1),0px 1px 1px 0px rgba(0,0,0,0.06),0px 1px 3px 0px rgba(0,0,0,0.04)', // example for theme.shadows[1] - '0px 3px 1px -2px rgba(0,0,0,0.1),0px 2px 2px 0px rgba(0,0,0,0.06),0px 1px 5px 0px rgba(0,0,0,0.04)', // example for theme.shadows[2] - '0px 3px 3px -2px rgba(0,0,0,0.1),0px 3px 4px 0px rgba(0,0,0,0.06),0px 1px 8px 0px rgba(0,0,0,0.04)', // example for theme.shadows[3] - '0px 2px 4px -1px rgba(0,0,0,0.1),0px 4px 5px 0px rgba(0,0,0,0.06),0px 1px 10px 0px rgba(0,0,0,0.04)', // example for theme.shadows[4] - '0px 3px 5px -1px rgba(0,0,0,0.1),0px 5px 8px 0px rgba(0,0,0,0.06),0px 1px 14px 0px rgba(0,0,0,0.04)', // example for theme.shadows[5] - '0px 3px 5px -1px rgba(0,0,0,0.1),0px 6px 10px 0px rgba(0,0,0,0.06),0px 1px 18px 0px rgba(0,0,0,0.04)', // example for theme.shadows[6] - '0px 4px 5px -2px rgba(0,0,0,0.1),0px 7px 10px 1px rgba(0,0,0,0.06),0px 2px 16px 1px rgba(0,0,0,0.04)', // example for theme.shadows[7] - '0px 5px 5px -3px rgba(0,0,0,0.1),0px 8px 10px 1px rgba(0,0,0,0.06),0px 3px 14px 2px rgba(0,0,0,0.04)', // example for theme.shadows[8] - '0px 5px 6px -3px rgba(0,0,0,0.1),0px 9px 12px 1px rgba(0,0,0,0.06),0px 3px 16px 2px rgba(0,0,0,0.04)', // example for theme.shadows[9] - '0px 5px 15px rgba(0,0,0,0.1)', // example for theme.shadows[10] - ], - typography, - // LAYOUTS + // shadows: [ + // 'none', + // '0px 2px 1px -1px rgba(0,0,0,0.1),0px 1px 1px 0px rgba(0,0,0,0.06),0px 1px 3px 0px rgba(0,0,0,0.04)', // example for theme.shadows[1] + // '0px 3px 1px -2px rgba(0,0,0,0.1),0px 2px 2px 0px rgba(0,0,0,0.06),0px 1px 5px 0px rgba(0,0,0,0.04)', // example for theme.shadows[2] + // '0px 3px 3px -2px rgba(0,0,0,0.1),0px 3px 4px 0px rgba(0,0,0,0.06),0px 1px 8px 0px rgba(0,0,0,0.04)', // example for theme.shadows[3] + // '0px 2px 4px -1px rgba(0,0,0,0.1),0px 4px 5px 0px rgba(0,0,0,0.06),0px 1px 10px 0px rgba(0,0,0,0.04)', // example for theme.shadows[4] + // '0px 3px 5px -1px rgba(0,0,0,0.1),0px 5px 8px 0px rgba(0,0,0,0.06),0px 1px 14px 0px rgba(0,0,0,0.04)', // example for theme.shadows[5] + // '0px 3px 5px -1px rgba(0,0,0,0.1),0px 6px 10px 0px rgba(0,0,0,0.06),0px 1px 18px 0px rgba(0,0,0,0.04)', // example for theme.shadows[6] + // '0px 4px 5px -2px rgba(0,0,0,0.1),0px 7px 10px 1px rgba(0,0,0,0.06),0px 2px 16px 1px rgba(0,0,0,0.04)', // example for theme.shadows[7] + // '0px 5px 5px -3px rgba(0,0,0,0.1),0px 8px 10px 1px rgba(0,0,0,0.06),0px 3px 14px 2px rgba(0,0,0,0.04)', // example for theme.shadows[8] + // '0px 5px 6px -3px rgba(0,0,0,0.1),0px 9px 12px 1px rgba(0,0,0,0.06),0px 3px 16px 2px rgba(0,0,0,0.04)', // example for theme.shadows[9] + // '0px 5px 15px rgba(0,0,0,0.1)', // example for theme.shadows[10] + // ], skeletonLayouts: { tertiaryContent: { xs: 12, @@ -112,206 +86,24 @@ export const themeSettings = (mode) => { }, }, }; - // MuiListItemButton: { - // styleOverrides: { - // root: { - // color: colorsA.greenAccent[500], - // paddingTop: '10px', - // paddingBottom: '10px', - // '&.Mui-selected': { - // color: colorsA.greenAccent[200], - // backgroundColor: colorsA.greenAccent[300], - // '&:hover': { - // backgroundColor: colorsA.greenAccent[700], - // }, - // '& .MuiListItemIcon-root': { - // color: colorsA.greenAccent[400], - // }, - // }, - // '&:hover': { - // backgroundColor: colorsA.greenAccent[400], - // color: colorsA.greenAccent[200], - // '& .MuiListItemIcon-root': { - // color: colorsA.greenAccent[400], - // }, - // }, - // }, - // }, - // }, - // MuiOutlinedInput: { - // styleOverrides: { - // root: { - // background: backgroundA.default, - // borderRadius: `${themeColors?.borderRadius}px`, - // '& .MuiOutlinedInput-notchedOutline': { - // borderColor: themeColors?.colors?.grey400, - // }, - // '&:hover $notchedOutline': { - // borderColor: themeColors?.primaryLight, - // }, - // '&.MuiInputBase-multiline': { - // padding: 1, - // }, - // }, - // input: { - // fontWeight: 500, - // background: backgroundA.default, - // padding: '15.5px 14px', - // borderRadius: `${themeColors?.borderRadius}px`, - // '&.MuiInputBase-inputSizeSmall': { - // padding: '10px 14px', - // '&.MuiInputBase-inputAdornedStart': { - // paddingLeft: 0, - // }, - // }, - // }, - // inputAdornedStart: { - // paddingLeft: 4, - // }, - // notchedOutline: { - // borderRadius: `${themeColors?.customization?.borderRadius}px`, - // }, - // }, - // }, }; -// const createColorGradient = (color1, color2, steps) => { -// const gradient = []; -// const stepFactor = 1 / (steps - 1); -// for (let i = 0; i < steps; i++) { -// gradient.push(shadeBlendConvert(i * stepFactor, color1, color2)); -// } -// return gradient; -// }; - -// TODO: TEST BOTH HEAX CONVERSION FUNCTIONS -// Utility function to convert Hex to RGBA -// const hexToRgba = (hex, alpha = 1) => { -// let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; -// hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b); -// let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); -// return result -// ? `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt( -// result[3], -// 16 -// )}, ${alpha})` -// : null; -// }; -// function convertHexToRGBA(hex, alpha) { -// const r = parseInt(hex.slice(1, 3), 16); -// const g = parseInt(hex.slice(3, 5), 16); -// const b = parseInt(hex.slice(5, 7), 16); - -// return `rgba(${r}, ${g}, ${b}, ${alpha})`; -// } -// Utility functions -// const pxToRem = (number, baseNumber = 16) => { -// return `${number / baseNumber}rem`; -// }; +// specialBreakpoints: specialBreakpoints, +// const colorsA = tokens(mode); +// const specialBreakpoints = { +// isSmUp: (breakpoints) => breakpoints.up('sm'), +// isMdUp: (breakpoints) => breakpoints.up('md'), +// isLgUp: (breakpoints) => breakpoints.up('lg'), +// isXlUp: (breakpoints) => breakpoints.up('xl'), +// isSmDown: (breakpoints) => breakpoints.down('sm'), +// isMdDown: (breakpoints) => breakpoints.down('md'), +// isLgDown: (breakpoints) => breakpoints.down('lg'), +// isXlDown: (breakpoints) => breakpoints.down('xl'), -// const hexToRgba = (hex, alpha = 1) => { -// let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; -// hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b); -// let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); -// return result -// ? `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${alpha})` -// : null; +// isXSmall: (breakpoints) => breakpoints.down('xs'), +// isSmall: (breakpoints) => breakpoints.down('sm'), +// isSmallMedium: (breakpoints) => breakpoints.up('sm'), +// isMedium: (breakpoints) => breakpoints.down('md'), +// isMediumLarge: (breakpoints) => breakpoints.up('md'), +// isLarge: (breakpoints) => breakpoints.up('lg'), +// isXLarge: (breakpoints) => breakpoints.up('xl'), // }; - -// const rgba = (color, opacity) => { -// return hexToRgba(color, opacity); -// }; - -// const linearGradient = (color, colorState, angle = 195) => { -// return `linear-gradient(${angle}deg, ${color}, ${colorState})`; -// }; -// chart: { -// axis: { -// domain: { -// line: { -// stroke: colors.greenAccent[800], -// strokeWidth: 1, -// }, -// }, -// ticks: { -// line: { -// stroke: colors.greenAccent[700], -// strokeWidth: 1, -// }, -// text: { -// fill: colors.greenAccent[900], -// fontSize: 12, -// }, -// }, -// }, -// grid: { -// line: { -// stroke: colors.greenAccent[200], -// strokeWidth: 1, -// }, -// }, -// legends: { -// text: { -// fill: colors.greenAccent[800], -// fontSize: 12, -// }, -// }, -// tooltip: { -// container: { -// background: colors.greenAccent[100], -// color: colors.greenAccent[800], -// fontSize: 12, -// borderRadius: 4, -// boxShadow: '0 2px 4px rgba(0,0,0,0.25)', -// }, -// }, -// points: { -// borderColor: colors.greenAccent[800], -// }, -// }, -// getHeaderStyle: { -// fontWeight: 'bold', -// color: 'text.primary', -// marginBottom: 2, // theme.spacing(2) -// '@media (maxWidth:599.95px)': { -// fontSize: '1.25rem', -// }, -// '@media (minWidth:600px) and (maxWidth:899.95px)': { -// fontSize: '1.5rem', -// }, -// '@media (minWidth:900px)': { -// fontSize: '1.75rem', -// }, -// }, - -// getStyledGridStyle: { -// '@media (maxWidth:599.95px)': { -// margin: 0.5, // theme.spacing(0.5) -// }, -// '@media (minWidth:600px) and (maxWidth:1199.95px)': { -// margin: 1, // theme.spacing(1) -// }, -// '@media (minWidth:1200px)': { -// margin: 2, // theme.spacing(2) -// }, -// '@media (minWidth:1800px)': { -// margin: 2, // theme.spacing(2) -// }, -// }, - -// getStyledGridItemStyle: { -// display: 'flex', -// flexDirection: 'column', -// alignItems: 'stretch', -// '@media (maxWidth:599.95px)': { -// padding: 1, // theme.spacing(1) -// }, -// '@media (minWidth:600px)': { -// padding: 0.25, // theme.spacing(0.25) -// }, -// '@media (minWidth:1200px)': { -// padding: 1, // theme.spacing(1) -// }, -// '@media (minWidth:1800px)': { -// padding: 2, // theme.spacing(2) -// }, -// }, diff --git a/src/assets/themes/tokens.jsx b/src/assets/themes/tokens.jsx deleted file mode 100644 index e195906..0000000 --- a/src/assets/themes/tokens.jsx +++ /dev/null @@ -1,132 +0,0 @@ -export const tokens = (mode) => ({ - ...(mode === 'dark' - ? { - grey: { - 100: '#e0e0e0', - 200: '#c2c2c2', - 300: '#a3a3a3', - 400: '#858585', - 500: '#666666', - 600: '#525252', - 700: '#3d3d3d', - 800: '#292929', - 900: '#141414', - }, - primary: { - 100: '#d0d1d5', - 200: '#a1a4ab', - 300: '#727681', - 400: '#1F2A40', - 500: '#141b2d', - 600: '#101624', - 700: '#0c101b', - 800: '#080b12', - 900: '#040509', - }, - blueGreenAccent: { - 100: '#dbf5f2', // Lightest shade, more towards blue - 200: '#b7ebe6', - 300: '#93e1da', - 400: '#6fd7ce', - 500: '#4bcdc2', // Mid-value, a balanced blue-green - 600: '#39a59b', - 700: '#2b7d76', - 800: '#1d5551', - 900: '#0f2d28', // Darkest shade, deeper blue-green - }, - greenAccent: { - 100: '#dbf5ee', - 200: '#b7ebde', - 300: '#94e2cd', - 400: '#70d8bd', - 500: '#4cceac', - 600: '#3da58a', - 700: '#2e7c67', - 800: '#1e5245', - 900: '#0f2922', - }, - redAccent: { - 100: '#f8dcdb', - 200: '#f1b9b7', - 300: '#e99592', - 400: '#e2726e', - 500: '#db4f4a', - 600: '#af3f3b', - 700: '#832f2c', - 800: '#58201e', - 900: '#2c100f', - }, - blueAccent: { - 100: '#e1e2fe', - 200: '#c3c6fd', - 300: '#a4a9fc', - 400: '#868dfb', - 500: '#6870fa', - 600: '#535ac8', - 700: '#3e4396', - 800: '#2a2d64', - 900: '#151632', - }, - } - : { - grey: { - 100: '#141414', - 200: '#292929', - 300: '#3d3d3d', - 400: '#525252', - 500: '#666666', - 600: '#858585', - 700: '#a3a3a3', - 800: '#c2c2c2', - 900: '#e0e0e0', - }, - primary: { - 100: '#040509', - 200: '#080b12', - 300: '#0c101b', - 400: '#f2f0f0', // manually changed - 500: '#141b2d', - 600: '#1F2A40', - 700: '#727681', - 800: '#a1a4ab', - 900: '#d0d1d5', - }, - // secondary: { - // 100: '#ff7961', - // 200: '#f44336', - // }, - greenAccent: { - 100: '#0f2922', - 200: '#1e5245', - 300: '#2e7c67', - 400: '#3da58a', - 500: '#4cceac', - 600: '#70d8bd', - 700: '#94e2cd', - 800: '#b7ebde', - 900: '#dbf5ee', - }, - redAccent: { - 100: '#2c100f', - 200: '#58201e', - 300: '#832f2c', - 400: '#af3f3b', - 500: '#db4f4a', - 600: '#e2726e', - 700: '#e99592', - 800: '#f1b9b7', - 900: '#f8dcdb', - }, - blueAccent: { - 100: '#151632', - 200: '#2a2d64', - 300: '#3e4396', - 400: '#535ac8', - 500: '#6870fa', - 600: '#868dfb', - 700: '#a4a9fc', - 800: '#c3c6fd', - 900: '#e1e2fe', - }, - }), -}); diff --git a/src/assets/themes/typography/typography.js b/src/assets/themes/typography/typography.js deleted file mode 100644 index f620e1b..0000000 --- a/src/assets/themes/typography/typography.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Typography used in theme - * @param {JsonObject} theme theme customization object - */ - -export default function themeTypography(theme) { - return { - fontFamily: theme?.customization?.fontFamily, - h6: { - fontWeight: 500, - color: theme.heading, - fontSize: '0.75rem', - }, - h5: { - fontSize: '0.875rem', - color: theme.heading, - fontWeight: 500, - }, - h4: { - fontSize: '1rem', - color: theme.heading, - fontWeight: 600, - }, - h3: { - fontSize: '1.25rem', - color: theme.heading, - fontWeight: 600, - }, - h2: { - fontSize: '1.5rem', - color: theme.heading, - fontWeight: 700, - }, - h1: { - fontSize: '2.125rem', - color: theme.heading, - fontWeight: 700, - }, - subtitle1: { - fontSize: '0.875rem', - fontWeight: 500, - color: theme.textDark, - }, - subtitle2: { - fontSize: '0.75rem', - fontWeight: 400, - color: theme.darkTextSecondary, - }, - caption: { - fontSize: '0.75rem', - color: theme.darkTextSecondary, - fontWeight: 400, - }, - body1: { - fontSize: '0.875rem', - fontWeight: 400, - lineHeight: '1.334em', - }, - body2: { - letterSpacing: '0em', - fontWeight: 400, - lineHeight: '1.5em', - color: theme.darkTextPrimary, - }, - button: { - textTransform: 'capitalize', - }, - customInput: { - marginTop: 1, - marginBottom: 1, - '& > label': { - top: 23, - left: 0, - color: theme.grey500, - '&[data-shrink="false"]': { - top: 5, - }, - }, - '& > div > input': { - padding: '30.5px 14px 11.5px !important', - }, - '& legend': { - display: 'none', - }, - '& fieldset': { - top: 0, - }, - }, - mainContent: { - backgroundColor: theme.background, - width: '100%', - minHeight: 'calc(100vh - 88px)', - flexGrow: 1, - padding: '20px', - marginTop: '88px', - marginRight: '20px', - borderRadius: `${theme?.customization?.borderRadius}px`, - }, - menuCaption: { - fontSize: '0.875rem', - fontWeight: 500, - color: theme.heading, - padding: '6px', - textTransform: 'capitalize', - marginTop: '10px', - }, - subMenuCaption: { - fontSize: '0.6875rem', - fontWeight: 500, - color: theme.darkTextSecondary, - textTransform: 'capitalize', - }, - commonAvatar: { - cursor: 'pointer', - borderRadius: '8px', - }, - smallAvatar: { - width: '22px', - height: '22px', - fontSize: '1rem', - }, - mediumAvatar: { - width: '34px', - height: '34px', - fontSize: '1.2rem', - }, - largeAvatar: { - width: '44px', - height: '44px', - fontSize: '1.5rem', - }, - }; -} diff --git a/src/components/buttons/actionButtons/GenericActionButtons.jsx b/src/components/buttons/actionButtons/GenericActionButtons.jsx index aad8ca1..5700053 100644 --- a/src/components/buttons/actionButtons/GenericActionButtons.jsx +++ b/src/components/buttons/actionButtons/GenericActionButtons.jsx @@ -1,27 +1,17 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { Box } from '@mui/material'; import MDTypography from '../../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; -import AddButton from '../../../zcleanup/AddButton'; -import RemoveButton from '../../../zcleanup/RemoveButton'; -import { useModalContext } from '../../../context/UTILITIES_CONTEXT/ModalContext/ModalContext'; -import useSelectedContext from '../../../context/hooks/useSelectedContext'; -import { getContextIcon } from '../../../components/reusable/icons/index'; -// import { -// useCollectionManager, -// useSelectedCollection, -// } from '../../../context/MAIN_CONTEXT/CollectionContext'; +import { getContextIcon } from '../../../layout/REUSABLE_COMPONENTS/icons/index'; import { useDeckStore } from '../../../context/MAIN_CONTEXT/DeckContext/DeckContext'; import { useCartStore } from '../../../context/MAIN_CONTEXT/CartContext/CartContext'; -import { DEFAULT_COLLECTION } from '../../../context/constants'; -import { useCardActions } from '../../../context/hooks/useCardActions'; import useCollectionManager from '../../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; import useSelectedCollection from '../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; import ActionButton from './ActionButton'; import { useSnackbar } from 'notistack'; -import GlassyIcon from '../../../components/reusable/icons/GlassyIcon'; +import GlassyIcon from '../../../layout/REUSABLE_COMPONENTS/icons/GlassyIcon'; import MDBox from '../../../layout/REUSABLE_COMPONENTS/MDBOX'; +import useDeckManager from '../../../context/MAIN_CONTEXT/DeckContext/useDeckManager'; -// Utility function for mapping cardSize to buttonSize const buttonSizeMap = { xs: 'extraSmall', sm: 'small', @@ -41,20 +31,11 @@ const GenericActionButtons = ({ const { enqueueSnackbar } = useSnackbar(); // Add this line to use Notistack const { addOneToCollection, removeOneFromCollection } = useCollectionManager(); - const { selectedCollection, allCollections, handleSelectCollection } = - useSelectedCollection(); - const { - addOneToDeck, - removeOneFromDeck, - selectedDeck, - allDecks, - setSelectedDeck, - } = useDeckStore(); + const { addOneToDeck, removeOneFromDeck } = useDeckManager(); const { addOneToCart, removeOneFromCart, cartData } = useCartStore(); const [buttonSize, setButtonSize] = useState( buttonSizeMap[cardSize] || 'medium' ); - const { closeModal } = useModalContext(); useEffect(() => { setButtonSize(buttonSizeMap[cardSize] || 'medium'); }, [cardSize]); @@ -90,10 +71,6 @@ const GenericActionButtons = ({ } else if (action === 'remove' && removeActions[currentContext]) { removeActions[currentContext](cardData); } - - // Hook into success or failure callbacks as necessary - // onSuccess(); - // onFailure(); }, [card, context, addActions, removeActions] ); @@ -114,10 +91,8 @@ const ActionButtons = ({ context, page, handleCardAction, + variant, }) => { - // console.log( - // `ActionButtons: buttonSize: ${buttonSize}, card: ${card?.name}, context: ${context}` - // ); const labelValue = typeof context === 'string' ? context : context?.pageContext; const stackDirection = buttonSize === 'extraSmall' ? 'column' : 'row'; @@ -159,17 +134,19 @@ const ActionButtons = ({ }, }} > - - - + {variant !== 'data-table' && ( + + + + )} { -// if (typeof context === 'undefined') { -// context = 'Collection'; -// } -// const { closeModal, isModalOpen, setModalOpen } = useModalContext(); -// const { selectedCollection, allCollections } = useSelectedContext(); -// const [buttonSize, setButtonSize] = React.useState('medium'); - -// const labelValue = -// typeof context === 'string' ? context : context?.pageContext; -// useEffect(() => { -// const buttonSizeMap = { -// xs: 'extraSmall', -// sm: 'small', -// md: 'medium', -// lg: 'large', // Adjust if there's another size you want for 'l' -// }; -// const size = buttonSizeMap[cardSize] || 'medium'; // Default to 'medium' if size is not defined -// setButtonSize(size); -// }, [cardSize]); - -// return ( -// -// {renderFullWidthAddButton( -// buttonSize, -// isModalOpen, -// labelValue, -// cardSize, -// context, -// card, -// page, -// onClick, -// closeModal, -// onSuccess, -// onFailure -// )} -// -// ); -// }; - -// export default GenericActionButtons; - -// // const renderSelectionDialog = () => ( -// // setSelectDialogOpen(false)}> -// // -// // -// // Select a {context} -// // -// // -// // -// // -// // {itemsForSelection?.map((item) => ( -// // -// // ))} -// // -// // -// // -// // ); diff --git a/src/components/buttons/other/AuthSwitch.jsx b/src/components/buttons/other/AuthSwitch.jsx deleted file mode 100644 index 6f76bf7..0000000 --- a/src/components/buttons/other/AuthSwitch.jsx +++ /dev/null @@ -1,128 +0,0 @@ -import React from 'react'; -import { renderToStaticMarkup } from 'react-dom/server'; - -import { Switch, FormControlLabel, FormControl, alpha } from '@mui/material'; -import { useFormContext, useMode } from '../../../context'; // Adjust with actual path -import { AuthModeSwitch } from '../../../layout/REUSABLE_STYLED_COMPONENTS/SpecificStyledComponents'; -import LoginIcon from '@mui/icons-material/Login'; -import PersonAddIcon from '@mui/icons-material/PersonAdd'; -import styled from 'styled-components'; -import { useCookies } from 'react-cookie'; -const convertSvg = (svg) => { - const markup = renderToStaticMarkup(svg); - const encoded = encodeURIComponent(markup); - const dataUri = `url('data:image/svg+xml;utf8,${encoded}')`; - return dataUri; -}; -const AuthSwitch = () => { - // const AuthSwitch = ({ signupMode, formLabel }) => { - const { theme } = useMode(); // Ensures theme is applied correctly - // const colors = theme; - const colors = theme.palette.chartTheme; - const cookies = useCookies('colorMode'); - const mode = cookies.colorMode; - - const primary = colors.primary.default; - const blue = colors.blueAccent.default; - const green = colors.greenAccent.light; - const lihhtgreen = colors.greenAccent.default; - const greenliht = colors.greenAccent.light; - const lightgrey = colors.grey.light; - const darkgrey = colors.grey.dark; - const darkestgrey = colors.grey.darkest; - - const { currentSchemaKey, toggleForm } = useFormContext(); // Use toggleForm for toggling modes - const signupMode = currentSchemaKey === 'signupForm'; - const handleToggle = () => { - toggleForm(currentSchemaKey === 'loginForm' ? 'signupForm' : 'loginForm'); - }; - const switchBaseStyles = { - '& .MuiSwitch-switchBase': { - padding: 1, // Center the switch base padding for proper alignment - '&.Mui-checked': { - transform: 'translateX(16px)', // Ensure this aligns with your switch size - color: theme.palette.common.white, // Thumb color when checked - '& + .MuiSwitch-track': { - backgroundColor: theme.palette.primary.main, // Track color when checked - }, - }, - }, - }; - const thumbStyles = { - '& .MuiSwitch-thumb': { - width: 22, // Adjust thumb size for better visual alignment - height: 22, // Adjust thumb size for better visual alignment - backgroundColor: mode === 'dark' ? green : lihhtgreen, - // backgroundColor: theme.palette.common.white, // Default thumb color - - '&:before': { - content: '" "', - display: 'block', - backgroundImage: signupMode - ? convertSvg() - : convertSvg(), - width: '100%', - height: '100%', - backgroundSize: '50%', - backgroundPosition: 'center', - backgroundRepeat: 'no-repeat', - }, - // '&:after': { - // right: 12, // Adjust the right icon position - // backgroundImage: convertSvg(), - // }, - }, - }; - const trackStyles = { - '& .MuiSwitch-track': { - backgroundColor: theme.palette.chartTheme.grey.light, // Default track color - opacity: 1, - width: 50, // Adjust thumb size for better visual alignment - height: 14, // Adjust thumb size for better visual alignment - // '&:before, &:after': { - // content: '""', - // position: 'absolute', - // top: '50%', - // transform: 'translateY(-50%)', - // }, - // '&:before': { - // left: 12, // Adjust the left icon position - // backgroundImage: convertSvg(), - // }, - // '&:after': { - // right: 12, // Adjust the right icon position - // backgroundImage: convertSvg(), - // }, - }, - }; - const switchStyles = { - ...switchBaseStyles, - ...thumbStyles, - ...trackStyles, - // backgroundImage: convertSvg(), - // right: 2, - }; - - return ( - - - } - label={signupMode ? 'Sign Up' : 'Log In'} - style={{ margin: 'auto', justifyContent: 'center' }} // Center label and switch - /> - - ); -}; - -export default AuthSwitch; diff --git a/src/components/buttons/other/CronTrigger.jsx b/src/components/buttons/other/CronTrigger.jsx deleted file mode 100644 index 9710b37..0000000 --- a/src/components/buttons/other/CronTrigger.jsx +++ /dev/null @@ -1,27 +0,0 @@ -// import React from 'react'; -// import { Box, Button } from '@mui/material'; -// import { useCombinedContext } from '../../../context/CombinedProvider'; -// import { useAuthContext } from '../../../context'; - -// const CronTrigger = () => { -// const { stopCronJob, handleSendAllCardsInCollections, listOfMonitoredCards } = -// useCombinedContext(); -// const { userId } = useAuthContext(); -// const handleTriggerCron = () => { -// console.log('TRIGGERING CRON JOB TO UPDATE: ' + listOfMonitoredCards); -// handleSendAllCardsInCollections(userId, listOfMonitoredCards); -// }; - -// const handleStopCron = () => { -// stopCronJob(); -// }; - -// return ( -// -// -// -// -// ); -// }; - -// export default CronTrigger; diff --git a/src/components/buttons/other/CustomButton.jsx b/src/components/buttons/other/CustomButton.jsx deleted file mode 100644 index 272b23a..0000000 --- a/src/components/buttons/other/CustomButton.jsx +++ /dev/null @@ -1,43 +0,0 @@ -// import React from 'react'; -// import { Button } from '@mui/material'; -// import { useMode } from '../../../context'; // Adjust the import path based on your project structure -// import MDButton from '../../../layout/REUSABLE_COMPONENTS/MDBUTTON'; - -// const CustomButton = ({ -// text, -// onClick, -// variant = 'contained', -// size = 'large', -// sx = {}, -// }) => { -// const { theme } = useMode(); - -// const defaultStyles = { -// background: theme.palette.backgroundE.darker, -// borderColor: theme.palette.backgroundB.darkest, -// borderWidth: 2, -// mx: 1, -// width: '70%', -// '&:hover': { -// fontWeight: 'bold', -// background: theme.palette.backgroundF.dark, -// borderColor: theme.palette.backgroundB.darkest, -// border: `1px solid ${theme.palette.backgroundB.darkest}`, -// }, -// ...sx, // Allow custom styles to override defaults -// }; - -// return ( -// -// {text} -// -// ); -// }; - -// export default CustomButton; diff --git a/src/components/buttons/other/DeckSwitch.jsx b/src/components/buttons/other/DeckSwitch.jsx deleted file mode 100644 index 7288664..0000000 --- a/src/components/buttons/other/DeckSwitch.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import { - Switch, - FormControlLabel, - Typography, - FormControl, -} from '@mui/material'; -import { useFormContext, useMode } from '../../../context'; // Adjust with actual path -import MDTypography from '../../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; - -const DeckSwitch = ({ editMode, onToggle }) => { - const { theme } = useMode(); // Ensures theme is applied correctly - - return ( - - - } - label={ - - {editMode ? 'Edit Mode' : 'View Mode'} {/* Label */} - - } - style={{ - margin: theme.spacing(1), // Provide some spacing - justifyContent: 'space-between', // Align items nicely - }} - /> - - ); -}; - -export default DeckSwitch; diff --git a/src/components/buttons/other/OrderSubmitButton.js b/src/components/buttons/other/OrderSubmitButton.js deleted file mode 100644 index e990dfd..0000000 --- a/src/components/buttons/other/OrderSubmitButton.js +++ /dev/null @@ -1,25 +0,0 @@ -// import React from 'react'; -// import { Button } from '@mui/material'; -// import { useMode } from '../../../context'; - -// const OrderSubmitButton = ({ onClick }) => { -// const { theme } = useMode(); -// return ( -// -// ); -// }; - -// export default OrderSubmitButton; diff --git a/src/components/buttons/other/ReusableLoadingButton.jsx b/src/components/buttons/other/ReusableLoadingButton.jsx new file mode 100644 index 0000000..e2eac8e --- /dev/null +++ b/src/components/buttons/other/ReusableLoadingButton.jsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { LoadingButton } from '@mui/lab'; +import AdjustSharpIcon from '@mui/icons-material/AdjustSharp'; +import { useMode } from '../../../context'; + +const ReusableLoadingButton = ({ + loading, + label, + icon, + onClick, + style, + fullWidth, + sx, + variant, +}) => { + const { theme } = useMode(); + + const getButtonStyles = (variant) => { + if (variant === 'warning') { + return { + background: theme.palette.error.main, + borderColor: theme.palette.error.darkest, + borderWidth: 5, + + '&:hover': { + fontWeight: 'bold', + background: theme.palette.error.light, + borderColor: theme.palette.error.dark, + }, + '&:focus': { + outline: `2px solid ${theme.palette.error.dark}`, + outlineOffset: 2, + }, + }; + } + return { + background: theme.palette.backgroundG.light, + borderColor: theme.palette.backgroundG.light, + borderWidth: 2, + '&:hover': { background: theme.palette.backgroundG.default }, + '&:focus': { background: theme.palette.backgroundG.default }, + }; + }; + + return ( + } + fullWidth={fullWidth} + onClick={onClick} + sx={{ + ...sx, + mt: 2, // Adjust spacing as needed + ...getButtonStyles(variant), + }} + > + {label} + + ); +}; + +export default ReusableLoadingButton; diff --git a/src/components/buttons/other/SearchButton.js b/src/components/buttons/other/SearchButton.js deleted file mode 100644 index fcd05ae..0000000 --- a/src/components/buttons/other/SearchButton.js +++ /dev/null @@ -1,32 +0,0 @@ -// import React from 'react'; -// import { Button, Grid } from '@mui/material'; -// import { useMode } from '../../../context'; - -// const SearchButton = ({ searchParams, handleSubmit }) => { -// // const { handleRequest } = useCardStore(); -// const { theme } = useMode(); - -// return ( -// -// -// -// ); -// }; - -// export default SearchButton; diff --git a/src/components/buttons/other/SignupSwitch.jsx b/src/components/buttons/other/SignupSwitch.jsx deleted file mode 100644 index e186d06..0000000 --- a/src/components/buttons/other/SignupSwitch.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { Switch, FormControlLabel, FormControl } from '@mui/material'; -import { useFormContext, useMode } from '../../../context'; // Adjust with actual path -const SignupSwitch = ({ signupMode, toggleAuthMode, formLabel }) => { - const { theme } = useMode(); // Ensures theme is applied correctly - - return ( - - - } - label={formLabel} - style={{ - margin: theme.spacing(1), // Provide some spacing - justifyContent: 'space-between', // Align items nicely - }} - /> - - ); -}; - -export default SignupSwitch; diff --git a/src/components/buttons/other/ThemeToggleButton.jsx b/src/components/buttons/other/ThemeToggleButton.jsx deleted file mode 100644 index 1576bdd..0000000 --- a/src/components/buttons/other/ThemeToggleButton.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useContext, useState } from 'react'; -import { IconButton, Menu, MenuItem } from '@mui/material'; -import Brightness4Icon from '@mui/icons-material/Brightness4'; -import Brightness7Icon from '@mui/icons-material/Brightness7'; -import { ColorModeContext } from '../../../context/UTILITIES_CONTEXT/ColorModeContext/ColorModeProvider'; - -const ThemeToggleButton = () => { - const [anchorEl, setAnchorEl] = useState(null); - const { toggleColorMode, setMode } = useContext(ColorModeContext); - - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - const handleThemeChange = (mode) => { - toggleColorMode(mode); // Updated this line - console.log('mode', mode); - handleClose(); - }; - - return ( -
- - - - - handleThemeChange('dark')}> - - Dark Mode - - handleThemeChange('light')}> - - Light Mode - - -
- ); -}; - -export default ThemeToggleButton; diff --git a/src/components/cards/CardCarousel.jsx b/src/components/cards/CardCarousel.jsx deleted file mode 100644 index 2e9c6fa..0000000 --- a/src/components/cards/CardCarousel.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; -import { Swiper, SwiperSlide } from 'swiper/react'; -import { EffectCoverflow, Pagination } from 'swiper'; -import 'swiper/css'; -import 'swiper/css/effect-coverflow'; -import 'swiper/css/pagination'; -import GenericCard from './GenericCard'; -import { - StyledCarousel, - StyledSwiperSlide, -} from '../../pages/pageStyles/StyledComponents'; - -const CardCarousel = ({ cards, context }) => { - const swiperSettings = { - effect: 'coverflow', - grabCursor: true, - centeredSlides: true, - slidesPerView: 'auto', - coverflowEffect: { - rotate: 50, - stretch: 0, - depth: 200, - modifier: 1, - slideShadows: true, - }, - loop: true, - pagination: { clickable: true }, - }; - - return ( - - - {cards.map((card, index) => ( - - - - ))} - - - ); -}; - -export default CardCarousel; diff --git a/src/components/cards/CardDetail.jsx b/src/components/cards/CardDetail.jsx index 465c53c..7a2d605 100644 --- a/src/components/cards/CardDetail.jsx +++ b/src/components/cards/CardDetail.jsx @@ -27,18 +27,25 @@ const CardDetail = ({ {icon && {icon}} - {/* {icon && ( - {icon} - )} */} - {title}: - {value && {value}} + {value && ( + + {value} + + )} {Array.isArray(values) && values.length > 0 && values.map((rarityValue, index) => ( @@ -56,25 +63,6 @@ const CardDetail = ({ variant="outlined" /> ))} - {/* - {Array.isArray(values) && values.length > 0 - ? values.map((rarityValue, index) => ( - onRarityClick(rarityValue?.toString())} - sx={{ - borderColor: getChipColor(rarityValue?.toString()), - borderWidth: '2px', - fontWeight: 700, - color: getChipColor(rarityValue?.toString()), - margin: '5px', - }} - variant="outlined" - /> - )) - : null} - */} ); diff --git a/src/components/cards/CardDetails.jsx b/src/components/cards/CardDetails.jsx deleted file mode 100644 index 0ef2501..0000000 --- a/src/components/cards/CardDetails.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { Box, Typography } from '@mui/material'; -import useResponsiveStyles from '../../context/hooks/style-hooks/useResponsiveStyles'; -import { useTheme } from 'styled-components'; -import { - CardDetailContainer, - CardDetailRow, - CardIconWrapper, - CardTitleStyle, - CardValueStyle, - useCardDetailStyles, -} from './styles/cardStyles'; -import { useMode } from '../../context'; -import MDTypography from '../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; - -const CardDetails = ({ details, className }) => { - // details expected to be an array of objects with { icon, title, value } - const { theme } = useMode(); - // const theme2 = useTheme(); - // const styles = useCardDetailStyles(theme, theme2); - // const { getHeaderStyle, getButtonTypographyVariant } = - // useResponsiveStyles(theme); - - return ( - - {details.map(({ icon, title, value }, index) => ( - - {icon && {icon}} - - {title}: - {value || ''} - - - ))} - - ); -}; - -export default CardDetails; diff --git a/src/components/cards/CardDetailsContainer.jsx b/src/components/cards/CardDetailsContainer.jsx index bc1709b..7ca3a31 100644 --- a/src/components/cards/CardDetailsContainer.jsx +++ b/src/components/cards/CardDetailsContainer.jsx @@ -1,5 +1,15 @@ import React from 'react'; -import { Box, Grid } from '@mui/material'; +import { + Box, + Card, + CardContent, + CardHeader, + Grid, + List, + ListItem, + Stack, + Typography, +} from '@mui/material'; import { FaDragon, FaLevelUpAlt, @@ -27,36 +37,115 @@ const IconWrapper = styled(Box)(({ theme }) => ({ color: theme.palette.text.primary, })); -const CardDetailsContainer = ({ card, className }) => { +const iconDetails = [ + { icon: FaLevelUpAlt, title: 'Level' }, + { icon: FaVenusMars, title: 'Type' }, + { icon: FaDragon, title: 'Race' }, + { icon: FaRegLightbulb, title: 'Attribute' }, + { icon: GiAxeSword, title: 'ATK' }, + { icon: FaShieldAlt, title: 'DEF' }, +]; + +const textDetails = [ + { title: 'Description' }, + { title: 'Price' }, + { title: 'Rarity', isMultiValue: true, action: 'onRarityClick' }, + { title: 'Card Sets', isMultiValue: true, action: 'onRarityClick' }, +]; + +const inventoryDetails = [ + { title: 'Deck', key: 'deck' }, + { title: 'Collection', key: 'collection' }, + { title: 'Cart', key: 'cart' }, +]; +// Consolidating the rendering of both icon and text details into a single component. +const RenderDetailsSection = ({ details, card, className, handleAction }) => { + const { theme } = useMode(); + + return details.map((detail, index) => ( + + + + + ) : null + } + title={detail.title} + value={ + detail.isMultiValue + ? card?.card_sets?.map((set) => set[detail.title.toLowerCase()]) + : card?.[detail.title.toLowerCase()] + } + quantity={card?.quantity} + {...(detail.action ? { [detail.action]: handleAction } : {})} + /> + + )); +}; + +const RenderInventoryList = () => ( + + + + {/* Render the header separately */} + {inventoryDetails[0].title} + } + /> + + + {/* Skip the header in mapping */} + {inventoryDetails.slice(1).map((detail, index) => ( + {detail.title} + ))} + + + + + +); +const CardDetailsContainer = ({ + card, + className, + isIconSection, + isTextSection, + isInventorySection, + titles, +}) => { const { theme } = useMode(); + const handleAction = () => console.log('Action clicked'); return ( - - {[ - { icon: FaLevelUpAlt, title: 'Level', value: card?.level }, - { icon: FaVenusMars, title: 'Type', value: card?.type }, - { icon: FaDragon, title: 'Race', value: card?.race }, - { icon: FaRegLightbulb, title: 'Attribute', value: card?.attribute }, - { icon: GiAxeSword, title: 'ATK', value: card?.atk }, - { icon: FaShieldAlt, title: 'DEF', value: card?.def }, - ].map((detail, index) => ( - - - - - ) - } - title={detail.title} - value={detail.value} - quantity={card?.quantity} - /> - - ))} + + {isIconSection && ( + + )} + {isTextSection && ( + + )} + {isInventorySection && } ); }; diff --git a/src/components/cards/media/CardMediaSection.js b/src/components/cards/CardMediaSection.js similarity index 87% rename from src/components/cards/media/CardMediaSection.js rename to src/components/cards/CardMediaSection.js index e0a70c3..11f0f97 100644 --- a/src/components/cards/media/CardMediaSection.js +++ b/src/components/cards/CardMediaSection.js @@ -1,15 +1,13 @@ import React, { useEffect, forwardRef, useState } from 'react'; -import { Backdrop, CardMedia, Popover, Popper } from '@mui/material'; -import CardToolTip from '../CardToolTip'; +import CardToolTip from './CardToolTip'; import PropTypes from 'prop-types'; -import { useModalContext, useMode, usePopoverContext } from '../../../context'; +import { useMode } from '../../context'; import { MediaContainer, Media, MediaPopover, - Overlay, -} from '../../../context/hooks/style-hooks/usePortfolioStyles'; -import { useOverlay } from '../../../context/hooks/useOverlay'; +} from '../../context/hooks/style-hooks/usePortfolioStyles'; +import { useOverlay } from '../../context/hooks/useOverlay'; const CardMediaSection = forwardRef( ( { diff --git a/src/components/cards/CardTextSection.jsx b/src/components/cards/CardTextSection.jsx deleted file mode 100644 index 2000a90..0000000 --- a/src/components/cards/CardTextSection.jsx +++ /dev/null @@ -1,119 +0,0 @@ -import React from 'react'; -import Typography from '@mui/material/Typography'; -import Box from '@mui/material/Box'; -import { useMode } from '../../../context'; -import FlexBetween from '../../layout/REUSABLE_COMPONENTS/FlexBetween'; -import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; - -// Updated SimpleSectionHeader component with additional parameters -const CardTextSection = ({ - name, - price, - cartQuantity, - collectionQuantity, - deckQuantity, - cardSet, - setCode, - cardRarity, -}) => { - const { theme } = useMode(); - return ( - - {/* CARD TITLE SECTION: name */} - - {name} - - {/* CARD TITLE SUBSECTION: set name, rarity, set code */} - - {`Set: ${cardSet}`} - - {cardRarity} - - - {setCode} - - - {/* CARD TITLE SUBSECTION B: context quantities, price, price change */} - - - - {`Cart: ${cartQuantity}`} - - - {`Collection: ${collectionQuantity}`} - - - {`Deck: ${deckQuantity}`} - - - - - {`Price: ${price}`} - - {`Price Change: ${price}`} - - - - - - ); -}; - -export default CardTextSection; diff --git a/src/components/cards/CardToolTip.jsx b/src/components/cards/CardToolTip.jsx index d117750..a9617a8 100644 --- a/src/components/cards/CardToolTip.jsx +++ b/src/components/cards/CardToolTip.jsx @@ -1,16 +1,47 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - // StyledAttributeSpan, - StyledDescriptionSpan, - StyledToolTipBox, - StyledTooltip, - StyledTooltipTitle, -} from './styles/cardStyles'; import { useMode } from '../../context'; -import { Box, Typography } from '@mui/material'; +import { Box, Tooltip, Typography, Zoom } from '@mui/material'; import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; - +import styled from 'styled-components'; +export const StyledToolTipBox = styled(Box)(({ theme }) => ({ + width: 'auto', + border: `1px solid ${theme.palette.divider}`, + borderRadius: theme.shape.borderRadius, + padding: theme.spacing(2), + backgroundColor: theme.palette.backgroundA.lightest, + color: theme.palette.text.primary, + boxShadow: theme.shadows[3], + alignContent: 'flex-start', + alignItems: 'flex-start', + height: '100%', + maxWidth: 220, + position: 'relative', + '&::before': { + content: '""', + display: 'block', + paddingTop: '100%', + }, + '& > img': { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + objectFit: 'cover', + }, +})); +export const StyledTooltipTitle = styled('h4')(({ theme }) => ({ + fontWeight: 'bold', + marginBottom: theme.spacing(1), + alignContent: 'flex-start', + alignItems: 'flex-start', +})); +export const StyledDescriptionSpan = styled('span')(({ theme }) => ({ + display: 'block', + marginTop: theme.spacing(1), + flexGrow: 1, +})); const formatKey = (key) => key.replace(/_/g, ' ').replace(/^\w/, (c) => c.toUpperCase()); const StyledAttributeSpan = ({ theme, children }) => ( @@ -47,13 +78,12 @@ const createTooltip = (card, theme) => { boxShadow: theme.shadows[3], borderRadius: theme.shape.borderRadius, border: `1px solid ${theme.palette.divider}`, - w: 'auto', - h: '100%', + // w: 'auto', + // h: '100%', bgColor: theme.palette.backgroundC.lightest, - p: theme.spacing(2), + // p: theme.spacing(2), color: theme.palette.text.primary, alignContent: 'flex-start', - '&::before': { content: '""', display: 'block', @@ -81,11 +111,16 @@ const createTooltip = (card, theme) => { const CardToolTip = ({ card }) => { const { theme } = useMode(); return ( - + {createTooltip(card, theme)} - + ); }; diff --git a/src/components/cards/CarouselCard.jsx b/src/components/cards/CarouselCard.jsx deleted file mode 100644 index dda4859..0000000 --- a/src/components/cards/CarouselCard.jsx +++ /dev/null @@ -1,160 +0,0 @@ -import React, { useContext, useEffect, useRef, useState } from 'react'; -import SwipeableViews from 'react-swipeable-views'; -import { - Box, - Button, - Container, - Grid, - MobileStepper, - Typography, -} from '@mui/material'; -import { ModalContext } from '../../context/UTILITIES_CONTEXT/ModalContext/ModalContext'; -import GenericCard from './GenericCard'; -import { - MainContainer2, - CardMobile, - ChartContainerMobile, - CardDetails, - CardDetailsContainer, - ChartContainer, -} from '../../pages/pageStyles/StyledComponents'; -import { AspectRatio } from '@mui/joy'; -import { debounce } from 'lodash'; -import { useMode } from '../../context'; -import MDTypography from '../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; -const CarouselCard = ({ card }) => { - const { theme } = useMode(); - const { openModalWithCard } = useContext(ModalContext); // Assuming ModalContext is imported - const cardRef = useRef(null); - const chartRef = useRef(null); - const [chartDimensions, setChartDimensions] = useState({ - width: 0, - height: 0, - }); - const HEIGHT_TO_WIDTH_RATIO = 1; // Define the ratio here - - const handleClick = () => { - openModalWithCard(card); - }; - - useEffect(() => { - const handleResize = debounce(() => { - if (chartRef.current) { - const width = chartRef.current.offsetWidth; - const height = width * HEIGHT_TO_WIDTH_RATIO; - setChartDimensions({ width, height }); - } - }, 100); - - window.addEventListener('resize', handleResize); - handleResize(); - - return () => { - window.removeEventListener('resize', handleResize); - handleResize.cancel(); - }; - }, []); - return ( - - {/* */}{' '} - {/* Ensure the container doesn't overflow */} - - - - handleClick()} - context={'Collection'} - ref={cardRef} - /> - - - {/* Top section for card details */} - - - - - - {card?.name} - - - - - - Price: ${card?.latestPrice?.num || card?.price || 0} - - - Quantity: {card?.quantity} - - {/* */} - - - - {/* Bottom section for chart */} - - - - - {/* Chart component goes here */} - - - - - - - - {/*
*/} - - ); -}; - -export default CarouselCard; diff --git a/src/components/cards/GenericCard.jsx b/src/components/cards/GenericCard.jsx index 6f9be82..b52ff93 100644 --- a/src/components/cards/GenericCard.jsx +++ b/src/components/cards/GenericCard.jsx @@ -6,9 +6,9 @@ import React, { useState, } from 'react'; import { CardActions, Typography } from '@mui/material'; -import CardMediaSection from './media/CardMediaSection'; +import CardMediaSection from './CardMediaSection'; import GenericActionButtons from '../buttons/actionButtons/GenericActionButtons'; -import placeholderImage from '../../assets/images/placeholder.jpeg'; +import placeholder from '../../assets/images/placeholder.jpeg'; import { useModalContext } from '../../context/UTILITIES_CONTEXT/ModalContext/ModalContext'; import { PopoverContext } from '../../context/UTILITIES_CONTEXT/PopoverContext/PopoverContext'; import { Box } from '@mui/system'; @@ -31,6 +31,9 @@ import useSelectedContext from '../../context/hooks/useSelectedContext'; import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; const GenericCard = React.forwardRef((props, ref) => { const { card, context, page } = props; + const effectiveContext = + typeof context === 'object' ? context.pageContext : context; + const { theme } = useMode(); const cardRef = useRef(null); const [cardSize, setCardSize] = useState('md'); // Default to 'sm' @@ -57,19 +60,6 @@ const GenericCard = React.forwardRef((props, ref) => { const { selectedDeck, allDecks } = useDeckStore(); const { setContext, setIsContextSelected } = useSelectedContext(); const { isCardInContext } = useAppContext(); - - // const { isCardInContext } = useAppContext(); - // const isCardInContext = useCallback( - // (selectedCollection, selectedDeck, cartData, context, card) => { - // const cardsList = { - // Collection: selectedCollection?.cards, - // Deck: selectedDeck?.cards, - // Cart: cartData?.cart, - // }; - // return !!cardsList[context]?.find((c) => c?.id === card?.id); - // }, - // [context, selectedCollection, selectedDeck, cartData] - // ); const { openModalWithCard, setModalOpen, setClickedCard, isModalOpen } = useModalContext(); const { setHoveredCard, setIsPopoverOpen, hoveredCard } = @@ -99,7 +89,7 @@ const GenericCard = React.forwardRef((props, ref) => { }, [hoveredCard, card, setIsPopoverOpen]); const isInContext = isCardInContext(card); const name = card?.name; - const imgUrl = card?.card_images?.[0]?.image_url || placeholderImage; + const imgUrl = card?.card_images?.[0]?.image_url || placeholder; const price = `Price: ${ card?.latestPrice?.num || card?.price || @@ -115,18 +105,7 @@ const GenericCard = React.forwardRef((props, ref) => { allDecks: allDecks, }); let cardContent = null; - if (cardSize === 'sm') { - cardContent = ( - - - {name} - - - {price} - - - ); - } else if (cardSize !== 'xs') { + if (cardSize !== 'xs') { cardContent = ( @@ -135,18 +114,22 @@ const GenericCard = React.forwardRef((props, ref) => { {price} - {`Cart: ${isInContext ? cartQuantity : 'N/A'}`} - {`Collection: ${isInContext ? collectionQuantity : 'N/A'}`} - {`Deck: ${isInContext ? deckQuantity : 'N/A'}`} + {cardSize !== 'sm' && ( + <> + {`Cart: ${isInContext ? cartQuantity : 'N/A'}`} + {`Collection: ${isInContext ? collectionQuantity : 'N/A'}`} + {`Deck: ${isInContext ? deckQuantity : 'N/A'}`} + + )} ); } @@ -158,7 +141,7 @@ const GenericCard = React.forwardRef((props, ref) => { isRequired={true} imgUrl={imgUrl} card={card} - context={context} + context={effectiveContext} page={page} quantity={card?.quantity} isHovered={hoveredCard === card} @@ -177,8 +160,8 @@ const GenericCard = React.forwardRef((props, ref) => { > handleContextSelect(context)} + context={effectiveContext} + onClick={() => handleContextSelect(effectiveContext)} onSuccess={() => enqueueSnackbar( { diff --git a/src/components/cards/styles/cardStyles.jsx b/src/components/cards/styles/cardStyles.jsx index 3816ae6..7bf1580 100644 --- a/src/components/cards/styles/cardStyles.jsx +++ b/src/components/cards/styles/cardStyles.jsx @@ -33,77 +33,75 @@ export const StyledButton = styled(Button)(({ theme }) => ({ maxHeight: '60px', width: '100%', })); -export const StyledToolTipBox = styled(Box)(({ theme }) => ({ - width: 'auto', - border: `1px solid ${theme.palette.divider}`, - borderRadius: theme.shape.borderRadius, - padding: theme.spacing(2), - backgroundColor: theme.palette.backgroundA.lightest, - color: theme.palette.text.primary, - boxShadow: theme.shadows[3], - alignContent: 'flex-start', - alignItems: 'flex-start', - height: '100%', - maxWidth: 220, - position: 'relative', - '&::before': { - content: '""', - display: 'block', - paddingTop: '100%', - }, - '& > img': { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - objectFit: 'cover', - }, -})); -export const StyledTooltip = styled(Tooltip)(({ theme }) => ({ - // width: 'auto', - // border: `1px solid ${theme.palette.divider}`, - // borderRadius: theme.shape.borderRadius, - // padding: theme.spacing(2), - // backgroundColor: theme.palette.backgroundA.lightest, - // color: theme.palette.text.primary, - // boxShadow: theme.shadows[3], - // alignContent: 'flex-start', - // alignItems: 'flex-start', - // height: '100%', - // maxWidth: 220, - // position: 'relative', - // '&::before': { - // content: '""', - // display: 'block', - // paddingTop: '100%', - // }, - // '& > img': { - // position: 'absolute', - // top: 0, - // left: 0, - // width: '100%', - // height: '100%', - // objectFit: 'cover', - // }, -})); -export const StyledTooltipTitle = styled('h4')(({ theme }) => ({ - fontWeight: 'bold', - marginBottom: theme.spacing(1), - alignContent: 'flex-start', - alignItems: 'flex-start', -})); - -export const StyledDescriptionSpan = styled('span')(({ theme }) => ({ - display: 'block', - marginTop: theme.spacing(1), - flexGrow: 1, -})); - -export const StyledAttributeSpan = styled('span')(({ theme }) => ({ - display: 'block', - marginBottom: theme.spacing(0.5), -})); +// export const StyledToolTipBox = styled(Box)(({ theme }) => ({ +// width: 'auto', +// border: `1px solid ${theme.palette.divider}`, +// borderRadius: theme.shape.borderRadius, +// padding: theme.spacing(2), +// backgroundColor: theme.palette.backgroundA.lightest, +// color: theme.palette.text.primary, +// boxShadow: theme.shadows[3], +// alignContent: 'flex-start', +// alignItems: 'flex-start', +// height: '100%', +// maxWidth: 220, +// position: 'relative', +// '&::before': { +// content: '""', +// display: 'block', +// paddingTop: '100%', +// }, +// '& > img': { +// position: 'absolute', +// top: 0, +// left: 0, +// width: '100%', +// height: '100%', +// objectFit: 'cover', +// }, +// })); +// export const StyledTooltip = styled(Tooltip)(({ theme }) => ({ +// // width: 'auto', +// // border: `1px solid ${theme.palette.divider}`, +// // borderRadius: theme.shape.borderRadius, +// // padding: theme.spacing(2), +// // backgroundColor: theme.palette.backgroundA.lightest, +// // color: theme.palette.text.primary, +// // boxShadow: theme.shadows[3], +// // alignContent: 'flex-start', +// // alignItems: 'flex-start', +// // height: '100%', +// // maxWidth: 220, +// // position: 'relative', +// // '&::before': { +// // content: '""', +// // display: 'block', +// // paddingTop: '100%', +// // }, +// // '& > img': { +// // position: 'absolute', +// // top: 0, +// // left: 0, +// // width: '100%', +// // height: '100%', +// // objectFit: 'cover', +// // }, +// })); +// export const StyledTooltipTitle = styled('h4')(({ theme }) => ({ +// fontWeight: 'bold', +// marginBottom: theme.spacing(1), +// alignContent: 'flex-start', +// alignItems: 'flex-start', +// })); +// export const StyledDescriptionSpan = styled('span')(({ theme }) => ({ +// display: 'block', +// marginTop: theme.spacing(1), +// flexGrow: 1, +// })); +// export const StyledAttributeSpan = styled('span')(({ theme }) => ({ +// display: 'block', +// marginBottom: theme.spacing(0.5), +// })); export const CardDetailContainer = styled(Box)(({ theme }) => ({ padding: theme.spacing(2), diff --git a/src/components/componentHelpers.jsx b/src/components/componentHelpers.jsx index c5ce162..cf5f519 100644 --- a/src/components/componentHelpers.jsx +++ b/src/components/componentHelpers.jsx @@ -1,4 +1,3 @@ -// Desc: Helper functions for components export const getQuantity = ({ card, cartData, @@ -7,7 +6,6 @@ export const getQuantity = ({ selectedDeck, allDecks, }) => { - // Helper function to find the quantity of a card in a list of collections or decks const findCardQuantity = (collectionsOrDecks, cardId) => collectionsOrDecks?.reduce( (acc, item) => diff --git a/src/components/dialogs/CollectionDialog.jsx b/src/components/dialogs/CollectionDialog.jsx index 5147405..3df8c3b 100644 --- a/src/components/dialogs/CollectionDialog.jsx +++ b/src/components/dialogs/CollectionDialog.jsx @@ -1,132 +1,80 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; -import { - Avatar, - Box, - CssBaseline, - Dialog, - DialogContent, - Grid, - Paper, -} from '@mui/material'; -import { useCollectionStore, useFormContext, useMode } from '../../context'; +import { CssBaseline, DialogTitle, Divider } from '@mui/material'; +import { useMode } from '../../context'; +import CollectionForm from '../forms/CollectionForm'; // Adjusted import import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; -import AddCollectionForm from '../forms/AddCollectionForm'; -import UpdateCollectionForm from '../forms/UpdateCollectionForm'; import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; import MDTypography from '../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; -const CollectionDialog = ({ - open, - onClose, - isNew, - collectionData, - collectionMode, -}) => { - const { currentForm, setCurrentForm } = useFormContext(); - const { theme } = useMode(); +import { + DialogPaper, + StyledDialog, + StyledDialogContent, +} from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; +import MDAvatar from '../../layout/REUSABLE_COMPONENTS/MDAVATAR'; - useEffect(() => { - if (collectionMode === 'edit') { - setCurrentForm('updateCollectionForm'); - } - }, [collectionMode, setCurrentForm]); +const CollectionDialog = ({ open, onClose, isNew, collectionData }) => { + const { theme } = useMode(); + const actionType = isNew ? 'add' : 'update'; return ( - - - + + - {/* */} - - - - - - - - {currentForm === 'addCollectionForm' - ? 'Add a Collection' - : 'Update a Collection'} - - - - {currentForm === 'addCollectionForm' ? ( - - ) : ( - - )} - - - - - - - - + + + + + {isNew ? 'Add a Collection' : 'Update a Collection'} + + + + + + + + + + ); }; CollectionDialog.propTypes = { - // open: PropTypes.bool.isRequired, - // onClose: PropTypes.func.isRequired, + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, isNew: PropTypes.bool, collectionData: PropTypes.shape({ name: PropTypes.string, @@ -134,4 +82,4 @@ CollectionDialog.propTypes = { }), }; -export default withDynamicSnackbar(CollectionDialog); +export default CollectionDialog; diff --git a/src/components/dialogs/DeckDialog.jsx b/src/components/dialogs/DeckDialog.jsx new file mode 100644 index 0000000..e488cd3 --- /dev/null +++ b/src/components/dialogs/DeckDialog.jsx @@ -0,0 +1,85 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { CssBaseline, DialogTitle, Divider } from '@mui/material'; +import { useMode } from '../../context'; +import DeckForm from '../forms/DeckForm'; // Adjusted import +import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; +import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; +import MDTypography from '../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; +import { + DialogPaper, + StyledDialog, + StyledDialogContent, +} from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; +import MDAvatar from '../../layout/REUSABLE_COMPONENTS/MDAVATAR'; + +const DeckDialog = ({ open, onClose, isNew, deckData }) => { + const { theme } = useMode(); + const actionType = isNew ? 'add' : 'update'; + + return ( + + + + + + + + + + {isNew ? 'Add a Deck' : 'Update a Deck'} + + + + + + + + + + + ); +}; + +DeckDialog.propTypes = { + open: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + isNew: PropTypes.bool, + deckData: PropTypes.shape({ + name: PropTypes.string, + description: PropTypes.string, + }), +}; + +export default DeckDialog; diff --git a/src/components/dialogs/DeckEditPanel.js b/src/components/dialogs/DeckEditPanel.js deleted file mode 100644 index 532d924..0000000 --- a/src/components/dialogs/DeckEditPanel.js +++ /dev/null @@ -1,374 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { - Paper, - Typography, - Button, - TextField, - Chip, - Box, - FormControl, - InputLabel, - Select, - MenuItem, -} from '@mui/material'; -import { useDeckStore, useFormContext, useMode } from '../../context'; -import useSnackBar from '../../context/hooks/useSnackBar'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; -import SaveIcon from '@mui/icons-material/Save'; -import DeleteIcon from '@mui/icons-material/Delete'; -import { FormBox } from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; -import FormField from '../forms/reusable/FormField'; -const DeckEditPanel = ({ selectedDeck, showSnackbar }) => { - const { theme } = useMode(); - // const { showSnackbar } = useSnackBar(); // Assuming snackbar hook for user notifications - const { - formMethods, - // formStates: { errors, isSubmitting, ...formStates }, - register, - handleSubmit, - setValue, - watch, - formState: { errors, isSubmitting }, - reset, - setFormSchema, - onSubmit, - // Removal of onSubmit from destructuring as we will define a local handler - } = useFormContext(); - // Local state for dynamic fields like tags - const [newTag, setNewTag] = useState(''); - const tags = watch('tags', selectedDeck?.tags || []); - const color = watch('color'); - - useEffect(() => { - setFormSchema('updateDeckForm'); - }, [setFormSchema]); - - useEffect(() => { - if (selectedDeck) { - reset({ - name: selectedDeck.name, - description: selectedDeck.description, - tags: selectedDeck.tags || [], - color: selectedDeck.color || 'red', - }); - } - }, [selectedDeck, reset]); - - const handleAddNewTag = (newTag) => { - if (newTag && !tags.includes(newTag)) { - setValue('tags', [...tags, newTag.trim()]); - } - }; - const handleTagDelete = (tagToDelete) => { - setValue( - 'tags', - tags.filter((tag) => tag !== tagToDelete) - ); - }; - const handleFormSubmit = async (data) => { - try { - // Assuming `onSubmit` is a function passed via context or props that handles the actual submission - await onSubmit(data); // Adjust based on actual implementation - showSnackbar({ - message: 'Deck updated successfully', - variant: 'success', - }); - } catch (error) { - showSnackbar({ - message: error.message || 'An error occurred while updating the deck.', - variant: 'error', - }); - } - }; - // const handleDeleteClick = async () => { - // try { - // // await deleteDeck(selectedDeck._id); - // showSnackbar({ - // message: 'Deck deleted successfully', - // variant: 'warning', - // }); - // reset(); // Optionally reset form state - // } catch (error) { - // showSnackbar({ - // message: error.message || 'Failed to delete deck', - // variant: 'error', - // }); - // } - // }; - - return ( - - Deck Editor - - - - - - {tags && - tags?.map((tag, index) => ( - handleTagDelete(tag)} - /> - ))} - setNewTag(e.target.value)} - onBlur={handleAddNewTag} - onKeyDown={(e) => { - if (e.key === 'Enter') { - e.preventDefault(); - handleAddNewTag(); - } - }} - /> - - - Color - - - - - - - - - ); -}; - -export default withDynamicSnackbar(DeckEditPanel); // Wrap DeckEditPanel with withDynamicSnackbar HOC - -// import React, { useState, useEffect } from 'react'; -// import { -// Paper, -// Typography, -// Button, -// TextField, -// Chip, -// Stack, -// FormControl, -// InputLabel, -// Select, -// MenuItem, -// Box, -// Switch, -// } from '@mui/material'; -// import DeleteIcon from '@mui/icons-material/Delete'; -// import { -// useDeckStore, -// useMode, -// usePageContext, -// useFormContext, -// } from '../../context'; -// import { -// StyledFormControl, -// SwitchControl, -// StyledButton, -// StyledTextField, -// StyledSelect, -// } from '../../pages/pageStyles/StyledComponents'; -// import FormField from '../reusable/FormField'; - -// const DeckEditPanel = ({ selectedDeck, handleToggleEdit, isEditing }) => { -// const { returnDisplay, loadingStatus, setIsFormDataLoading } = -// usePageContext(); -// const { -// setFormType, -// register, -// handleSubmit, -// errors, -// currentFormType, -// isSubmitting, -// // formState: { errors, isSubmitting }, -// onSubmit, -// } = useFormContext(); - -// useEffect(() => { -// // Assuming setFormType is a function that can set the form context -// setFormType('addDeckForm'); -// }, [isNew, setFormType]); -// const isAddDeckForm = currentFormType === 'addDeckForm'; - -// const { theme } = useMode(); -// const tags = selectedDeck?.tags || []; - -// return ( -// -//
{ -// onSubmit(data); -// // Show snackbar after form submission -// const message = { -// title: 'Form Submitted', -// description: isAddDeckForm -// ? 'Deck added successfully' -// : 'Deck updated successfully', -// }; -// const variant = 'success'; -// showSnackbar(message, variant); -// })} -// > -// -// -// {/* */} -// -// -// {tags.map((tag, index) => ( -// handleTagDelete(tag)} -// /> -// ))} -// setNewTag(e.target.value)} -// size="small" -// sx={{ flex: 1 }} -// /> -// -// -// -// Color -// -// -// -// -// -// -// -//
-// ); -// }; -// export default DeckEditPanel; diff --git a/src/components/dialogs/homeDetailsModal/DetailsModal.jsx b/src/components/dialogs/DetailsModal.jsx similarity index 98% rename from src/components/dialogs/homeDetailsModal/DetailsModal.jsx rename to src/components/dialogs/DetailsModal.jsx index 1d210f1..0227f16 100644 --- a/src/components/dialogs/homeDetailsModal/DetailsModal.jsx +++ b/src/components/dialogs/DetailsModal.jsx @@ -13,7 +13,7 @@ import { } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import { FaGithub, FaExternalLinkAlt } from 'react-icons/fa'; -import { useModalContext } from '../../../context'; +import { useModalContext } from '../../context'; const DetailsModal = () => { const { featureData, detailsModalShow, closeDetailsModal } = diff --git a/src/components/dialogs/GenericCardDialog.jsx b/src/components/dialogs/GenericCardDialog.jsx new file mode 100644 index 0000000..8f11f19 --- /dev/null +++ b/src/components/dialogs/GenericCardDialog.jsx @@ -0,0 +1,359 @@ +// import React, { useCallback, useEffect, useState } from 'react'; +// import { +// Dialog, +// DialogTitle, +// DialogContent, +// Snackbar, +// Alert, +// Grid, +// Container, +// useMediaQuery, +// Slide, +// Fade, +// IconButton, +// Stack, +// Typography, +// Card, +// CardHeader, +// List, +// CardContent, +// ListItem, +// Backdrop, +// } from '@mui/material'; +// import useSelectedContext from '../../context/hooks/useSelectedContext'; +// import { useModalContext, useMode } from '../../context'; +// import CardMediaSection from '../cards/CardMediaSection'; +// import GenericActionButtons from '../buttons/actionButtons/GenericActionButtons'; +// import CloseIcon from '@mui/icons-material/Close'; +// import CardDetail from '../cards/CardDetail'; +// import { useOverlay } from '../../context/hooks/useOverlay'; +// import CardDetailsContainer from '../cards/CardDetailsContainer'; +// import { enqueueSnackbar } from 'notistack'; +// import { +// DialogPaper, +// StyledDialog, +// } from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; + +// const GenericCardDialog = (props) => { +// const { theme } = useMode(); +// const { generateOverlay, handleRarityClick, openOverlay, closeOverlay } = +// useOverlay(theme); +// const { +// open = false, +// transition = false, +// close, +// hideBackdrop = false, +// title = '', +// content = '', +// actions = '', +// dividers = false, +// closeIcon = true, +// // fullScreen = false, +// actionPosition = 'left', +// closeIconSx = { +// position: 'absolute', +// right: 8, +// top: 8, +// color: (theme) => theme.palette.grey[500], +// }, +// card, +// context, +// ...other +// } = props || {}; +// const fullScreen = useMediaQuery(theme.breakpoints.down('md')); +// const { closeModal } = useModalContext(); +// const [imageUrl, setImageUrl] = useState(card?.card_images[0]?.image_url); +// const [hasLoggedCard, setHasLoggedCard] = useState(false); +// const { setContext, setIsContextSelected } = useSelectedContext(); +// const handleAction = useCallback( +// (message, severity, error) => { +// enqueueSnackbar(message, severity); +// if (error) console.error('Action failed:', error); +// }, +// [enqueueSnackbar] +// ); +// useEffect(() => { +// if (open && card && !hasLoggedCard) { +// enqueueSnackbar('Card details loaded successfully.', 'success'); +// setHasLoggedCard(true); +// } +// return () => { +// if (!open) setHasLoggedCard(false); +// }; +// }, [open, card, enqueueSnackbar, hasLoggedCard]); +// // Context selection handling +// const handleContextSelect = useCallback( +// (newContext) => { +// setContext(newContext); +// setIsContextSelected(true); +// }, +// [setContext, setIsContextSelected] +// ); +// return ( +// +// +// +// {title} +// {closeIcon && ( +// +// +// +// )} +// +// +// +// +// +// +// {generateOverlay()} +// + +// {/* these two grid items are for the card details */} +// +// } +// title="Description" +// value={card?.desc} +// isTextSection={true} +// titles={['Description', 'Price', 'Rarity', 'Card Sets']} +// /> +// +// +// +// +// +// +// Inventory +// +// +// +// {/* */} +// {'Cart'} +// {'Deck'} +// {'Collection'} +// +// +// +// +// +// {['Deck', 'Collection', 'Cart'].map((mappedContext) => ( +// handleContextSelect(mappedContext)} +// onSuccess={() => +// handleAction( +// { +// title: 'Action successful', +// message: `Card added to ${mappedContext} successfully.`, +// }, +// 'success' +// ) +// } +// onFailure={(error) => +// handleAction( +// { +// title: 'Action failed', +// message: `Failed to add card to ${mappedContext}.`, +// }, +// 'error', +// error +// ) +// } +// /> +// ))} +// +// +// +// +// +// +// +// ); +// }; + +// export default GenericCardDialog; +import React, { useCallback, useEffect, useState } from 'react'; +import { + Dialog, + IconButton, + Slide, + Fade, + Typography, + Card, + CardHeader, + List, + ListItem, + CardContent, + useMediaQuery, + useTheme, + Grid, +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import CardMediaSection from '../cards/CardMediaSection'; +import GenericActionButtons from '../buttons/actionButtons/GenericActionButtons'; +import CardDetailsContainer from '../cards/CardDetailsContainer'; +import { useSelectedContext, useModalContext, useMode } from '../../context'; +import { useSnackbar } from 'notistack'; +import FlexBetween from '../../layout/REUSABLE_COMPONENTS/FlexBetween'; + +const GenericCardDialog = ({ + open = false, + transition = false, + onClose, + title = '', + card, + context, + ...otherProps +}) => { + const { theme } = useMode(); + const fullScreen = useMediaQuery(theme.breakpoints.down('md')); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + + const { closeModal } = useModalContext(); + const { enqueueSnackbar } = useSnackbar(); // Assuming useOverlay has enqueueSnackbar method + const [imageUrl, setImageUrl] = useState(card?.card_images[0]?.image_url); + + // Snackbar handlers (simplified for this example) + const handleAction = useCallback( + (message, variant) => { + enqueueSnackbar(message, { variant }); + }, + [enqueueSnackbar] + ); + + useEffect(() => { + if (open && card) { + handleAction('Card details loaded successfully.', 'success'); + } + }, [open, card, handleAction]); + + const handleContextSelect = useCallback( + (newContext) => { + // Context selection handling logic here + handleAction(`Card added to ${newContext} successfully.`, 'success'); + }, + [handleAction] + ); + + return ( + + + + + + + {title} + + + + + + + + + + + + + Cart + Deck + Collection + + + + + {['Deck', 'Collection', 'Cart'].map((mappedContext) => ( + handleContextSelect(mappedContext)} + /> + ))} + + + + + ); +}; + +export default GenericCardDialog; diff --git a/src/components/dialogs/LoginDialog.jsx b/src/components/dialogs/LoginDialog.jsx index 7649763..d4f90c0 100644 --- a/src/components/dialogs/LoginDialog.jsx +++ b/src/components/dialogs/LoginDialog.jsx @@ -9,40 +9,25 @@ import { DialogContent, DialogTitle, Divider, - FormControl, FormControlLabel, - Grid, - IconButton, - Link, - Paper, - Switch, Typography, - useMediaQuery, } from '@mui/material'; -import Brightness4Icon from '@mui/icons-material/Brightness4'; -import Brightness7Icon from '@mui/icons-material/Brightness7'; import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; // Adjust import paths as necessary -import LoginForm from '../forms/LoginForm'; -import SignupForm from '../forms/SignupForm'; import { useFormContext, useMode } from '../../context'; import useAuthDialog from '../../context/hooks/useAuthDialog'; // Adjust import paths as necessary import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; import MDTypography from '../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; import { - DialogContentsBox, - DialogPaer, DialogPaper, - FormPaper, StyledDialog, StyledDialogContent, } from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; import MDAvatar from '../../layout/REUSABLE_COMPONENTS/MDAVATAR'; -import { AuthModeSwitch } from '../../layout/REUSABLE_STYLED_COMPONENTS/SpecificStyledComponents'; -import AuthSwitch from '../buttons/other/AuthSwitch'; -import SimpleButton from '../../layout/REUSABLE_COMPONENTS/unique/SimpleButton'; - -function LoginDialog({ showSnackbar }) { +import RCSwitch from '../forms/reusable/RCSwitch'; +import LoginIcon from '@mui/icons-material/Login'; +import PersonAddIcon from '@mui/icons-material/PersonAdd'; +import AuthForm from '../forms/AuthForm'; +function LoginDialog() { const { theme, toggleColorMode, mode } = useMode(); const { toggleLoginDialog, isLoggedIn, logout } = useAuthDialog(); // const { currentForm, setFormSchema } = useFormContext(); @@ -64,6 +49,11 @@ function LoginDialog({ showSnackbar }) { logout(); toggleLoginDialog(); }; + const handleToggle = () => { + setFormSchema( + currentSchemaKey === 'loginForm' ? 'signupForm' : 'loginForm' + ); + }; const formTitle = currentSchemaKey === 'loginForm' ? 'Login' : 'Sign Up'; const signupMode = currentSchemaKey === 'signupForm'; @@ -118,40 +108,31 @@ function LoginDialog({ showSnackbar }) { > - + {formTitle} - {/* */} - - {/* */} + {/* */} + } + iconRight={} + /> - {currentSchemaKey === 'loginForm' ? ( - + + {/* {currentSchemaKey === 'loginForm' ? ( + ) : ( - - )} - {/* */} + + )} */} } label="Remember me" @@ -177,4 +158,4 @@ function LoginDialog({ showSnackbar }) { ); } -export default withDynamicSnackbar(LoginDialog); +export default LoginDialog; diff --git a/src/components/dialogs/SelectionErrorDialog.jsx b/src/components/dialogs/SelectionErrorDialog.jsx index 7b1e736..3388952 100644 --- a/src/components/dialogs/SelectionErrorDialog.jsx +++ b/src/components/dialogs/SelectionErrorDialog.jsx @@ -13,51 +13,33 @@ import PersonIcon from '@mui/icons-material/Person'; import AddIcon from '@mui/icons-material/Add'; import Typography from '@mui/material/Typography'; import { blue } from '@mui/material/colors'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; import { DialogContent, Slide } from '@mui/material'; +import useSnackbarManager from '../../context/hooks/useSnackbarManager'; const Transition = React.forwardRef(function Transition(props, ref) { return ; }); function SelectionErrorDialog(props) { - const { onClose, selectedValue, open, showSnackbar } = props; - const { - handleBackToCollections, - showCollections, - allCollections, - handleSelectCollection, - } = useSelectedCollection(); + const { onClose, selectedValue, open } = props; + const { allCollections } = useSelectedCollection(); + const { showSuccess, showError, showInfo } = useSnackbarManager(); // Using custom snackbar hook + const handleClose = () => { onClose(selectedValue); }; - // const handleListItemClick = (value) => { - // if (typeof value?._id === 'string') { - // showSnackbar('Not implemented yet', 'successs'); - // } else { - // showSnackbar(`${value} selected as backup account`, 'success'); - // } - // onClose(value); - // }; const handleListItemClick = React.useCallback( (collection) => { if (collection._id) { - showSnackbar('Not implemented yet', 'info'); // Assuming 'successs' was a typo, corrected to 'info' for a non-implemented feature + showInfo('Not implemented yet'); // Show an informational snackbar } else { - showSnackbar(`${collection} selected as backup account`, 'success'); + showSuccess(`${collection} selected as backup account`); // Show a success snackbar } onClose(collection); }, - [onClose, showSnackbar] - ); - const handleAction = React.useCallback( - (message, severity, error) => { - showSnackbar(message, severity); - if (error) console.error('Action failed:', error); - }, - [showSnackbar] + [onClose, showInfo, showSuccess] ); return ( @@ -87,40 +69,6 @@ function SelectionErrorDialog(props) { ))} - - handleListItemClick(collection)} - // onClick={() => handleContextSelect(mappedContext)} - onSuccess={() => - handleAction( - { - title: 'Action successful', - message: `Card added to ${selectedValue} successfully.`, - }, - 'success', - null - ) - } - onFailure={(error) => - handleAction( - { - title: 'Action failed', - message: `Failed to add card to ${selectedValue}.`, - }, - 'error', - error - ) - } - > - - - - - - - - @@ -130,7 +78,7 @@ function SelectionErrorDialog(props) { SelectionErrorDialog.propTypes = { onClose: PropTypes.func.isRequired, open: PropTypes.bool.isRequired, - selectedValue: PropTypes.string.isRequired, + selectedValue: PropTypes.string, }; -export default withDynamicSnackbar(SelectionErrorDialog); +export default SelectionErrorDialog; diff --git a/src/components/dialogs/stripeModal/StripeCheckoutModal.js b/src/components/dialogs/StripeCheckoutModal.js similarity index 96% rename from src/components/dialogs/stripeModal/StripeCheckoutModal.js rename to src/components/dialogs/StripeCheckoutModal.js index ab582fc..a6722ad 100644 --- a/src/components/dialogs/stripeModal/StripeCheckoutModal.js +++ b/src/components/dialogs/StripeCheckoutModal.js @@ -1,7 +1,7 @@ import React from 'react'; import { Modal, Fade, Box, Typography, Backdrop } from '@mui/material'; import { Elements } from '@stripe/react-stripe-js'; -import StripeForm from '../../forms/customerCheckoutForm/StripeForm'; +import StripeForm from '../forms/customerCheckoutForm/StripeForm'; import { loadStripe } from '@stripe/stripe-js'; const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY); diff --git a/src/components/dialogs/cardDialog/GenericCardDialog.jsx b/src/components/dialogs/cardDialog/GenericCardDialog.jsx deleted file mode 100644 index a0a0146..0000000 --- a/src/components/dialogs/cardDialog/GenericCardDialog.jsx +++ /dev/null @@ -1,230 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { - Dialog, - DialogTitle, - DialogContent, - Snackbar, - Alert, - Grid, - Container, - useMediaQuery, - Slide, - Fade, - IconButton, - Stack, - Typography, - Card, - CardHeader, - List, - CardContent, - ListItem, - Backdrop, -} from '@mui/material'; -import useSnackbar from '../../../context/hooks/useSnackBar'; -import useSelectedContext from '../../../context/hooks/useSelectedContext'; -import { useModalContext, useMode } from '../../../context'; -import CardMediaSection from '../../cards/media/CardMediaSection'; -import GenericActionButtons from '../../buttons/actionButtons/GenericActionButtons'; -import CloseIcon from '@mui/icons-material/Close'; -import CardDetail from '../../cards/CardDetail'; -import { useOverlay } from '../../../context/hooks/useOverlay'; -import CardDetailsContainer from '../../cards/CardDetailsContainer'; -import { enqueueSnackbar } from 'notistack'; -import { DialogPaper } from '../../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; - -const GenericCardDialog = (props) => { - const { theme } = useMode(); - const { generateOverlay, handleRarityClick, openOverlay, closeOverlay } = - useOverlay(theme); - const { - open = false, - transition = false, - close, - hideBackdrop = false, - title = '', - content = '', - actions = '', - dividers = false, - closeIcon = true, - // fullScreen = false, - actionPosition = 'left', - closeIconSx = { - position: 'absolute', - right: 8, - top: 8, - color: (theme) => theme.palette.grey[500], - }, - card, - context, - ...other - } = props || {}; - const fullScreen = useMediaQuery(theme.breakpoints.down('md')); - const { closeModal } = useModalContext(); - const [imageUrl, setImageUrl] = useState(card?.card_images[0]?.image_url); - // const { handleSnackBar, handleCloseSnackbar } = useSnackbar(); - const [hasLoggedCard, setHasLoggedCard] = useState(false); - const { setContext, setIsContextSelected } = useSelectedContext(); - const handleAction = useCallback( - (message, severity, error) => { - enqueueSnackbar(message, severity); - if (error) console.error('Action failed:', error); - }, - [enqueueSnackbar] - ); - useEffect(() => { - if (open && card && !hasLoggedCard) { - enqueueSnackbar('Card details loaded successfully.', 'success'); - setHasLoggedCard(true); - } - return () => { - if (!open) setHasLoggedCard(false); - }; - }, [open, card, enqueueSnackbar, hasLoggedCard]); - // Context selection handling - const handleContextSelect = useCallback( - (newContext) => { - setContext(newContext); - setIsContextSelected(true); - }, - [setContext, setIsContextSelected] - ); - return ( - - - - {title} - {closeIcon && ( - - - - )} - - - - - - - {generateOverlay()} - - - {/* these two grid items are for the card details */} - - } - title="Description" - value={card?.desc} - /> - } - title="Price" - value={card?.price} - /> - set.set_rarity)} - onRarityClick={handleRarityClick} - /> - set.set_code)} - onRarityClick={handleRarityClick} - /> - - - - {/* */} - - {/* these two grid items are for the related user inventory data and action buttons */} - {/* */} - - - - Inventory - - - - {/* */} - {'Cart'} - {'Deck'} - {'Collection'} - - - - - - {['Deck', 'Collection', 'Cart'].map((mappedContext) => ( - handleContextSelect(mappedContext)} - onSuccess={() => - handleAction( - { - title: 'Action successful', - message: `Card added to ${mappedContext} successfully.`, - }, - 'success' - ) - } - onFailure={(error) => - handleAction( - { - title: 'Action failed', - message: `Failed to add card to ${mappedContext}.`, - }, - 'error', - error - ) - } - /> - ))} - - - - - - - - ); -}; - -export default GenericCardDialog; diff --git a/src/components/forms/AddCollectionForm.jsx b/src/components/forms/AddCollectionForm.jsx deleted file mode 100644 index b64b9d8..0000000 --- a/src/components/forms/AddCollectionForm.jsx +++ /dev/null @@ -1,103 +0,0 @@ -import React from 'react'; -import { Box } from '@mui/material'; -import FormField from './reusable/FormField'; -import { LoadingButton } from '@mui/lab'; -import { useFormContext, useMode } from '../../context'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { - formSchemas, - getDefaultValuesFromSchema, -} from '../../context/UTILITIES_CONTEXT/FormContext/schemas'; -import { - FormBox, - FormFieldBox, -} from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; - -const AddCollectionForm = ({ showSnackbar }) => { - const formId = 'addCollectionForm'; - const { onSubmit } = useFormContext(); - const { theme } = useMode(); - const { - register, - handleSubmit, - formState: { errors, isSubmitting }, - } = useForm({ - resolver: zodResolver(formSchemas.addCollectionForm), - defaultValues: getDefaultValuesFromSchema(formSchemas.addCollectionForm), - }); - - const fields = [ - { name: 'name', label: 'Name', type: 'text' }, - { name: 'description', label: 'Description', type: 'text' }, - ]; - const onFormSubmit = async (data) => { - try { - // Simulate form submission - await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulated delay - onSubmit(data, formId); - showSnackbar( - { - title: 'Success', - description: "You've successfully added a new collection.", - }, - 'success' - ); // Adjust message as needed - } catch (error) { - // On error: - showSnackbar( - { - title: 'Error', - description: 'Failed to add collection. Please try again.', - }, - 'error' - ); // Adjust message as needed - } - }; - return ( - - - - - - - - - Create Collection - - - ); -}; - -export default withDynamicSnackbar(AddCollectionForm); diff --git a/src/components/forms/AddDeckForm.jsx b/src/components/forms/AddDeckForm.jsx deleted file mode 100644 index fea3735..0000000 --- a/src/components/forms/AddDeckForm.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import { useFormContext } from '../../context'; -import FormField from '../reusable/FormField'; -import { Button, Paper, Typography, Box } from '@mui/material'; -import SaveIcon from '@mui/icons-material/Save'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; - -const AddDeckForm = ({ showSnackbar }) => { - const { formStates, onSubmit } = useFormContext(); - const formId = 'addDeckForm'; // Assuming this is the formId for creating decks - - const { - register, - handleSubmit, - formState: { errors, isSubmitting }, - } = formStates[formId]; - - const onFormSubmit = async (data) => { - try { - await onSubmit(data, formId); - showSnackbar( - { - title: 'Success', - description: 'Deck created successfully.', - }, - 'success' - ); - } catch (error) { - showSnackbar( - { - title: 'Error', - description: 'Failed to create deck. Please try again.', - }, - 'error' - ); - } - }; - - return ( - - Create Deck -
- - - - - - -
- ); -}; -export default withDynamicSnackbar(AddDeckForm); // Wrap DeckEditPanel with withDynamicSnackbar HOC diff --git a/src/components/forms/AuthForm.jsx b/src/components/forms/AuthForm.jsx new file mode 100644 index 0000000..b089af4 --- /dev/null +++ b/src/components/forms/AuthForm.jsx @@ -0,0 +1,67 @@ +import React from 'react'; +import PersonAddIcon from '@mui/icons-material/PersonAdd'; +import PersonIcon from '@mui/icons-material/Person'; +import EmailIcon from '@mui/icons-material/Email'; +import LockIcon from '@mui/icons-material/Lock'; +import RCZodForm from './reusable/RCZodForm'; +import LoginIcon from '@mui/icons-material/Login'; + +const loginFields = [ + { + name: 'username', + label: 'Username', + type: 'text', + icon: , + field: 'username', + }, + { + name: 'password', + label: 'Password', + type: 'password', + icon: , + field: 'password', + }, +]; +const signupFields = [ + { + name: 'firstName', + label: 'First Name', + type: 'text', + icon: , + field: 'firstName', + }, + { + name: 'lastName', + label: 'Last Name', + type: 'text', + icon: , + field: 'lastName', + }, + { + name: 'email', + label: 'Email', + type: 'email', + icon: , + field: 'email', + }, +]; + +const AuthForm = ({ formType }) => { + // Combine forms to make signup forms + const combinedFields = [...signupFields, ...loginFields]; + const isSignup = formType === 'signupForm'; + const fields = isSignup ? combinedFields : loginFields; + const buttonLabel = isSignup ? 'Sign Up' : 'Login'; + const startIcon = isSignup ? : ; + + return ( + + ); +}; + +export default AuthForm; diff --git a/src/components/forms/CollectionForm.jsx b/src/components/forms/CollectionForm.jsx new file mode 100644 index 0000000..b139f7d --- /dev/null +++ b/src/components/forms/CollectionForm.jsx @@ -0,0 +1,71 @@ +import React, { useEffect } from 'react'; +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; +import { useFormContext, useMode } from '../../context'; +import useSnackbarManager from '../../context/hooks/useSnackbarManager'; +import RCZodForm from './reusable/RCZodForm'; + +// Common fields structure used in both add and update forms +const collectionFields = [ + { + name: 'name', + label: 'Name', + type: 'text', + required: true, + }, + { + name: 'description', + label: 'Description', + type: 'text', + required: true, + multiline: true, + rows: 4, + }, +]; + +const CollectionForm = ({ collectionData, actionType }) => { + const { setFormSchema, onSubmit } = useFormContext(); + const { theme } = useMode(); + // const { showSuccess, showError } = useSnackbarManager(); + + // Determine the schema name and button label based on the action type + const schemaName = + actionType === 'add' ? 'addCollectionForm' : 'updateCollectionForm'; + const buttonLabel = + actionType === 'add' ? 'Create Collection' : 'Update Collection'; + const startIcon = + actionType === 'add' ? : ; + + // useEffect(() => { + // if (collectionData && actionType === 'update') { + // setFormSchema(schemaName, collectionData); + // } + // }, [collectionData, setFormSchema, schemaName, actionType]); + + // const handleFormSubmit = async (data) => { + // const method = actionType === 'add' ? 'Add' : 'Update'; + // try { + // await onSubmit(data, schemaName, collectionData?._id); + // showSuccess( + // `You've successfully ${method.toLowerCase()}ed the collection.` + // ); + // } catch (error) { + // showError( + // `Failed to ${method.toLowerCase()} collection. Please try again.` + // ); + // } + // }; + + return ( + + ); +}; + +export default CollectionForm; diff --git a/src/components/forms/CustomSelector.js b/src/components/forms/CustomSelector.js deleted file mode 100644 index bc9e771..0000000 --- a/src/components/forms/CustomSelector.js +++ /dev/null @@ -1,93 +0,0 @@ -// import React from 'react'; -// import { useTheme } from '@mui/material/styles'; -// import OutlinedInput from '@mui/material/OutlinedInput'; -// import MenuItem from '@mui/material/MenuItem'; -// import FormControl from '@mui/material/FormControl'; -// import Select from '@mui/material/Select'; -// import PropTypes from 'prop-types'; - -// const ITEM_HEIGHT = 48; -// const ITEM_PADDING_TOP = 8; -// const MenuProps = { -// PaperProps: { -// style: { -// maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, -// width: 250, -// }, -// }, -// }; - -// function getStyles(name, selected, theme) { -// return { -// fontWeight: -// selected.indexOf(name) === -1 -// ? theme.typography.fontWeightRegular -// : theme.typography.fontWeightMedium, -// }; -// } - -// const CustomMultipleSelect = ({ -// items, -// selectedItems, -// onChange, -// placeholder, -// label, -// }) => { -// const theme = useTheme(); - -// const handleChange = (event) => { -// const { -// target: { value }, -// } = event; -// onChange(typeof value === 'string' ? value.split(',') : value); -// }; - -// return ( -// -// -// -// ); -// }; - -// CustomMultipleSelect.propTypes = { -// items: PropTypes.arrayOf(PropTypes.string).isRequired, -// selectedItems: PropTypes.arrayOf(PropTypes.string).isRequired, -// onChange: PropTypes.func.isRequired, -// placeholder: PropTypes.string, -// label: PropTypes.string, -// }; - -// CustomMultipleSelect.defaultProps = { -// placeholder: 'Select items', -// label: 'Custom select', -// }; - -// export default CustomMultipleSelect; diff --git a/src/components/forms/DeckForm.jsx b/src/components/forms/DeckForm.jsx new file mode 100644 index 0000000..7370632 --- /dev/null +++ b/src/components/forms/DeckForm.jsx @@ -0,0 +1,144 @@ +import React, { useEffect, useState } from 'react'; +import SaveIcon from '@mui/icons-material/Save'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { useFormContext } from '../../context'; +import RCZodForm from './reusable/RCZodForm'; + +const DeckForm = ({ actionType, deckData }) => { + const { onSubmit, setFormSchema, formMethods } = useFormContext(); + const isUpdateMode = actionType === 'update'; + const [tags, setTags] = useState(deckData?.tags || []); + + useEffect(() => { + setFormSchema('updateDeckForm'); + if (deckData) { + console.log('deckData:', deckData); + formMethods.reset({ + ...deckData, + tags: deckData?.tags?.join(', '), + color: deckData?.color || 'red', + }); + } + }, [deckData, setFormSchema, formMethods]); + + const handleAddTag = (tag) => { + if (tag && !tags.includes(tag)) { + setTags([...tags, tag]); + formMethods.setValue('tags', [...tags, tag].join(', ')); + } + }; + + const handleDeleteTag = (tagToDelete) => { + const updatedTags = tags.filter((tag) => tag !== tagToDelete); + setTags(updatedTags); + formMethods.setValue('tags', updatedTags.join(', ')); + }; + const updateDeckFields = [ + { name: 'name', label: 'Name', type: 'text', icon: null, required: true }, + { + name: 'description', + label: 'Description', + type: 'text', + multiline: true, + rows: 4, + icon: null, + required: false, + }, + { + name: 'tags', + label: 'Tags', + type: 'chips', + chipData: tags, + icon: null, + onAddChip: handleAddTag, + onDeleteChip: handleDeleteTag, + required: false, + }, + { + name: 'color', + label: 'Color', + type: 'select', + icon: null, + required: false, + options: [ + { value: 'red', label: 'Red' }, + { value: 'blue', label: 'Blue' }, + { value: 'green', label: 'Green' }, + { value: 'yellow', label: 'Yellow' }, + { value: 'purple', label: 'Purple' }, + { value: 'pink', label: 'Pink' }, + { value: 'orange', label: 'Orange' }, + { value: 'teal', label: 'Teal' }, + ], + }, + ]; + + const addDeckFields = [ + { name: 'name', label: 'Name', type: 'text', icon: null, required: true }, + { + name: 'description', + label: 'Description', + type: 'text', + multiline: true, + rows: 4, + icon: null, + required: false, + }, + ]; + + const formId = isUpdateMode ? 'updateDeckForm' : 'addDeckForm'; + const fields = isUpdateMode ? updateDeckFields : addDeckFields; + + React.useEffect(() => { + if (!isUpdateMode) { + setFormSchema(formId); + } + }, [setFormSchema, formId, isUpdateMode]); + + const handleSubmit = (data) => { + if (isUpdateMode) { + console.log('Submitting update deck data:', data); + onSubmit(data); + } else { + console.log('Add Deck Data:', data); + onSubmit(data); + } + }; + + const handleDelete = () => { + if (isUpdateMode) { + console.log('Deleting deck:', deckData._id); + onSubmit({ _id: deckData._id, delete: true }, formId); + } + }; + + return ( + } + initialValues={deckData} + additionalData={{ + deckId: deckData ? deckData?._id : null, + }} + additionalButtons={ + isUpdateMode + ? [ + { + label: 'Delete Deck', + onClick: handleDelete, + startIcon: , + color: 'error', + variant: 'contained', + disabled: !deckData, + }, + ] + : [] + } + defaultValues={isUpdateMode ? deckData : {}} + /> + ); +}; + +export default DeckForm; diff --git a/src/components/forms/LoginForm.jsx b/src/components/forms/LoginForm.jsx deleted file mode 100644 index 06fcbe1..0000000 --- a/src/components/forms/LoginForm.jsx +++ /dev/null @@ -1,152 +0,0 @@ -import React, { useEffect } from 'react'; -import { - Box, - Checkbox, - FormControlLabel, - Grid, - InputAdornment, - Link, -} from '@mui/material'; -import FormField from './reusable/FormField'; -import { LoadingButton } from '@mui/lab'; -import { useFormContext, useMode } from '../../context'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; -import { CopyrightOutlined } from '@mui/icons-material'; -import LockOutlinedIcon from '@material-ui/icons/LockOutlined'; -import AuthSwitch from '../buttons/other/AuthSwitch'; -import LoginIcon from '@mui/icons-material/Login'; -import PersonIcon from '@mui/icons-material/Person'; -import LockIcon from '@mui/icons-material/Lock'; -import { - FormBox, - FormFieldBox, -} from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; -import SimpleButton from '../../layout/REUSABLE_COMPONENTS/unique/SimpleButton'; -const baseButtonStyles = { - bgcolor: '#6a59ff', // background-color - borderColor: '#6a59ff', - borderWidth: 2, - borderStyle: 'solid', - display: 'flex', - flexGrow: 1, - alignItems: 'center', - justifyContent: 'center', - marginLeft: 'auto', - marginRight: 'auto', - marginBottom: '16px', - marginTop: '16px', - position: 'relative', - bottom: 0, - cursor: 'pointer', - transition: 'background-color 0.3s, border-color 0.3s, color 0.3s', - ':hover': { - fontWeight: 'bold', - bgcolor: '#4a6da7', - borderColor: '#34597f', - }, - ':focus': { - outline: '2px solid #62a4ff', - outlineOffset: 2, - }, -}; -const LoginForm = ({ showSnackbar, signupMode, toggleAuthMode, formLabel }) => { - const { formMethods, onSubmit, setFormSchema } = useFormContext(); - const { theme } = useMode(); - const { - register, - handleSubmit, - formState: { errors, isSubmitting }, - } = formMethods; - - useEffect(() => { - setFormSchema('loginForm'); - }, [setFormSchema]); - const fields = [ - { - name: 'username', - label: 'Username', - type: 'text', - icon: , - }, - { - name: 'password', - label: 'Password', - type: 'password', - icon: , - }, - ]; - - const onFormSubmit = (data) => { - onSubmit(data, 'loginForm') - .then(() => { - showSnackbar( - { title: 'Success', description: "You've successfully logged in." }, - 'success' - ); - }) - .catch((error) => { - showSnackbar( - { title: 'Error', description: 'Login failed. Please try again.' }, - 'error' - ); - }); - }; - return ( - - {fields.map((field, index) => ( - - {field.icon} - ), - }} - /> - {errors[field.name] && ( - {errors[field.name].message} - )} - - ))} - {/* */} - } - fullWidth - sx={{ - background: theme.palette.backgroundG.light, - borderColor: theme.palette.backgroundG.light, - borderWidth: 2, - '&:hover': { background: theme.palette.backgroundG.default }, - '&:focus': { background: theme.palette.backgroundG.default }, - }} - > - Login - - - ); -}; - -export default withDynamicSnackbar(LoginForm); diff --git a/src/components/forms/OptionsComponent.jsx b/src/components/forms/OptionsComponent.jsx new file mode 100644 index 0000000..662faef --- /dev/null +++ b/src/components/forms/OptionsComponent.jsx @@ -0,0 +1,140 @@ +// OptionsComponent.jsx +import React, { useEffect } from 'react'; +import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; +import { Autocomplete, Grid, TextField } from '@mui/material'; +import { + FormBox, + FormFieldBox, +} from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; +import FormField from './reusable/FormField'; +import { useFormContext, useMode } from '../../context'; +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; + +const OptionsComponent = ({ + canSearch, + search, + handleSearchChange, + pageSize, + setPageSize, + pageOptions, + // schemaName, +}) => { + const { theme } = useMode(); + const schemaName = 'collectionSearchForm'; + const buttonLabel = 'Search'; + const startIcon = ; + const collectionSearchFields = [ + { + name: 'searchTerm', + label: 'Search', + type: 'text', + required: true, + value: search, + }, + ]; + const { + formMethods, + onSubmit, + handleChange, + setFormSchema, + formState: { errors, isSubmitting }, + // getValues, + handleSearchTermChange, + } = useFormContext(); + + useEffect(() => { + setFormSchema(schemaName); + }, [setFormSchema, schemaName]); + + const onFormSubmit = (data) => { + onSubmit(data, schemaName); + }; + return ( + + + {canSearch && ( + +
+ {collectionSearchFields?.map((field, index) => ( + + + + ))} +
+
+ )} + + setPageSize(parseInt(newValue, 10))} + options={pageOptions?.map((option) => option.toString())} + renderInput={(params) => ( + + )} + /> + +
+
+ ); +}; + +export default OptionsComponent; diff --git a/src/components/forms/ProfileForm.jsx b/src/components/forms/ProfileForm.jsx deleted file mode 100644 index 60791ca..0000000 --- a/src/components/forms/ProfileForm.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { TextField, Button } from '@mui/material'; -import { useFormContext } from '../../context'; -import FormField from './reusable/FormField'; - -const ProfileForm = ({ userName, name, age, status, onSave }) => { - const { forms, handleChange, handleSubmit } = useFormContext(); - const profileValues = forms?.updateUserDataForm || {}; - const formType = 'updateUserDataForm'; - const handleFormSubmit = (event) => { - event.preventDefault(); // Prevent the default form submission behavior - handleSubmit(formType)(event); // Pass the event to your form handler - }; - - return ( -
- - - - - - - - ); -}; - -ProfileForm.propTypes = { - username: PropTypes.string, - firstName: PropTypes.string, - lastName: PropTypes.string, - - age: PropTypes.string, - status: PropTypes.string, - onSave: PropTypes.func.isRequired, -}; - -export default ProfileForm; diff --git a/src/components/forms/SearchForm.jsx b/src/components/forms/SearchForm.jsx index 2902961..445eecb 100644 --- a/src/components/forms/SearchForm.jsx +++ b/src/components/forms/SearchForm.jsx @@ -1,103 +1,32 @@ -import React, { useCallback, useEffect } from 'react'; -import { Button, Grid } from '@mui/material'; -import { useFormContext, useMode, usePageContext } from '../../context'; -import { - StyledFormBox, - StyledFormPaper, -} from '../../pages/pageStyles/StyledComponents'; -import { FormBox } from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; -import { LoadingButton } from '@mui/lab'; -import FormField from './reusable/FormField'; +import React from 'react'; +import SearchIcon from '@mui/icons-material/Search'; +import RCZodForm from './reusable/RCZodForm'; +import { useFormContext } from '../../context'; -const SearchForm = ({ onFocus, onBlur }) => { - const { theme } = useMode(); - const formId = 'searchForm'; // Define the form ID for schema selection - const { - formMethods, - formStates: { errors, isSubmitting, ...formStates }, - onSubmit, - handleFieldChange, - handleSearchTermChange, - // handleChange, - handleBlur, - handleFocus, - forms, - handleChange, - setFormSchema, - } = useFormContext(); - - useEffect(() => { - setFormSchema(formId); - }, [setFormSchema]); - - // const handleChange = (e) => { - // const { name, value } = e.target; - // if (name === 'searchTerm') { - // handleSearchTermChange(value); // Call the method when searchTerm changes - // } - // handleFieldChange(formId, name, value); // Continue to update the form state as before - // }; - - // const { searchTerm } = formStates?.searchForm; - const handleKeyPress = (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - formMethods.handleSubmit((data) => { - onSubmit(data); - }); - } - }; +const SearchForm = () => { + const { formMethods, formStates } = useFormContext(); + const { forms } = formStates; + const { handleChange, handleFocus, handleBlur } = formMethods; + const searchFields = [ + { + name: 'searchTerm', + label: 'Search for cards', + type: 'text', + required: false, + value: forms?.searchForm?.searchTerm || '', + onChange: handleChange, + onFocus: handleFocus, + onBlur: handleBlur, + }, + ]; return ( - - - onSubmit(data, 'searchForm') - )} - > - - {errors?.searchTerm && ( - -

{errors?.searchTerm?.message}

-
- )} - - {isSubmitting ? 'Loading...' : 'Search'} - - {errors?.root &&

{errors?.root?.message}

} -
-
+ } + /> ); }; diff --git a/src/components/forms/SignupForm.jsx b/src/components/forms/SignupForm.jsx deleted file mode 100644 index 3e6e74f..0000000 --- a/src/components/forms/SignupForm.jsx +++ /dev/null @@ -1,168 +0,0 @@ -import React, { useEffect } from 'react'; -import { Box, Button } from '@mui/material'; -import FormField from './reusable/FormField'; -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { - formSchemas, - getDefaultValuesFromSchema, -} from '../../context/UTILITIES_CONTEXT/FormContext/schemas'; // Ensure this path is correct -import { LoadingButton } from '@mui/lab'; -import { useFormContext, useMode, usePageContext } from '../../context'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; -import AuthSwitch from '../buttons/other/AuthSwitch'; -import PersonAddIcon from '@mui/icons-material/PersonAdd'; - -import { - FormBox, - FormFieldBox, -} from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; -const baseButtonStyles = { - bgcolor: '#6a59ff', // background-color - borderColor: '#6a59ff', - borderWidth: 2, - borderStyle: 'solid', - display: 'flex', - flexGrow: 1, - alignItems: 'center', - justifyContent: 'center', - marginLeft: 'auto', - marginRight: 'auto', - marginBottom: '16px', - marginTop: '16px', - position: 'relative', - bottom: 0, - cursor: 'pointer', - transition: 'background-color 0.3s, border-color 0.3s, color 0.3s', - ':hover': { - fontWeight: 'bold', - bgcolor: '#4a6da7', - borderColor: '#34597f', - }, - ':focus': { - outline: '2px solid #62a4ff', - outlineOffset: 2, - }, -}; -const SignupForm = ({ - showSnackbar, - signupMode, - toggleAuthMode, - formLabel, -}) => { - const { theme } = useMode(); - const { formMethods, onSubmit, setFormSchema } = useFormContext(); - - const { - register, - handleSubmit, - formState: { errors, isSubmitting }, - } = formMethods; - - useEffect(() => { - setFormSchema('signupForm'); - }, [setFormSchema]); - const fields = [ - { name: 'firstName', label: 'First Name', type: 'text' }, - { name: 'lastName', label: 'Last Name', type: 'text' }, - { name: 'email', label: 'Email', type: 'email' }, - { name: 'username', label: 'Username', type: 'text' }, - { name: 'password', label: 'Password', type: 'password' }, - ]; - - // Updated onFormSubmit to directly use onSubmit from context - const onFormSubmit = (data) => { - onSubmit(data, 'signupForm') - .then(() => { - showSnackbar( - { title: 'Success', description: "You've successfully signed up." }, - 'success' - ); - }) - .catch((error) => { - showSnackbar( - { title: 'Error', description: 'Signup failed. Please try again.' }, - 'error' - ); - }); - }; - // useEffect(() => { - // setIsFormDataLoading(isSubmitting); - // console.log('isSubmitting:', isSubmitting); - // }, [isSubmitting, setIsFormDataLoading]); - return ( - - {fields.map((field, index) => ( - - - - ))} - - {errors.form && ( - {errors.form.message} - )} - - } - fullWidth - sx={{ - background: theme.palette.backgroundG.light, - borderColor: theme.palette.backgroundG.light, - borderWidth: 2, - '&:hover': { background: theme.palette.backgroundG.default }, - '&:focus': { background: theme.palette.backgroundG.default }, - }} - // type="submit" - // variant="contained" - // loading={isSubmitting} - // onClick={() => handleSubmit(onFormSubmit)()} - // color="primary" - // sx={{ - // background: theme.palette.backgroundE.darker, - // borderColor: theme.palette.backgroundB.darkest, - // borderWidth: 2, - // flexGrow: 1, - // justifySelf: 'bottom', - // bottom: 0, - // mx: 'auto', - // my: 2, - // '&:hover': { - // fontWeight: 'bold', - // background: theme.palette.backgroundF.dark, - // borderColor: theme.palette.backgroundB.darkest, - // border: `1px solid ${theme.palette.backgroundB.darkest}`, - // }, - // }} - // fullWidth - > - Sign Up - - - ); -}; - -export default withDynamicSnackbar(SignupForm); diff --git a/src/components/forms/UpdateCollectionForm.jsx b/src/components/forms/UpdateCollectionForm.jsx deleted file mode 100644 index 80cfbc8..0000000 --- a/src/components/forms/UpdateCollectionForm.jsx +++ /dev/null @@ -1,116 +0,0 @@ -import React, { useEffect } from 'react'; -import { Box } from '@mui/material'; -import { LoadingButton } from '@mui/lab'; -import { useFormContext, useMode } from '../../context'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { - formSchemas, - getDefaultValuesFromSchema, -} from '../../context/UTILITIES_CONTEXT/FormContext/schemas'; -import FormField from './reusable/FormField'; -import { - FormBox, - FormFieldBox, -} from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; - -const UpdateCollectionForm = ({ showSnackbar, collectionData }) => { - const formId = 'updateCollectionForm'; - const { onSubmit } = useFormContext(); - const { theme } = useMode(); - const { - register, - handleSubmit, - reset, - formState: { errors, isSubmitting }, - } = useForm({ - resolver: zodResolver(formSchemas.updateCollectionForm), - defaultValues: - collectionData || - getDefaultValuesFromSchema(formSchemas.updateCollectionForm), - }); - - // Automatically reset form fields to the passed collection data - React.useEffect(() => { - reset(collectionData); - }, [collectionData, reset]); - - const fields = [ - { name: 'name', label: 'Name', type: 'text' }, - { name: 'description', label: 'Description', type: 'text' }, - ]; - - const onFormSubmit = async (data) => { - try { - // Simulate form submission - await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulated delay - - onSubmit(data, formId, collectionData._id); // Adjust to call the actual update function - - // On success: - showSnackbar( - { - title: 'Success', - description: "You've successfully updated the collection.", - }, - 'success' - ); - } catch (error) { - // On error: - showSnackbar( - { - title: 'Error', - description: 'Failed to update collection. Please try again.', - }, - 'error' - ); - } - }; - - return ( - - - - - - - - - Update Collection - - - ); -}; - -export default withDynamicSnackbar(UpdateCollectionForm); diff --git a/src/components/forms/UpdateDeckForm.jsx b/src/components/forms/UpdateDeckForm.jsx deleted file mode 100644 index 4c8d719..0000000 --- a/src/components/forms/UpdateDeckForm.jsx +++ /dev/null @@ -1,97 +0,0 @@ -import React, { useEffect } from 'react'; -import { useFormContext, useMode } from '../../context'; -import FormField from '../reusable/FormField'; -import { Button, Paper, Typography, Box } from '@mui/material'; -import SaveIcon from '@mui/icons-material/Save'; -import DeleteIcon from '@mui/icons-material/Delete'; -import { withDynamicSnackbar } from '../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; -import { FormBox } from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; - -const UpdateDeckForm = ({ selectedDeck, showSnackbar }) => { - const { theme } = useMode(); - const { formStates, onSubmit } = useFormContext(); - const formId = 'updateDeckForm'; // Assuming this is the formId for updating decks - - const { - register, - handleSubmit, - reset, - formState: { errors, isSubmitting }, - } = formStates[formId]; - - useEffect(() => { - if (selectedDeck) { - reset(selectedDeck); - } - }, [selectedDeck, reset]); - - const onFormSubmit = async (data) => { - try { - await onSubmit({ ...data, _id: selectedDeck._id }, formId); - showSnackbar( - { - title: 'Success', - description: 'Deck updated successfully.', - }, - 'success' - ); - } catch (error) { - showSnackbar( - { - title: 'Error', - description: 'Failed to update deck. Please try again.', - }, - 'error' - ); - } - }; - - return ( - - Update Deck - - - - - - - - - - ); -}; -export default withDynamicSnackbar(UpdateDeckForm); // Wrap DeckEditPanel with withDynamicSnackbar HOC diff --git a/src/components/forms/cleanup/EditUserProfileForm.jsx b/src/components/forms/cleanup/EditUserProfileForm.jsx deleted file mode 100644 index 29fdfb0..0000000 --- a/src/components/forms/cleanup/EditUserProfileForm.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import { Typography } from '@mui/material'; -import { useUserContext } from '../../context/UserContext/UserContext'; -import { usePageContext } from '../../context/PageContext/PageContext'; -import { PageHeader, PageContent } from '../../layout'; -import { FormWrapper, StyledTextField, StyledButton } from './styled'; -import { useFormContext, useMode } from '../../../context'; - -const EditUserProfileForm = () => { - const { theme } = useMode(); - const { user, fetchUserData } = useUserContext(); - const userId = user?.id; - const { isPageLoading, setIsPageLoading, setPageError } = usePageContext(); - const { forms, handleChange, handleSubmit, formErrors } = useFormContext(); - const userDataValues = forms.updateUserDataForm; - const userDataErrors = formErrors.updateUserDataForm || {}; - - const handleFormSubmit = (event) => { - event.preventDefault(); - handleSubmit('updateUserDataForm'); - }; - return ( - <> - - - Edit User Settings - - - -
- - - - - - Save - - -
- - ); -}; - -export default EditUserProfileForm; diff --git a/src/components/forms/cleanup/EditUserSettingsForm.jsx b/src/components/forms/cleanup/EditUserSettingsForm.jsx deleted file mode 100644 index 51f7cad..0000000 --- a/src/components/forms/cleanup/EditUserSettingsForm.jsx +++ /dev/null @@ -1,124 +0,0 @@ -// Import dependencies -import React, { useState, useEffect } from 'react'; -import { useCookies } from 'react-cookie'; -import { Box, Button, TextField, Typography } from '@mui/material'; -import { useUserContext } from '../../context/UserContext/UserContext'; -import { usePageContext } from '../../context/PageContext/PageContext'; -import { PageLayout, PageHeader, PageContent } from '../../layout'; -import LoadingIndicator from '../../reusable/indicators/LoadingIndicator'; -import ErrorIndicator from '../../reusable/indicators/ErrorIndicator'; -import ThemeToggleButton from '../../components/reusable/buttons/ThemeToggleButton'; -import { useMode } from '../../../context'; - -const EditUserSettingsForm = ({ user }) => { - const [cookies] = useCookies(['user']); - const { theme } = useMode(); - const { user: currentUser } = cookies; - const userId = currentUser?.id; - - const { - userData, - updateUser, - fetchUserData, - isLoading, - setIsLoading, - pageError, - setPageError, - logPageData, - } = useUserContext(); - - const { isPageLoading, setIsPageLoading } = usePageContext(); - - const [formState, setFormState] = useState({ - email: '', - password: '', - confirmPassword: '', - }); - - const [formError, setFormError] = useState(null); - - const { email, password, confirmPassword } = formState; - - const handleChange = (e) => { - const { name, value } = e.target; - setFormState((prev) => ({ ...prev, [name]: value })); - }; - - const handleSubmit = async (e) => { - e.preventDefault(); - if (password !== confirmPassword) { - setFormError('Passwords do not match'); - return; - } - try { - setIsLoading(true); - await updateUser(userId, formState); - await fetchUserData(); - logPageData('EditUserSettingsForm', userData); - } catch (e) { - console.error('Failed to update user settings:', e); - setPageError(e); - } finally { - setIsLoading(false); - } - }; - - if (isPageLoading) return ; - if (pageError) return ; - if (isLoading) return ; - - return ( - <> - - - Edit User Settings - - - -
- - - - - - - - -
- - ); -}; - -export default EditUserSettingsForm; diff --git a/src/components/forms/formsConfig.jsx b/src/components/forms/formsConfig.jsx index a2757f4..0038a3d 100644 --- a/src/components/forms/formsConfig.jsx +++ b/src/components/forms/formsConfig.jsx @@ -1,11 +1,71 @@ +import LoginIcon from '@mui/icons-material/Login'; +import PersonIcon from '@mui/icons-material/Person'; +import LockIcon from '@mui/icons-material/Lock'; +import EmailRoundedIcon from '@mui/icons-material/EmailRounded'; +import VerifiedUserRoundedIcon from '@mui/icons-material/VerifiedUserRounded'; +import DescriptionRoundedIcon from '@mui/icons-material/DescriptionRounded'; +import AccountCircleRoundedIcon from '@mui/icons-material/AccountCircleRounded'; +import FindInPageRoundedIcon from '@mui/icons-material/FindInPageRounded'; +// TODO: ADD EMAIL ICON +// TODO: ADD First ICON +// TODO: ADD Last ICON +// TODO: ADD General Name ICON +// TODO: ADD Description ICON + const auth = [ - { name: 'firstName', label: 'First Name', type: 'text' }, - { name: 'lastName', label: 'Last Name', type: 'text' }, - { name: 'email', label: 'Email', type: 'email' }, - { name: 'username', label: 'Username', type: 'text' }, - { name: 'password', label: 'Password', type: 'password' }, + { + name: 'firstName', + label: 'First Name', + type: 'text', + icon: , + }, + { + name: 'lastName', + label: 'Last Name', + type: 'text', + icon: , + }, + { name: 'email', label: 'Email', type: 'email', icon: }, + { + name: 'username', + label: 'Username', + type: 'text', + icon: , + }, + { + name: 'password', + label: 'Password', + type: 'password', + icon: , + }, ]; const collection = [ - { name: 'name', label: 'Name', type: 'text' }, - { name: 'description', label: 'Description', type: 'text' }, + { + name: 'name', + label: 'Name', + type: 'text', + icon: , + required: true, + multiline: false, + }, + { + name: 'description', + label: 'Description', + type: 'text', + icon: , + required: true, + multiline: true, + rows: 4, + }, ]; +const deck = [ + { name: 'name', label: 'Name', type: 'text', icon: <> }, + { name: 'description', label: 'Description', type: 'text', icon: <> }, +]; +const formData = { + auth, + collection, + deck, +}; + +export default formData; diff --git a/src/components/forms/hooks/useSubmitHandler.jsx b/src/components/forms/hooks/useSubmitHandler.jsx new file mode 100644 index 0000000..5b3287c --- /dev/null +++ b/src/components/forms/hooks/useSubmitHandler.jsx @@ -0,0 +1,39 @@ +import { useCallback } from 'react'; +import useSnackbarManager from '../../../context/hooks/useSnackbarManager'; + +function useSubmitHandler( + onSubmit, + successTitle, + successDescription, + errorDescription +) { + const { showSuccess, showError } = useSnackbarManager(); + + return useCallback( + (data, formType) => { + onSubmit(data, formType) + .then(() => { + // Using showSuccess for positive feedback + showSuccess( + `${successTitle}: ${successDescription.replace('{timeRange}', data?.timeRange)}` + ); + }) + .catch((error) => { + // Using showError for negative feedback + showError( + `${errorDescription.replace('{timeRange}', data?.timeRange)}: ${error}` + ); + }); + }, + [ + onSubmit, + showSuccess, + showError, + successTitle, + successDescription, + errorDescription, + ] + ); +} + +export default useSubmitHandler; diff --git a/src/components/forms/index.jsx b/src/components/forms/index.jsx index f8ca92c..53c6b15 100644 --- a/src/components/forms/index.jsx +++ b/src/components/forms/index.jsx @@ -1,2 +1,20 @@ -import CollectionForm from './CollectionForm'; -import ProfileForm from './ProfileForm'; +import SignupForm from './SignupForm'; +import LoginForm from './LoginForm'; +import SearchForm from './SearchForm'; +import UpdateCollectionForm from './UpdateCollectionForm'; +import AddCollectionForm from './AddCollectionForm'; +import AddDeckForm from './AddDeckForm'; +import UpdateDeckForm from './UpdateDeckForm'; +import SearchSettingsForm from './SearchSettingsForm'; + +const forms = { + signup: SignupForm, + login: LoginForm, + search: SearchForm, + updateCollection: UpdateCollectionForm, + addCollection: AddCollectionForm, + updateDeck: UpdateDeckForm, + addDeck: AddDeckForm, +}; + +export default forms; diff --git a/src/components/forms/reusable/FormField.jsx b/src/components/forms/reusable/FormField.jsx index 776600d..edce8e4 100644 --- a/src/components/forms/reusable/FormField.jsx +++ b/src/components/forms/reusable/FormField.jsx @@ -3,8 +3,17 @@ import PropTypes from 'prop-types'; // Import PropTypes import { useMode } from '../../../context'; import { StyledTextField } from '../../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; -const FormField = ({ name, register, errors, ...props }) => { +const FormField = ({ + name, + register, + initialValue, + value, + errors, + ...props +}) => { const { theme } = useMode(); + const showLabel = !initialValue; + return ( { fullWidth variant="outlined" theme={theme} + InputLabelProps={{ + shrink: showLabel ? undefined : true, + }} {...props} + sx={{}} /> ); }; @@ -24,10 +37,8 @@ FormField.propTypes = { name: PropTypes.string.isRequired, // Name is a required string register: PropTypes.func.isRequired, // Register is a required function errors: PropTypes.object, // Errors is an object, not necessarily required - theme: PropTypes.object, // Theme is an object, not necessarily required - // CONTROLLED FIELDS - value: PropTypes.string, // Value is a string, not necessarily required + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // Value can be string or number }; // Define default props if there are any optional props diff --git a/src/components/forms/reusable/RCSwitch.jsx b/src/components/forms/reusable/RCSwitch.jsx new file mode 100644 index 0000000..edaae5e --- /dev/null +++ b/src/components/forms/reusable/RCSwitch.jsx @@ -0,0 +1,98 @@ +import React from 'react'; +import { Switch, FormControlLabel, FormControl } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import { useMode } from '../../../context'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { useCookies } from 'react-cookie'; + +// Convert SVG icons to data URI +const convertSvg = (svg) => { + const markup = renderToStaticMarkup(svg); + const encoded = encodeURIComponent(markup); + const dataUri = `url('data:image/svg+xml;utf8,${encoded}')`; + return dataUri; +}; + +// Generic switch component +const RCSwitch = ({ + checked, + onChange, + labelLeft, + labelRight, + iconLeft, + iconRight, + size = { width: 50, height: 14, thumbWidth: 22, thumbHeight: 22 }, +}) => { + const { theme } = useMode(); + const colors = theme.palette.chartTheme; + const cookies = useCookies('colorMode'); + const mode = cookies.colorMode; + const green = colors.greenAccent.light; + const lihhtgreen = colors.greenAccent.default; + const calculateTransform = () => { + const trackPadding = 2; // Adjust based on your design + const transformDistance = size.width - size.thumbWidth - trackPadding; + return transformDistance; + }; + const SwitchBaseStyles = { + // pt: 1.5, + // pl: 1.5, + my: 1.7, + // ml: 1.5, + '&.Mui-checked': { + transform: `translateX(${calculateTransform()}px)`, + color: theme.palette.common.white, + '& + .MuiSwitch-track': { + backgroundColor: theme.palette.primary.main, + }, + }, + }; + + const ThumbStyles = (icon) => ({ + width: size.thumbWidth, + height: size.thumbHeight, + // backgroundColor: theme.palette.common.white, + backgroundColor: mode === 'dark' ? green : lihhtgreen, + + '&:before': { + content: '" "', + display: 'block', + backgroundImage: convertSvg(icon), + width: '100%', + height: '100%', + backgroundSize: '50%', + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat', + }, + }); + + const TrackStyles = { + backgroundColor: theme.palette.grey.light, + opacity: 1, + // borderRadius: 20, + width: size.width, + height: size.height, + }; + + const SwitchStyles = { + ...SwitchBaseStyles, + '& .MuiSwitch-thumb': checked + ? ThumbStyles(iconRight) + : ThumbStyles(iconLeft), + '& .MuiSwitch-track': TrackStyles, + }; + + return ( + + + } + label={checked ? labelRight : labelLeft} + style={{ margin: 'auto', justifyContent: 'center' }} + /> + + ); +}; + +export default RCSwitch; diff --git a/src/components/forms/reusable/RCZodForm.jsx b/src/components/forms/reusable/RCZodForm.jsx new file mode 100644 index 0000000..fc03843 --- /dev/null +++ b/src/components/forms/reusable/RCZodForm.jsx @@ -0,0 +1,197 @@ +import React, { useEffect } from 'react'; +import { + Box, + InputAdornment, + FormControl, + InputLabel, + Select, + MenuItem, + Chip, +} from '@mui/material'; +import FormField from './FormField'; +import ReusableLoadingButton from '../../buttons/other/ReusableLoadingButton'; +import { useFormContext, useMode } from '../../../context'; +import { + FormBox, + FormFieldBox, +} from '../../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; + +const RCZodForm = ({ + fields, + buttonLabel, + startIcon, + schemaName, + additionalButtons, + initialValues, + additionalData, +}) => { + const { theme } = useMode(); + const { + formMethods, + onSubmit, + handleChange, + setFormSchema, + currentForm, + currentFormSchema, + formState: { errors, isSubmitting }, + // getValues, + // handleSearchTermChange, + } = useFormContext(); + + // useEffect(() => { + // setFormSchema(schemaName); + // }, [setFormSchema, schemaName]); + // useEffect(() => { + // setFormSchema(schemaName); + // if (initialValues) { + // console.log('initialValues:', initialValues); + // formMethods.reset(initialValues); + // } + // }, [setFormSchema, schemaName, formMethods, initialValues]); + // useEffect(() => { + // // console.log('initialValues:', initialValues); + // console.log('SCHEMA NAME CHANGED TO:', schemaName); + // setFormSchema(schemaName); + // // When currentForm or schemaName changes, reset form with new initialValues or empty values + // formMethods.reset(initialValues || {}); + // }, [setFormSchema, schemaName, formMethods, initialValues]); + const onFormSubmit = (data) => { + onSubmit(data, additionalData); + }; + + const renderField = (field) => { + const isSearchForm = + schemaName === 'searchForm' && field.name === 'searchTerm'; + + const onChange = isSearchForm ? handleChange : undefined; + + if (field.type === 'select') { + return ( + + + {field.label} + + + + ); + } else if (field.type === 'chips') { + return ( + + + {field?.values?.map((value, index) => ( + field.onDelete(value)} + /> + ))} + + + ); + } else { + return ( + + formMethods.handleChange(e.target.value)} + InputProps={ + field.icon + ? { + endAdornment: ( + + {field.icon} + + ), + } + : null + } + multiline={field.multiline} + rows={field.rows} + /> + + ); + } + }; + + return ( + + {fields?.map(renderField)} + + {startIcon} + + } + fullWidth + /> + {additionalButtons && + additionalButtons?.map((button, index) => ( + + ))} + + ); +}; + +export default RCZodForm; diff --git a/src/components/forms/reusable/Select.jsx b/src/components/forms/reusable/Select.jsx index cf0e3c6..8fc0a9d 100644 --- a/src/components/forms/reusable/Select.jsx +++ b/src/components/forms/reusable/Select.jsx @@ -71,7 +71,7 @@ const SelectComponent = React.forwardRef( }, }} > - + {/* Select Option - + */} {options.map((option) => ( {option.label} diff --git a/src/components/forms/search/SearchComponent.jsx b/src/components/forms/search/SearchComponent.jsx index 0a5e57e..678a44a 100644 --- a/src/components/forms/search/SearchComponent.jsx +++ b/src/components/forms/search/SearchComponent.jsx @@ -1,6 +1,7 @@ // DeckSearch.js import React, { useEffect, useState } from 'react'; import { + Card, Collapse, Container, Grid, @@ -19,6 +20,8 @@ import { useCardStore, useMode } from '../../../context'; import useLocalStorage from '../../../context/hooks/useLocalStorage'; import { useCardStoreHook } from '../../../context/hooks/useCardStore'; import { useConfiguratorContext } from '../../../context'; +import SimpleCard from '../../../layout/REUSABLE_COMPONENTS/unique/SimpleCard'; +import uniqueTheme from '../../../layout/REUSABLE_COMPONENTS/unique/uniqueTheme'; const SearchComponent = (pageContext) => { const { theme } = useMode(); const itemsPerPage = 12; @@ -44,27 +47,21 @@ const SearchComponent = (pageContext) => { return ( - { onClick={toggleConfigurator} size="large" > - + { display: 'flex', alignItems: 'center', justifyContent: 'center', - p: theme.spacing(2), }} > - {/* eslint-disable-next-line max-len */} setSearchBarFocused(true)} onBlur={() => setSearchBarFocused(false)} /> - + - + + + ); diff --git a/src/components/forms/search/SearchResults.jsx b/src/components/forms/search/SearchResults.jsx index c495304..6be7eda 100644 --- a/src/components/forms/search/SearchResults.jsx +++ b/src/components/forms/search/SearchResults.jsx @@ -3,7 +3,7 @@ import { Box, Grid } from '@mui/material'; import useGridItems from '../../../context/hooks/useGridItems'; import usePagination from '../../../context/hooks/usePagination'; import PaginationComponent from '../../../layout/collection/collectionGrids/cards-datatable/PaginationComponent'; -import LoadingIndicator from '../../reusable/indicators/LoadingIndicator'; +import LoadingIndicator from '../../../layout/LoadingIndicator'; import MDBox from '../../../layout/REUSABLE_COMPONENTS/MDBOX'; const SearchResults = ({ @@ -20,6 +20,7 @@ const SearchResults = ({ isLoading, pageContext, itemsPerPage, + type: 'search', }); if (isLoading) return ; diff --git a/src/components/forms/selectors/ThemeSelector.jsx b/src/components/forms/selectors/ThemeSelector.jsx index 25e6fca..ef64bd1 100644 --- a/src/components/forms/selectors/ThemeSelector.jsx +++ b/src/components/forms/selectors/ThemeSelector.jsx @@ -3,10 +3,9 @@ import { FormControl, InputLabel } from '@mui/material'; import { Controller } from 'react-hook-form'; import { useSnackbar } from 'notistack'; -import SelectComponent from '../reusable/Select'; import { useFormContext, useMode } from '../../../context'; import { StyledChartBox } from '../../../pages/pageStyles/StyledComponents'; -import MDTypography from '../../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; +import SelectComponent from '../reusable/Select'; const ThemeSelector = ({ setTheme }) => { const { theme } = useMode(); diff --git a/src/components/forms/selectors/TimeRangeSelector.jsx b/src/components/forms/selectors/TimeRangeSelector.jsx index ef4f184..a8bdd34 100644 --- a/src/components/forms/selectors/TimeRangeSelector.jsx +++ b/src/components/forms/selectors/TimeRangeSelector.jsx @@ -2,15 +2,15 @@ import { useMemo } from 'react'; import { useFormContext, useMode } from '../../../context'; import useSelectedCollection from '../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; import { StyledChartBox } from '../../../pages/pageStyles/StyledComponents'; -import { FormControl, InputLabel } from '@mui/material'; +import { FormControl, InputLabel, Typography } from '@mui/material'; import { Controller } from 'react-hook-form'; import SelectComponent from '../reusable/Select'; import useTimeRange from './useTimeRange'; +import MDTypography from '../../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; -const TimeRangeSelector = ({ showSnackbar }) => { +const TimeRangeSelector = () => { const { theme } = useMode(); - const { timeRangeOptions, onFormSubmit, control, errors } = - useTimeRange(showSnackbar); + const { timeRangeOptions, onFormSubmit, control, errors } = useTimeRange(); return ( { error={!!errors.timeRange} sx={{ width: '100%' }} > - Time Range + + {/* */} + Time Range + {/* */} + { ); }; export default TimeRangeSelector; - -// // const TimeRangeSelector = ({ showSnackbar }) => { -// // const { theme } = useMode(); -// // const { formMethods, onSubmit } = useFormContext(); -// // const { selectedCollection } = useSelectedCollection(); -// // const averagedChartData = selectedCollection?.averagedChartData; - -// // const timeRangeOptions = useMemo(() => { -// // const options = []; -// // averagedChartData?.forEach((value, key) => { -// // options.push({ -// // value: key, -// // label: key.toUpperCase(), -// // }); -// // }); -// // return options; -// // }, [averagedChartData]); - -// // const { -// // control, -// // handleSubmit, -// // formState: { errors }, -// // } = formMethods; - -// // const onFormSubmit = (data) => { -// // onSubmit(data, 'timeRangeSelector') -// // .then(() => { -// // showSnackbar( -// // { -// // title: 'Success', -// // description: `Now viewing chart data for ${data?.timeRange}`, -// // }, -// // 'success' -// // ); -// // }) -// // .catch((error) => { -// // showSnackbar( -// // { -// // title: 'Error', -// // description: `Failed to view chart data for ${data?.timeRange}: ${error}`, -// // }, -// // 'error' -// // ); -// // }); -// // }; - -// // return ( -// // -// // -// // Time Range -// // ( -// // -// // )} -// // /> -// // -// // -// // ); -// // }; - -// // export default TimeRangeSelector; -// // import React, { useMemo } from 'react'; -// // import { useFormContext, Controller } from 'react-hook-form'; -// // import { FormControl, InputLabel } from '@mui/material'; -// // import { useMode } from '../../../../context'; -// // import useSelectedCollection from '../../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -// // import SelectComponent from '../../../REUSABLE_COMPONENTS/SelectComponent'; -// // import StyledChartBox from '../../../REUSABLE_COMPONENTS/StyledChartBox'; - -// const TimeRangeSelector = ({ showSnackbar }) => { -// const { theme } = useMode(); -// const { formMethods, onSubmit } = useFormContext(); -// const { selectedCollection } = useSelectedCollection(); -// const averagedChartData = selectedCollection?.averagedChartData; - -// // Convert the Map to an array of options for the select component -// const timeRangeOptions = useMemo(() => { -// return Array.from(averagedChartData?.entries() || []).map( -// ([key, value]) => ({ -// value: key, -// label: value.name.toUpperCase(), -// }) -// ); -// }, [averagedChartData]); - -// const { -// control, -// handleSubmit, -// formState: { errors }, -// } = formMethods; - -// const onFormSubmit = (data) => { -// onSubmit(data, 'timeRangeSelector') -// .then(() => { -// showSnackbar( -// { -// title: 'Success', -// description: `Now viewing chart data for ${data?.timeRange}`, -// }, -// 'success' -// ); -// }) -// .catch((error) => { -// showSnackbar( -// { -// title: 'Error', -// description: `Failed to view chart data for ${data?.timeRange}: ${error}`, -// }, -// 'error' -// ); -// }); -// }; - -// return ( -// -// -// Time Range -// ( -// -// )} -// /> -// -// -// ); -// }; - -// export default TimeRangeSelector; diff --git a/src/components/forms/selectors/useTimeRange.jsx b/src/components/forms/selectors/useTimeRange.jsx index 497d24a..79af5bb 100644 --- a/src/components/forms/selectors/useTimeRange.jsx +++ b/src/components/forms/selectors/useTimeRange.jsx @@ -1,46 +1,70 @@ import { useMemo, useCallback } from 'react'; import { useFormContext } from '../../../context'; import useSelectedCollection from '../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +import useSubmitHandler from '../hooks/useSubmitHandler'; function useTimeRange(showSnackbar) { const { formMethods, onSubmit } = useFormContext(); const { selectedCollection } = useSelectedCollection(); const averagedChartData = selectedCollection?.averagedChartData; - + const selectedTimeRange = formMethods.watch('timeRange', '24hr'); const timeRangeOptions = useMemo(() => { - return Object.keys(averagedChartData || {}).map((key) => ({ - value: key, - label: key.toUpperCase(), - })); + return Object.entries(averagedChartData || {}).map(([key, value]) => { + // console.log(`Key: ${key}, Value: `, value); + return { + value: value?.id, + label: value?.name?.toUpperCase(), + }; + }); }, [averagedChartData]); - // Watching the "timeRange" field to get its current value - const selectedTimeRange = formMethods.watch('timeRange', '24hr'); + const handleTimeRangeSubmit = async (data) => { + // Here you can integrate the logic to handle the submission data, + // for instance, to update the chart based on the selected time range. + console.log('Time Range Selected:', data.timeRange); + }; + const successTitle = 'Success'; + const successDescription = 'Now viewing chart data for {timeRange}'; + const errorDescription = 'Failed to view chart data for {timeRange}'; - const onFormSubmit = useCallback( - (data) => { - onSubmit(data, 'timeRangeSelector') - .then(() => { - showSnackbar( - { - title: 'Success', - description: `Now viewing chart data for ${data?.timeRange}`, - }, - 'success' - ); - }) - .catch((error) => { - showSnackbar( - { - title: 'Error', - description: `Failed to view chart data for ${data?.timeRange}: ${error}`, - }, - 'error' - ); - }); - }, - [onSubmit, showSnackbar] + // Using the custom submit handler hook. + const onFormSubmit = useSubmitHandler( + handleTimeRangeSubmit, + successTitle, + successDescription, + errorDescription ); + // const onFormSubmit = useSubmitHandler( + // onSubmit, + // showSnackbar, + // 'Success', + // 'Now viewing chart data for {timeRange}', + // 'Failed to view chart data for {timeRange}' + // ); + // const onFormSubmit = useCallback( + // (data) => { + // onSubmit(data, 'timeRangeSelector') + // .then(() => { + // showSnackbar( + // { + // title: 'Success', + // description: `Now viewing chart data for ${data?.timeRange}`, + // }, + // 'success' + // ); + // }) + // .catch((error) => { + // showSnackbar( + // { + // title: 'Error', + // description: `Failed to view chart data for ${data?.timeRange}: ${error}`, + // }, + // 'error' + // ); + // }); + // }, + // [onSubmit, showSnackbar] + // ); return { timeRangeOptions, diff --git a/src/components/grids/deckBuilderGrids/CardsGrid.js b/src/components/grids/deckBuilderGrids/CardsGrid.js deleted file mode 100644 index a6e58ad..0000000 --- a/src/components/grids/deckBuilderGrids/CardsGrid.js +++ /dev/null @@ -1,159 +0,0 @@ -// import React, { useMemo, useState } from 'react'; -// import { Grid, Grow, Typography } from '@mui/material'; -// import { useDeckStore } from '../../../context/MAIN_CONTEXT/DeckContext/DeckContext'; -// import SkeletonDeckItem from '../gridItems/SkeletonDeckItem'; -// import GridLayout from '../searchResultsGrids/GridLayout'; -// import StoreItem from '../gridItems/StoreItem'; -// import MDBox from '../../../layout/REUSABLE_COMPONENTS/MDBOX'; - -// const CardsGrid = ({ isLoading }) => { -// const { selectedCards } = useDeckStore(); -// const [error, setError] = useState(''); - -// const flattenSelectedCards = useMemo(() => { -// if (!Array.isArray(selectedCards)) return []; - -// const cardCountMap = new Map(); - -// return selectedCards.reduce((acc, card) => { -// if (!card) return acc; -// const currentCount = cardCountMap.get(card.id) || 0; -// if (currentCount < 3) { -// cardCountMap.set(card.id, currentCount + 1); -// return [...acc, { ...card, uniqueKey: `${card.id}-${currentCount}` }]; -// } -// return acc; -// }, []); -// }, [selectedCards]); - -// if (error) { -// return {error}; -// } - -// const skeletonCount = 12; - -// return ( -// -// {(isLoading -// ? Array.from({ length: skeletonCount }) -// : flattenSelectedCards -// ).map((item, index) => ( -// -// -// -// {isLoading ? ( -// -// ) : ( -// -// )} -// -// -// -// ))} -// -// ); -// }; - -// export default React.memo(CardsGrid); -import React, { useMemo } from 'react'; -import { Grid, Grow, Typography } from '@mui/material'; -import { useDeckStore } from '../../../context/MAIN_CONTEXT/DeckContext/DeckContext'; -import SkeletonDeckItem from '../gridItems/SkeletonDeckItem'; -import StoreItem from '../gridItems/StoreItem'; -import MDBox from '../../../layout/REUSABLE_COMPONENTS/MDBOX'; -import GridLayout from '../../../layout/Containers/GridLayout'; - -const CardsGrid = ({ isLoading }) => { - const { selectedCards } = useDeckStore(); - - // Efficiently flattening and processing selectedCards with useMemo - const flattenSelectedCards = useMemo(() => { - if (!Array.isArray(selectedCards)) return []; - - const cardCountMap = new Map(); - - return selectedCards.reduce((acc, card) => { - if (!card) return acc; - const currentCount = (cardCountMap.get(card.id) || 0) + 1; - if (currentCount <= 3) { - acc.push({ ...card, uniqueKey: `${card.id}-${currentCount - 1}` }); - cardCountMap.set(card.id, currentCount); - } - return acc; - }, []); - }, [selectedCards]); - - const gridItems = useMemo(() => { - return isLoading ? Array.from({ length: 12 }) : flattenSelectedCards; - }, [isLoading, flattenSelectedCards]); - - // Define grid item breakpoints once to reuse in the mapping - const gridItemProps = { xs: 6, sm: 4, md: 4, lg: 4 }; - - return ( - - {gridItems.map((item, index) => ( - - - - {isLoading ? ( - - ) : ( - - )} - - - - ))} - - ); -}; - -export default React.memo(CardsGrid); diff --git a/src/components/grids/deckBuilderGrids/SelectDeckList.jsx b/src/components/grids/deckBuilderGrids/SelectDeckList.jsx deleted file mode 100644 index 8da1a5d..0000000 --- a/src/components/grids/deckBuilderGrids/SelectDeckList.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { Grid, Chip, Typography } from '@mui/material'; -import DeckBuilderIcon from '../../reusable/icons/DeckBuilderIcon'; -import { useDeckStore } from '../../../context/MAIN_CONTEXT/DeckContext/DeckContext'; -import { useMode } from '../../../context'; - -const SelectDeckList = ({ handleSelectDeck }) => { - const { allDecks } = useDeckStore(); - const { theme } = useMode(); - - return ( - - {allDecks?.map((deck) => ( - - - {deck?.name || 'Unnamed'} - - } - onClick={() => handleSelectDeck(deck?._id)} - icon={} - variant="outlined" - color="primary" - clickable - sx={{ - justifyContent: 'flex-start', - padding: 1, - borderRadius: theme.shape.borderRadius, - width: '100%', // Make chip full width of the container - }} - /> - - ))} - - ); -}; - -export default SelectDeckList; diff --git a/src/components/grids/gridItems/ReusableSkeletonItem.jsx b/src/components/grids/gridItems/ReusableSkeletonItem.jsx deleted file mode 100644 index 074a7b9..0000000 --- a/src/components/grids/gridItems/ReusableSkeletonItem.jsx +++ /dev/null @@ -1,30 +0,0 @@ -// ReusableSkeletonItem.jsx -import React from 'react'; -import { Grid } from '@mui/material'; -import SkeletonStoreItem from './SkeletonStoreItem'; -import SkeletonDeckItem from './SkeletonDeckItem'; - -const ReusableSkeletonItem = ({ itemType, count, gridItemProps, context }) => - context === 'Cart' ? ( - <> - {Array(count) - .fill(0) - .map((_, index) => ( - - - - ))} - - ) : ( - <> - {Array(count) - .fill(0) - .map((_, index) => ( - - - - ))} - - ); - -export default ReusableSkeletonItem; diff --git a/src/components/grids/gridItems/SkeletonDeckItem.jsx b/src/components/grids/gridItems/SkeletonDeckItem.jsx deleted file mode 100644 index 686cd89..0000000 --- a/src/components/grids/gridItems/SkeletonDeckItem.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import { Box, CardContent, Skeleton } from '@mui/material'; -import { - AspectRatioBoxSkeleton, - StyledSkeletonCard, -} from '../../../pages/pageStyles/StyledComponents'; -import { useMode } from '../../../context'; - -const SkeletonDeckItem = ({ context }) => { - const { theme } = useMode(); - return ( - - - - - - - - - - - - - - ); -}; - -export default SkeletonDeckItem; diff --git a/src/components/grids/gridItems/SkeletonStoreItem.jsx b/src/components/grids/gridItems/SkeletonStoreItem.jsx deleted file mode 100644 index 6348f9d..0000000 --- a/src/components/grids/gridItems/SkeletonStoreItem.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import { Box, CardContent, Skeleton } from '@mui/material'; -import { useMode } from '../../../context'; -import { - AspectRatioBoxSkeleton, - StyledSkeletonCard, -} from '../../../pages/pageStyles/StyledComponents'; -const SkeletonStoreItem = () => { - const { theme } = useMode(); - return ( - - - - - - - - - - - - - - ); -}; - -export default SkeletonStoreItem; diff --git a/src/components/grids/gridItems/StoreItem.jsx b/src/components/grids/gridItems/StoreItem.jsx deleted file mode 100644 index 8aa6b56..0000000 --- a/src/components/grids/gridItems/StoreItem.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React, { memo } from 'react'; -import { Box, Container } from '@mui/material'; -import GenericCard from '../../cards/GenericCard'; - -// eslint-disable-next-line react/display-name -const StoreItem = memo(({ card, context, page, index }) => { - return ( - - - - - - ); -}); - -export default StoreItem; diff --git a/src/components/reusable/CustomPagination.jsx b/src/components/reusable/CustomPagination.jsx deleted file mode 100644 index 35ef3ec..0000000 --- a/src/components/reusable/CustomPagination.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { Box, Pagination } from '@mui/material'; -import { useMode } from '../../context'; - -const CustomPagination = ({ - totalCount, - itemsPerPage, - currentPage, - handlePagination, -}) => { - const { theme } = useMode(); - const pageCount = Math.ceil(totalCount / itemsPerPage); - - return ( - - - - ); -}; - -export default CustomPagination; diff --git a/src/components/reusable/icons/DeckBuilderIcon.jsx b/src/components/reusable/icons/DeckBuilderIcon.jsx deleted file mode 100644 index 6bb8302..0000000 --- a/src/components/reusable/icons/DeckBuilderIcon.jsx +++ /dev/null @@ -1,161 +0,0 @@ -/* eslint-disable max-len */ -// /* eslint-disable max-len */ -// import * as React from 'react'; -// import { SvgIcon } from '@mui/material'; - -// export default function DeckBuilderIcon(props) { -// // Extract customPaths and otherProps from props -// const { customPaths, ...otherProps } = props; - -// // Default paths if custom paths are not provided -// const defaultPaths = [ -// 'M1050 1666 c-129 -45 -387 -136 -572 -201 -186 -65 -347 -127 -358 -137 -24 -22 -26 -70 -5 -92 48 -48 491 -413 513 -423 65 -29 98 -20 679 183 309 108 576 205 592 215 25 15 31 25 31 53 0 38 -4 42 -348 330 -160 134 -198 156 -259 156 -21 -1 -143 -38 -273 -84z', -// 'M60 1188 c0 -7 6 -23 13 -35 14 -25 477 -417 522 -441 19 -11 55 -16 -// 100 -17 66 0 104 12 633 198 310 108 577 204 593 213 29 14 49 48 49 81 0 13 -// -4 12 -24 -6 -28 -27 0 -16 -656 -247 -583 -205 -602 -210 -686 -168 -22 10 -// -149 111 -282 224 -134 113 -248 207 -253 208 -5 2 -9 -2 -9 -10z', -// 'M70 1079 c0 -36 7 -43 253 -249 138 -117 266 -219 284 -226 18 -8 59 -// -14 92 -14 53 0 133 25 630 199 313 110 579 204 590 210 28 16 51 51 51 80 l0 -// 24 -23 -21 c-36 -34 -1183 -432 -1243 -432 -89 0 -111 14 -370 232 -137 116 -// -252 215 -256 222 -4 6 -8 -5 -8 -25z', -// 'M70 972 c0 -35 4 -38 324 -305 181 -152 203 -167 253 -177 30 -7 71 -// -9 92 -5 44 8 1109 379 1167 406 41 19 64 52 64 91 l0 21 -23 -21 c-14 -13 -// -243 -100 -602 -227 -742 -262 -679 -258 -902 -69 -68 57 -179 152 -248 210 -// -122 103 -125 105 -125 76z', -// 'M70 873 c0 -18 7 -38 15 -47 38 -37 490 -413 514 -426 17 -10 52 -14 -// 101 -14 71 1 104 11 634 198 307 108 572 204 588 212 30 15 48 49 48 87 -1 19 -// -2 20 -13 7 -7 -8 -19 -21 -28 -27 -20 -16 -1140 -408 -1192 -417 -21 -4 -63 -// -2 -93 4 -51 11 -72 26 -282 202 -125 105 -242 204 -259 221 l-33 31 0 -31z', -// 'M70 778 c0 -30 10 -39 324 -301 181 -152 203 -167 253 -177 30 -7 71 -// -9 92 -5 44 8 1109 379 1167 406 22 11 46 31 52 45 19 39 14 49 -11 26 -36 -// -34 -1183 -432 -1243 -432 -89 0 -111 14 -370 232 -137 116 -252 215 -256 222 -// -5 6 -8 -1 -8 -16z' -// ]; - -// // Use customPaths if provided, otherwise use defaultPaths -// const paths = customPaths || defaultPaths; -// return ( -// -// -// -// -// -// -// -// -// -// -// ); -// } -import * as React from 'react'; -import { SvgIcon } from '@mui/material'; - -export default function DeckBuilderIcon(props) { - // Extract customPaths and otherProps from props - const { customPaths, ...otherProps } = props; - // const { customPaths, ...otherProps } = props; - - // Default paths if custom paths are not provided - const defaultPaths = [ - 'M1050 1666 c-129 -45 -387 -136 -572 -201 -186 -65 -347 -127 -358 -137 -24 -22 -26 -70 -5 -92 48 -48 491 -413 513 -423 65 -29 98 -20 679 183 309 108 576 205 592 215 25 15 31 25 31 53 0 38 -4 42 -348 330 -160 134 -198 156 -259 156 -21 -1 -143 -38 -273 -84z', - 'M60 1188 c0 -7 6 -23 13 -35 14 -25 477 -417 522 -441 19 -11 55 -16 100 -17 66 0 104 12 633 198 310 108 577 204 593 213 29 14 49 48 49 81 0 13 -4 12 -24 -6 -28 -27 0 -16 -656 -247 -583 -205 -602 -210 -686 -168 -22 10 -149 111 -282 224 -134 113 -248 207 -253 208 -5 2 -9 -2 -9 -10z', - 'M70 1079 c0 -36 7 -43 253 -249 138 -117 266 -219 284 -226 18 -8 59 -14 92 -14 53 0 133 25 630 199 313 110 579 204 590 210 28 16 51 51 51 80 l0 24 -23 -21 c-36 -34 -1183 -432 -1243 -432 -89 0 -111 14 -370 232 -137 116 -252 215 -256 222 -4 6 -8 -5 -8 -25z', - 'M70 972 c0 -35 4 -38 324 -305 181 -152 203 -167 253 -177 30 -7 71 -9 92 -5 44 8 1109 379 1167 406 41 19 64 52 64 91 l0 21 -23 -21 c-14 -13 -243 -100 -602 -227 -742 -262 -679 -258 -902 -69 -68 57 -179 152 -248 210 -122 103 -125 105 -125 76z', - 'M70 873 c0 -18 7 -38 15 -47 38 -37 490 -413 514 -426 17 -10 52 -14 101 -14 71 1 104 11 634 198 307 108 572 204 588 212 30 15 48 49 48 87 -1 19 -2 20 -13 7 -7 -8 -19 -21 -28 -27 -20 -16 -1140 -408 -1192 -417 -21 -4 -63 -2 -93 4 -51 11 -72 26 -282 202 -125 105 -242 204 -259 221 l-33 31 0 -31z', - 'M70 778 c0 -30 10 -39 324 -301 181 -152 203 -167 253 -177 30 -7 71 -9 92 -5 44 8 1109 379 1167 406 22 11 46 31 52 45 19 39 14 49 -11 26 -36 -34 -1183 -432 -1243 -432 -89 0 -111 14 -370 232 -137 116 -252 215 -256 222 -5 6 -8 -1 -8 -16z', - ]; - - // const defaultPaths = [ - // 'M1050 1666 c-129 -45 -387 -136 -572 -201 -186 -65 -347 -127 -358 - // -137 -24 -22 -26 -70 -5 -92 48 -48 491 -413 513 -423 65 -29 98 -20 679 183 - // 309 108 576 205 592 215 25 15 31 25 31 53 0 38 -4 42 -348 330 -160 134 -198 - // 156 -259 156 -21 -1 -143 -38 -273 -84z', - // 'M60 1188 c0 -7 6 -23 13 -35 14 -25 477 -417 522 -441 19 -11 55 -16 - // 100 -17 66 0 104 12 633 198 310 108 577 204 593 213 29 14 49 48 49 81 0 13 - // -4 12 -24 -6 -28 -27 0 -16 -656 -247 -583 -205 -602 -210 -686 -168 -22 10 - // -149 111 -282 224 -134 113 -248 207 -253 208 -5 2 -9 -2 -9 -10z', - // 'M70 1079 c0 -36 7 -43 253 -249 138 -117 266 -219 284 -226 18 -8 59 - // -14 92 -14 53 0 133 25 630 199 313 110 579 204 590 210 28 16 51 51 51 80 l0 - // 24 -23 -21 c-36 -34 -1183 -432 -1243 -432 -89 0 -111 14 -370 232 -137 116 - // -252 215 -256 222 -4 6 -8 -5 -8 -25z', - // 'M70 972 c0 -35 4 -38 324 -305 181 -152 203 -167 253 -177 30 -7 71 - // -9 92 -5 44 8 1109 379 1167 406 41 19 64 52 64 91 l0 21 -23 -21 c-14 -13 - // -243 -100 -602 -227 -742 -262 -679 -258 -902 -69 -68 57 -179 152 -248 210 - // -122 103 -125 105 -125 76z', - // 'M70 873 c0 -18 7 -38 15 -47 38 -37 490 -413 514 -426 17 -10 52 -14 - // 101 -14 71 1 104 11 634 198 307 108 572 204 588 212 30 15 48 49 48 87 -1 19 - // -2 20 -13 7 -7 -8 -19 -21 -28 -27 -20 -16 -1140 -408 -1192 -417 -21 -4 -63 - // -2 -93 4 -51 11 -72 26 -282 202 -125 105 -242 204 -259 221 l-33 31 0 -31z', - // 'M70 778 c0 -30 10 -39 324 -301 181 -152 203 -167 253 -177 30 -7 71 - // -9 92 -5 44 8 1109 379 1167 406 22 11 46 31 52 45 19 39 14 49 -11 26 -36 - // -34 -1183 -432 -1243 -432 -89 0 -111 14 -370 232 -137 116 -252 215 -256 222 - // -5 6 -8 -1 -8 -16z' - // ]; - - // Use customPaths if provided, otherwise use defaultPaths - const paths = customPaths || defaultPaths; - return ( - - - {paths?.map((d, i) => ( - - ))} - - - ); -} diff --git a/src/config.js b/src/config.js deleted file mode 100644 index ac980fb..0000000 --- a/src/config.js +++ /dev/null @@ -1,8 +0,0 @@ -const config = { - basename: '', - defaultPath: '/home', - fontFamily: "'Roboto', sans-serif", - borderRadius: 12, -}; - -export default config; diff --git a/src/config.json b/src/config.json new file mode 100644 index 0000000..0d90ac9 --- /dev/null +++ b/src/config.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "target": "es5", + "module": "commonjs", + "sourceMap": true, + "outDir": "dist", + "paths": { + "@material-ui/core": ["../node_modules/@material-ui/core"], + "@material-ui/icons": ["../node_modules/@material-ui/icons"], + "@material-ui/lab": ["../node_modules/@material-ui/lab"], + "@material-ui/pickers": ["../node_modules/@material-ui/pickers"], + "@material-ui/styles": ["../node_modules/@material-ui/styles"], + "@material-ui/system": ["../node_modules/@material-ui/system"], + "@material-ui/utils": ["../node_modules/@material-ui/utils"], + "@tabler/icons-react": ["../node_modules/@tabler/icons-react"], + "@tabler/icons": ["../node_modules/@tabler/icons"], + "@tabler/core": ["../node_modules/@tabler/core"], + "@tabler/core-react": ["../node_modules/@tabler/core-react"], + "@tabler/core-icons": ["../node_modules/@tabler/core-icons"], + "@tabler/core-react-icons": ["../node_modules/@tabler/core-react-icons"] + } + } +} diff --git a/src/context/Helpers.jsx b/src/context/Helpers.jsx index 0de877f..1655ba5 100644 --- a/src/context/Helpers.jsx +++ b/src/context/Helpers.jsx @@ -17,13 +17,6 @@ export const BASE_API_URL = `${process.env.REACT_APP_SERVER}/api/users`; */ export const createApiUrl = (path) => `${BASE_API_URL}/${path}`; -const lastRequestTime = { - POST: 0, - PUT: 0, - DELETE: 0, - GET: 0, - PATCH: 0, -}; /** * Filters a collection of data points based on a time threshold. * @@ -85,20 +78,17 @@ export const roundToNearestTenth = (value) => { return Math.round(value * 10) / 10; }; -// 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.values(collection).reduce((total, price) => total + price, 0); }; -// Function to get the quantity of cards in a collection by its ID export const getCardQuantity = (collectionId, allCollections) => { // Assuming allCollections is an array of collection objects const collection = allCollections.find((coll) => coll._id === collectionId); return collection ? collection.cards.length : 0; }; -// Custom Hook to get the userId from cookies export const useUserId = () => { const [cookies] = useCookies(['authUser']); const [userId, setUserId] = useState(null); @@ -165,3 +155,23 @@ export const createNewPriceEntry = (price) => { timestamp: new Date(), }; }; +export const calculateCollectionValue = (collection) => { + if (!collection) return 0; + + const cards = collection?.cards; + + if (!Array.isArray(cards)) { + console.warn('Invalid collection format', collection, cards); + return 0; + } + + return cards.reduce((totalValue, card) => { + const cardPrice = card?.price || 0; + const cardQuantity = card?.quantity || 0; + return totalValue + cardPrice * cardQuantity; + }, 0); +}; + +export const shouldFetchCollections = (prevUserId, currentUserId) => { + return prevUserId !== currentUserId && currentUserId != null; +}; diff --git a/src/context/MAIN_CONTEXT/AuthContext/authContext.js b/src/context/MAIN_CONTEXT/AuthContext/authContext.js index 46cf135..625cf63 100644 --- a/src/context/MAIN_CONTEXT/AuthContext/authContext.js +++ b/src/context/MAIN_CONTEXT/AuthContext/authContext.js @@ -8,16 +8,13 @@ import React, { useMemo, useState, } from 'react'; -import axios from 'axios'; import { useCookies } from 'react-cookie'; -import { usePageContext } from '../../UTILITIES_CONTEXT/PageContext/PageContext'; -import { processResponseData } from './helpers'; import useLogger from '../../hooks/useLogger'; import { defaultContextValue } from '../../constants'; import { Redirect, useNavigate } from 'react-router-dom'; -import { useLoading } from '../../hooks/useLoading'; import useFetchWrapper from '../../hooks/useFetchWrapper'; import jwt_decode from 'jwt-decode'; +import { CircularProgress, Snackbar } from '@mui/material'; export const AuthContext = createContext(defaultContextValue.AUTH_CONTEXT); @@ -32,6 +29,7 @@ export default function AuthProvider({ children }) { 'authUser', 'userId', ]); + const [openSnackbar, setOpenSnackbar] = useState(false); const [user, setUser] = useState({ isLoggedIn: false, accessToken: '', @@ -63,7 +61,7 @@ export default function AuthProvider({ children }) { const [responseMessage, setResponseMessage] = useState(''); const { fetchWrapper } = useFetchWrapper(); - const { logEvent } = useLogger('AuthContext'); + // const { logEvent } = useLogger('AuthContext'); // Helper function to set cookies const setAuthCookies = (data) => { const { accessToken, refreshToken, user } = data; @@ -105,10 +103,18 @@ export default function AuthProvider({ children }) { ); } }; + // const logout = useCallback(() => { + // clearAuthCookies(); + // }, [removeCookie, navigate]); const logout = useCallback(() => { - clearAuthCookies(); - }, [removeCookie, navigate]); - + ['accessToken', 'refreshToken', 'user'].forEach((cookieName) => + removeCookie(cookieName, { path: '/' }) + ); + setOpenSnackbar(true); // Open the snackbar with loading icon + setTimeout(() => { + navigate('/login'); // Redirect to login after the timeout + }, 3000); // Set timeout duration (e.g., 3000 ms = 3 seconds) + }, [navigate, removeCookie]); const login = useCallback( async (username, password) => { await executeAuthAction( @@ -140,7 +146,12 @@ export default function AuthProvider({ children }) { const checkTokenValidity = useCallback(() => { const accessToken = cookies.accessToken; if (!accessToken) { - logout(); + console.info('No access token found, user not logged in.'); + setOpenSnackbar(true); // Notify the user they are being redirected to login + setTimeout(() => { + setOpenSnackbar(false); // Close the notification + navigate('/login'); // Redirect to login after showing the notification + }, 3000); // Adjust the timeout duration as needed return; } @@ -148,7 +159,6 @@ export default function AuthProvider({ children }) { const { exp } = jwt_decode(accessToken); const isTokenExpired = Date.now() >= exp * 1000; if (isTokenExpired) { - console.log('Token is invalid, logging out...'); logout(); } } catch (error) { @@ -161,6 +171,13 @@ export default function AuthProvider({ children }) { useEffect(() => { checkTokenValidity(); }, [checkTokenValidity]); + const renderSnackbar = ( + } + /> + ); const contextValue = useMemo( () => ({ @@ -178,7 +195,9 @@ export default function AuthProvider({ children }) { ); return ( - {children} + + {children} {renderSnackbar} + ); } diff --git a/src/context/MAIN_CONTEXT/CardContext/CardContext.jsx b/src/context/MAIN_CONTEXT/CardContext/CardContext.jsx index f28052f..5dd2983 100644 --- a/src/context/MAIN_CONTEXT/CardContext/CardContext.jsx +++ b/src/context/MAIN_CONTEXT/CardContext/CardContext.jsx @@ -10,13 +10,11 @@ export const CardProvider = ({ children }) => { searchData, searchSettings, setSearchSettings, - // previousSearchData, isDataValid, setSearchData, cardsVersion, openConfigurator, setOpenConfigurator, - // setPreviousSearchData, setIsDataValid, clearSearchData, handleRequest, @@ -29,13 +27,11 @@ export const CardProvider = ({ children }) => { searchData, searchSettings, setSearchSettings, - // previousSearchData, isDataValid, setSearchData, cardsVersion, openConfigurator, setOpenConfigurator, - // setPreviousSearchData, setIsDataValid, clearSearchData, handleRequest, diff --git a/src/context/MAIN_CONTEXT/CartContext/CartContext.js b/src/context/MAIN_CONTEXT/CartContext/CartContext.js index c7b954b..2327534 100644 --- a/src/context/MAIN_CONTEXT/CartContext/CartContext.js +++ b/src/context/MAIN_CONTEXT/CartContext/CartContext.js @@ -205,7 +205,6 @@ export const CartProvider = ({ children }) => { console.error(error.message || 'Failed to create user cart'); } }, [userId, fetchWrapper, setCartDataAndCookie]); - const fetchUserCart = useCallback(async () => { const loadingID = 'fetchUserCart'; if (!userId || isLoading(loadingID)) return; @@ -242,9 +241,6 @@ export const CartProvider = ({ children }) => { setError, logger, ]); - // useEffect(() => { - // if (!hasFetchedCart) fetchUserCart(); - // }, [fetchUserCart, userId, hasFetchedCart, setCartDataAndCookie]); const updateCart = useCallback( async (cartId, updatedCart, method, type) => { if (!userId || !cartId) return; @@ -281,80 +277,6 @@ export const CartProvider = ({ children }) => { }, [userId, fetchWrapper, setCartDataAndCookie] ); - // useEffect(() => { - // if (userId) { - // fetchUserCart(); // Fetch cart data when userId is available - // } - // }, [userId, fetchUserCart]); - // useEffect to fetch and set cart data - // useEffect(() => { - // if (userId && isLoggedIn) { - // fetchUserCart().catch((error) => - // console.error('Failed to fetch or create cart:', error) - // ); - // } - // }, [userId, fetchUserCart]); - // useEffect(() => { - // const newTotalQuantity = cartData?.cart?.reduce( - // (total, item) => total + item?.quantity, - // 0 - // ); - // setTotalQuantity(newTotalQuantity); - // setTotalPrice(totalCost); - // }, [cartData?.cart, totalCost]); - // const updateCart = useCallback( - // async (cartId, updatedCart, method, type) => { - // if (!userId || !cartId) return; - - // const formattedCartData = { - // userId: userId, - // cart: updatedCart.map((item) => ({ - // id: item.id, // assuming id is the unique identifier for each cart item - // quantity: item.quantity, // ensure this is the current quantity to be updated in the cart - // price: item.price, // ensure this is the current price of the item - // // Include other necessary fields as per your cart item structure - // })), - // method: method, // 'POST' for adding items, 'DELETE' for removing items, 'PUT' for updating items - // type: type, - // // Calculating total quantity and total price outside of the cart array - // quantity: updatedCart.reduce((total, item) => total + item.quantity, 0), - // totalPrice: updatedCart.reduce( - // (total, item) => total + item.quantity * item.price, - // 0 - // ), - // }; - - // try { - // const response = await fetch( - // `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart/${cartId}/update`, - // { - // method: 'PUT', - // headers: { - // 'Content-Type': 'application/json', - // }, - // body: JSON.stringify(formattedCartData), - // } - // ); - - // const { message, data } = await response.json(); - // console.log('PUT: /cart -----> response message', message); - // if (response.ok) { - // console.log('PUT: /cart -----> response data', data); - // setCartDataAndCookie(data); // Update your cart state and cookie here - // } else { - // console.error( - // 'Failed to update cart: ', - // data?.error || 'Error occurred' - // ); - // // Handle errors appropriately (e.g., show an error message to the user) - // } - // } catch (error) { - // console.error('Error updating cart: ', error); - // // Handle errors appropriately (e.g., show an error message to the user) - // } - // }, - // [userId, setCartDataAndCookie] // dependencies array - // ); const addCardsToCart = async (cards, cart) => { console.log('ADD CARDS TO CART: ', cards, cart); const newCards = []; diff --git a/src/context/MAIN_CONTEXT/ChartContext/ChartContext.jsx b/src/context/MAIN_CONTEXT/ChartContext/ChartContext.jsx index 63beabd..1902a38 100644 --- a/src/context/MAIN_CONTEXT/ChartContext/ChartContext.jsx +++ b/src/context/MAIN_CONTEXT/ChartContext/ChartContext.jsx @@ -11,26 +11,16 @@ export const ChartProvider = ({ children }) => { const contextValue = useMemo( () => ({ - // timeRange: selectedCollection.averagedChartData[], - // timeRanges: TIME_RANGES, - // tickValues: tickValues, - // xFormat: xFormat, - nivoChartData: selectedCollection?.nivoChartData, newNivoChartData: selectedCollection?.newNivoChartData, muiChartData: selectedCollection?.muiChartData, nivoTestData: selectedCollection?.nivoTestData, - // setTimeRange, }), [ - // timeRange, - // tickValues, - // xFormat, selectedCollection?.nivoChartData, selectedCollection?.newNivoChartData, selectedCollection?.muiChartData, selectedCollection?.nivoTestData, - // setTimeRange, ] ); return ( diff --git a/src/context/MAIN_CONTEXT/CollectionContext/CollectionContext.jsx b/src/context/MAIN_CONTEXT/CollectionContext/CollectionContext.jsx index 98c3780..18d5e5b 100644 --- a/src/context/MAIN_CONTEXT/CollectionContext/CollectionContext.jsx +++ b/src/context/MAIN_CONTEXT/CollectionContext/CollectionContext.jsx @@ -7,7 +7,7 @@ import { useMemo, useState, } from 'react'; -import { calculateCollectionValue } from './collectionUtility'; +import { calculateCollectionValue } from '../../../assets/currentlyUnused/collectionUtility'; import { useAuthContext } from '../AuthContext/authContext'; import useCollectionManager from './useCollectionManager'; import { defaultContextValue } from '../../constants'; diff --git a/src/context/MAIN_CONTEXT/CollectionContext/collectionUtility.jsx b/src/context/MAIN_CONTEXT/CollectionContext/collectionUtility.jsx deleted file mode 100644 index 3b087e8..0000000 --- a/src/context/MAIN_CONTEXT/CollectionContext/collectionUtility.jsx +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ -export const calculateCollectionValue = (collection) => { - if (!collection) return 0; - - const cards = collection?.cards; - - if (!Array.isArray(cards)) { - console.warn('Invalid collection format', collection, cards); - return 0; - } - - return cards.reduce((totalValue, card) => { - const cardPrice = card?.price || 0; - const cardQuantity = card?.quantity || 0; - return totalValue + cardPrice * cardQuantity; - }, 0); -}; - -export const shouldFetchCollections = (prevUserId, currentUserId) => { - return prevUserId !== currentUserId && currentUserId != null; -}; diff --git a/src/context/MAIN_CONTEXT/CollectionContext/useCollectionManager.jsx b/src/context/MAIN_CONTEXT/CollectionContext/useCollectionManager.jsx index 6dad84f..685665c 100644 --- a/src/context/MAIN_CONTEXT/CollectionContext/useCollectionManager.jsx +++ b/src/context/MAIN_CONTEXT/CollectionContext/useCollectionManager.jsx @@ -17,7 +17,6 @@ const useCollectionManager = () => { showCollections, selectedCollectionId, customError: selectedCollectionError, - updateCollectionsData, handleSelectCollection, resetCollection, @@ -110,14 +109,14 @@ const useCollectionManager = () => { performAction(createApiUrl('create'), 'POST', data, 'createNewCollection'); const deleteCollection = (collectionId) => performAction( - createApiUrl(`delete/${collectionId}`), + createApiUrl(`${collectionId}/delete`), 'DELETE', {}, 'deleteCollection' ); const updateCollection = (collectionId, updatedData) => performAction( - createApiUrl(`update/${collectionId}`), + createApiUrl(`${collectionId}/update`), 'PUT', updatedData, 'updateCollection' @@ -213,570 +212,3 @@ const useCollectionManager = () => { }; export default useCollectionManager; -// const validateParams = useCallback( -// (data, expectedTypes, actionName) => { -// let isValid = true; -// const errors = []; - -// expectedTypes.forEach((type, index) => { -// const actualType = Array.isArray(data[index]) -// ? 'array' -// : typeof data[index]; -// if (actualType !== type) { -// isValid = false; -// errors.push(`Param ${index + 1}: Expected ${type}, got ${actualType}`); -// } -// }); - -// if (!isValid) { -// logger.logError(`[${actionName}] Validation errors: `, errors.join('; ')); -// } -// return isValid; -// }, -// [logger] -// ); -// const createNewCollection = useCallback( -// (data) => { -// performAction( -// createApiUrl('create'), -// 'POST', -// data, -// 'createNewCollection', -// { -// paramTypes: ['object'], -// beforeAction: () => console.log('Creating new collection...'), -// afterAction: () => console.log('New collection created.'), -// } -// ); -// }, -// [performAction, createApiUrl] -// ); -// const deleteCollection = useCallback( -// (collectionId) => { -// performAction( -// createApiUrl('delete'), -// 'DELETE', -// { -// collectionId, -// }, -// 'deleteCollection', -// ['string'] -// ); -// }, -// [performAction] -// ); -// const updateCollection = useCallback( -// (collectionId, updatedData) => { -// performAction( -// createApiUrl('update'), -// 'PUT', -// updatedData, -// 'updateCollection', -// ['string', 'object'] -// ); -// }, -// [performAction] -// ); -// const updateCollection = useCallback( -// (collectionId, updatedData) => { -// performAction( -// `/update/${collectionId}`, -// 'PUT', -// updatedData, -// 'updateCollection', -// ['string', 'object'] -// ); -// }, -// [performAction] -// ); -// const updateCardInCollection = useCallback( -// (collectionId, cardId, updatedData) => { -// if ( -// !validateParams( -// [collectionId, cardId, updatedData], -// ['string', 'string', 'object'] -// ) -// ) { -// logger.logError('Invalid parameter types for updateCardInCollection.'); -// return; -// } -// performAction( -// `/updateCard/${collectionId}/${cardId}`, -// 'PUT', -// updatedData, -// 'updateCardInCollection' -// ); -// }, -// [performAction, logger] -// ); -// /** -// * Updates the chart data for a specific collection. -// * @param {string} userId - The ID of the user who owns the collection. -// * @param {string} collectionId - The ID of the collection for which chart data is being updated. -// * @param {Object} chartData - The updated chart data for the collection. The structure of this data depends on how chart data is managed in your application. -// * @returns {Promise} The response from the server. -// */ -// const updateChartDataInCollection = async (collectionId, chartData) => { -// logger.logEvent('updateChartDataInCollection start', { -// collectionId, -// chartData, -// }); -// try { -// const response = await fetchWrapper( -// createApiUrl(`/${collectionId}/updateChartData`), -// 'PUT', -// chartData -// ); -// // const data = handleApiResponse(response, 'updateChartDataInCollection'); -// // updateSelectedCollection(data); - -// // return data; -// } catch (error) { -// logger.logEvent('updateChartDataInCollection error', error); -// throw error; -// } -// }; -// const checkAndUpdateCardPrices = useCallback(async (cards, collection) => { -// const response = await fetchWrapper( -// createApiUrl('/allCollections/automatedPriceUpdate'), -// 'PUT', -// { cards, type: 'automated' } -// ); -// // const data = handleApiResponse(response, 'checkAndUpdateCardPrices'); -// // updateSelectedCollection(data); -// }); -// const createNewCollection = useCallback( -// (data) => { -// if (!validateParams([data], ['object'])) { -// logger.logError('Invalid parameter types for createNewCollection.'); -// return; -// } -// performAction('/create', 'POST', data, 'createNewCollection'); -// }, -// [performAction, logger] -// ); -// const deleteCollection = useCallback( -// (collectionId) => { -// if (!validateParams([collectionId], ['string'])) { -// logger.logError('Invalid parameter types for deleteCollection.'); -// return; -// } -// performAction( -// `/delete/${collectionId}`, -// 'DELETE', -// {}, -// 'deleteCollection' -// ); -// }, -// [performAction, logger] -// ); -// const updateAndSyncCollection = useCallback( -// (collectionId, updatedData) => { -// if (!validateParams([collectionId, updatedData], ['string', 'object'])) { -// logger.logError('Invalid parameter types for updateAndSyncCollection.'); -// return; -// } -// performAction( -// `/update/${collectionId}`, -// 'PUT', -// updatedData, -// 'updateAndSyncCollection' -// ); -// }, -// [performAction, logger] -// ); -// const addCardsToCollection = useCallback( -// (newCards, collection) => { -// console.log('addCardsToCollection', newCards, collection); -// if (!validateParams([[newCards], collection], ['object', 'object'])) { -// logger.logError( -// `Invalid parameter types for addCardsToCollection, showing: ${ -// (typeof newCards, typeof collection) -// }` -// ); -// return; -// } -// performAction( -// '/update', -// 'PUT', -// { cards: newCards }, -// 'addCardsToCollection' -// ); -// }, -// [performAction, logger] -// ); -// // Effect to fetch collections on initial load or userId change -// useEffect(() => { -// fetchCollections(); -// }, [fetchCollections]); -// const { fetchData: fetchCollections, isLoading: isLoadingCollections } = -// useGet({ -// userId, -// isLoggedIn, // This might be omitted if not relevant for the fetch -// hasFetchedFlag: hasFetchedCollections, -// path: '/allCollections', -// setLoadingFlag: setHasFetchedCollections, -// updateStates: (responseData) => { -// // Assuming your updateStates function is defined to update the relevant states -// updateStates(responseData); -// }, -// setError, // Assuming setError is a state setter for storing any fetch errors -// fetchWrapper, // Your custom fetch wrapper function -// createApiUrl, // Function to create the full API URL -// logger, // Your logging function or hook -// }); - -// // To initiate fetching: -// useEffect(() => { -// fetchCollections(); -// }, []); - -// const getAllCollectionsForUser = useCallback(async () => { -// const loadingID = 'fetchCollections'; // Define a unique loading ID -// if (!userId || isLoading(loadingID) || hasFetchedCollections) return; -// try { -// const { data, error } = await fetchWrapper( -// createApiUrl('/allCollections'), -// 'GET', -// null, -// loadingID -// ); -// if (error) { -// throw new Error(error); -// } -// const collectionData = JSON.parse(data); -// console.log('GET ALL COLLECTIONS', collectionData); -// setCollectionData({ data: collectionData }); -// setAllCollections(collectionData?.data || []); -// setSelectedCollection(collectionData?.data?.[0] || DEFAULT_COLLECTION); -// setHasFetchedCollections(true); -// } catch (error) { -// setError(error.message || 'Failed to fetch collections'); -// logger.logEvent( -// 'Error fetching collections getAllCollectionsForUser', -// error.message -// ); -// } -// }, [ -// userId, -// hasFetchedCollections, -// createApiUrl, -// fetchWrapper, -// logger, -// isLoading, -// setError, -// setCollectionData, -// setAllCollections, -// setSelectedCollection, -// setHasFetchedCollections, -// ]); -// useEffect(() => { -// // if (isLoggedIn && userId && !hasFetchedCollections) { -// if (userId && !hasFetchedCollections) { -// getAllCollectionsForUser(); -// } -// }, [selectedCollection, setSelectedCollection]); -// const getAllCollectionsForUser = useCallback(async () => { -// const loadingID = 'fetchCollections'; // Define a unique loading ID -// if (!userId || isLoading(loadingID) || hasFetchedCollections) { -// return; -// } -// try { -// // fetchWrapper should return the data directly or throw an error if unsuccessful -// const responseData = await fetchWrapper( -// createApiUrl('/allCollections'), -// 'GET', -// null, -// loadingID -// ); -// if ( -// (responseData && responseData?.status === 200) || -// responseData?.status === 201 -// ) { -// console.log('SUCCESS: fetching collections'); -// updateStates(responseData); -// } -// } catch (error) { -// console.error(error); -// setError(error.message || 'Failed to fetch collections'); -// logger.logEvent( -// 'Error fetching collections getAllCollectionsForUser', -// error.message -// ); -// } finally { -// setHasFetchedCollections(true); // Prevent re-fetching by updating state -// } -// }, [ -// userId, -// hasFetchedCollections, -// createApiUrl, -// fetchWrapper, -// logger, -// isLoading, -// setError, -// setCollectionData, -// setAllCollections, -// setSelectedCollection, -// setHasFetchedCollections, -// startLoading, // Include startLoading and stopLoading if they are not globally available -// stopLoading, -// ]); -// const createNewCollection = async (coData) => { -// const loadingID = 'createNewCollection'; -// if (!userId || isLoading(loadingID)) return; -// try { -// const { data, error } = await fetchWrapper( -// createApiUrl('/create'), -// 'POST', -// coData, -// loadingID -// ); -// if (error) { -// throw new Error(error); -// } -// updateSelectedCollection(data.data); // Assuming updateSelectedCollection updates context or state -// return data.data; -// } catch (error) { -// setError(error.message || 'Failed to create new collection'); -// logger.logEvent( -// 'Error creating new collection', -// 'createNewCollection', -// error.message -// ); -// throw error; -// } -// }; - -// /** -// * Updates and synchronizes a specific collection for a user. -// * @param {string} userId - The ID of the user who owns the collection. -// * @param {string} collectionId - The ID of the collection to be updated. -// * @param {Object} updatedData - The updated data for the collection. -// * @param {string} updatedData.name - (Optional) New name of the collection. -// * @param {string} updatedData.description - (Optional) New description of the collection. -// * @param {Array} updatedData.cards - (Optional) Array of updated cards. -// * @returns {Promise} The response from the server. -// */ -// const updateAndSyncCollection = async (collectionId, updatedData) => { -// const loadingID = 'updateAndSyncCollection'; -// if (!userId || isLoading(loadingID)) return; -// try { -// const { data, error } = await fetchWrapper( -// createApiUrl(`/${collectionId}`), -// 'PUT', -// updatedData, -// loadingID -// ); -// if (error) { -// throw new Error(error); -// } -// setAllCollections((prev) => -// prev.map((collection) => -// collection._id === collectionId ? data.data : collection -// ) -// ); -// updateSelectedCollection(data.data); // Assuming updateSelectedCollection updates context or state -// return data.data; -// } catch (error) { -// setError(error.message || 'Failed to update collection'); -// logger.logEvent('updateAndSyncCollection error', error); -// throw error; -// } -// }; - -// /** -// * Deletes a specific collection for a user. -// * @param {string} collectionId - The ID of the collection to be deleted. -// * @returns {Promise} The response from the server. -// */ -// const deleteCollection = async (collectionId) => { -// const loadingID = 'deleteCollection'; -// if (!userId || isLoading(loadingID)) return; -// setError(null); -// try { -// const response = await fetchWrapper( -// createApiUrl(`/${collectionId}`), -// 'DELETE', -// { collectionId }, -// loadingID -// ); -// // const data = handleApiResponse(response, 'addCardsToCollection'); - -// setAllCollections((prev) => -// prev.filter((collection) => collection?._id !== collectionId) -// ); -// return response; -// } catch (error) { -// setError(error); -// logger.logEvent('deleteCollection error', error); -// throw error; -// } -// }; -// /** -// * Adds new cards to a specific collection. -// * @param {string} userId - The ID of the user who owns the collection. -// * @param {string} collectionId - The ID of the target collection. -// * @param {Array} cards - Array of card objects to be added. -// * @returns {Promise} The response from the server. -// */ -// const addCardsToCollection = async (cards, collection) => { -// const loadingID = 'addCardsToCollection'; -// if (!userId || isLoading(loadingID)) return; -// setError(null); -// const newCards = []; -// const updatedCards = []; - -// for (const card of cards) { -// const existingCard = collection?.cards?.find((c) => c.id === card.id); - -// if (existingCard) { -// // await updateCardsInCollection(collection._id, [card], 'increment'); -// existingCard.tag = 'incremented'; -// updatedCards.push(existingCard); -// } else { -// card.tag = 'added'; -// newCards.push({ ...card, quantity: 1 }); -// } -// } - -// if (newCards.length > 0) { -// logger.logEvent('addCardsToCollection ADD', { -// newCards, -// collection, -// }); -// const data = await fetchWrapper( -// createApiUrl(`/${collection?._id}/add`), -// 'POST', -// { cards: newCards }, -// loadingID -// ); -// // const data = handleApiResponse(response, 'addCardsToCollection'); -// updateSelectedCollection(data.data); -// } -// if (updatedCards.length > 0) { -// logger.logEvent('addCardsToCollection UPDATE', { -// updatedCards, -// collection, -// }); -// const data = await fetchWrapper( -// createApiUrl(`/${collection._id}/update`), -// 'PUT', -// { cards: updatedCards, type: 'increment' }, -// loadingID -// ); -// // const data = handleApiResponse(response, 'addCardsToCollection'); -// updateSelectedCollection(data.data); -// } -// }; -// /** -// * Removes cards from a specific collection. -// * @param {string} userId - The ID of the user who owns the collection. -// * @param {string} collectionId - The ID of the collection from which cards are being removed. -// * @param {Array} cardIds - Array of card object IDs to be removed. -// * @returns {Promise} The response from the server. -// */ -// const removeCardsFromCollection = async (cards, cardIds, collection) => { -// const loadingID = 'removeCardsFromCollection'; -// if (!userId || isLoading(loadingID)) return; -// setError(null); -// const collectionId = collection._id; -// const cardsToRemove = []; -// const cardsToDecrement = []; -// for (const card of cards) { -// const existingCard = collection?.cards?.find((c) => c.id === card.id); -// if (existingCard) { -// if (existingCard.quantity > 1) { -// existingCard.tag = 'decremented'; -// cardsToDecrement.push(existingCard); -// } else { -// card.tag = 'removed'; -// cardsToRemove.push(card); -// } -// } -// } -// try { -// if (cardsToRemove.length > 0) { -// logger.logEvent('removeCardsFromCollection REMOVE', { -// collectionId, -// cardsToRemove, -// }); -// const response = await fetchWrapper( -// createApiUrl(`/${collectionId}/remove`), -// 'DELETE', -// { cards: cardsToRemove }, -// loadingID -// ); -// const data = handleApiResponse(response, 'removeCardsFromCollection'); -// updateSelectedCollection(data); -// } - -// if (cardsToDecrement.length > 0) { -// logger.logEvent('removeCardsFromCollection DECREMENT', { -// collectionId, -// cardsToDecrement, -// }); -// const response = await fetchWrapper( -// createApiUrl(`/${collectionId}/update`), -// 'PUT', -// { cards: cardsToDecrement, type: 'decrement' }, -// loadingID -// ); -// const data = handleApiResponse(response, 'removeCardsFromCollection'); -// updateSelectedCollection(data); -// } -// } catch (error) { -// setError(error); -// logger.logEvent('removeCardsFromCollection error', error); -// throw error; -// } -// }; -// const getAllCollectionsForUser = useCallback(async () => { -// if (!userId || hasFetchedCollections) return; - -// try { -// const response = await fetchWrapper( -// createApiUrl('/allCollections'), -// 'GET' -// ); -// const data = handleApiResponse(response, 'getAllCollectionsForUser'); -// // console.log('getAllCollectionsForUser', data); -// setCollectionData({ data: data }); -// setAllCollections(data?.data); -// setSelectedCollection(data?.data?.[0]); -// setHasFetchedCollections(true); -// } catch (error) { -// logger.logEvent( -// 'Error fetching collections', -// 'getAllCollectionsForUser', -// error.message -// ); -// } -// }, [ -// createApiUrl, -// fetchWrapper, -// handleApiResponse, -// logger, -// allCollections, -// hasFetchedCollections, -// userId, -// ]); -// Call getAllCollectionsForUser on component mount -// useEffect(() => { -// if (userId && !hasFetchedCollections) { -// getAllCollectionsForUser(); -// } -// }, [userId, hasFetchedCollections, getAllCollectionsForUser]); - -// useEffect(() => { -// if (userId && !hasFetchedCollections) { -// // console.log('getAllCollectionsForUser'); -// getAllCollectionsForUser(); -// } -// }, [userId, getAllCollectionsForUser, hasFetchedCollections]); -// useEffect(() => { -// if (hasFetchedCollections) { -// // console.log('setAllCollections', collectionData?.data?.allCollections); -// setAllCollections(collectionData?.data); -// // updateSelectedCollection(collectionData?.data?.[0]); -// } -// }, [hasFetchedCollections, collectionData]); diff --git a/src/context/MAIN_CONTEXT/CollectionContext/useCollectionStats.jsx b/src/context/MAIN_CONTEXT/CollectionContext/useCollectionStats.jsx deleted file mode 100644 index 3b6f7cb..0000000 --- a/src/context/MAIN_CONTEXT/CollectionContext/useCollectionStats.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useEffect, useState } from 'react'; -import useSelectedCollection from './useSelectedCollection'; - -const useCollectionStats = () => { - const { allCollections } = useSelectedCollection(); - const [collectionStats, setCollectionStats] = useState({}); - const [metaStats, setMetaStats] = useState({}); - const totals = []; - const quantities = []; - useEffect(() => { - const stats = {}; - for (const collection of allCollections) { - const { - totalPrice, - totalQuantity, // Fixed typo from 'toalQuantity' to 'totalQuantity' - nivoChartData, - newNivoChartData, - nivoTestData, - averagedChartData, - chartData, - muiChartData, - name, - descriptions, - lastUpdated, - collectionPriceHistory, - dailyCollectionPriceHistory, - createdAt, - collectionStatistics, - id, // Assuming 'id' is available in 'collection' for mapping - } = collection; - - const { avgPrice, highPoint, lowPoint, percentageChange, priceChange } = - collectionStatistics; - - totals.push(totalPrice); - quantities.push(totalQuantity); - - stats[collection.id] = { - totalPrice, - totalQuantity, - nivoChartData, - newNivoChartData, - nivoTestData, - averagedChartData, - chartData, - muiChartData, - name, - descriptions, - lastUpdated, - collectionPriceHistory, - dailyCollectionPriceHistory, - createdAt, - avgPrice, - highPoint, - lowPoint, - percentageChange, - priceChange, - collectionStatistics, - }; - } - - setCollectionStats(stats); - console.log('COLLECTION STATS RECORDED: ', stats); - }, []); // Dependency array ensures this effect runs only when allCollections changes - - const calculateAndSetMetaStats = () => { - const metaStats = {}; - metaStats.totalValue = totals.reduce((acc, total) => acc + total, 0); - metaStats.totalQuantity = quantities.reduce( - (acc, quantity) => acc + quantity, - 0 - ); - setMetaStats(metaStats); - console.log('META STATS RECORDED: ', metaStats); - return metaStats; - }; - - useEffect(() => { - calculateAndSetMetaStats(); - }, []); - return { collectionStats, metaStats }; -}; - -export default useCollectionStats; diff --git a/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx b/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx index d6150de..72558aa 100644 --- a/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx +++ b/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx @@ -5,74 +5,62 @@ import { DEFAULT_CARDS_COUNT, } from '../../constants'; import useLocalStorage from '../../hooks/useLocalStorage'; -import jsonData from './nivoTestData.json'; +import jsonData from '../../../data/nivoTestData.json'; import _set from 'lodash/set'; function useSelectedCollection() { const { nivoTestData } = jsonData; - const [collections, setCollections] = useLocalStorage('collections', { allIds: [], byId: { - [SELECTED_COLLECTION_ID]: - DEFAULT_COLLECTION.addMultipleDefaultCards(DEFAULT_CARDS_COUNT), + [SELECTED_COLLECTION_ID]: DEFAULT_COLLECTION, }, selectedId: SELECTED_COLLECTION_ID, showCollections: true, nivoTestData: nivoTestData, }); const [selectedCollectionId, setSelectedCollectionId] = useState(null); - const [collectionsVisible, setCollectionsVisible] = useState(true); const [customError, setCustomError] = useState(null); const prevSelectedCollectionIdRef = useRef(null); useEffect(() => { prevSelectedCollectionIdRef.current = selectedCollectionId; }, [selectedCollectionId]); - const isCollectionShown = useMemo( - () => collections.selectedId !== null, - [collections.selectedId] - ); const getSelectedCollection = useMemo( () => collections.byId[collections.selectedId] || DEFAULT_COLLECTION, [collections.byId, collections.selectedId] ); const handleSelectCollection = useCallback( (collection) => { - console.log('SELECTED COLLECTION ID', collection._id); - const prevSelectedCollectionId = prevSelectedCollectionIdRef.current; - console.log('Previous selected collection ID:', prevSelectedCollectionId); - setSelectedCollectionId(collection._id); + console.log('SELECTED COLLECTION ID', collection?._id); + // setSelectedCollectionId(collection?._id); setCustomError(null); - setCollectionsVisible(false); // Hide collection list to show the selected collection's details. - const collectionId = collection._id; - if (!collections.byId[collectionId]) { + + if (!collections.byId[collection?._id]) { setCustomError('Invalid collection selected'); return; } setCollections((prev) => ({ ...prev, - selectedId: collectionId, - showCollections: false, + selectedId: collection?._id, + showCollections: !prev.showCollections, + // showCollections: true, })); + // if (prevSelectedCollectionIdRef.current !== collection?._id) { + // toggleShowCollections(); + // } setCustomError(null); }, [collections.byId, setCollections, setSelectedCollectionId] ); - const toggleShowCollections = useCallback(() => { - setCollections((prev) => ({ - ...prev, - showCollections: !prev.showCollections, - })); - }, [setCollections]); - const handleBackToCollections = useCallback(() => { - setCollections((prev) => ({ - ...prev, - selectedId: null, - showCollections: true, - })); - setCustomError(null); - }, [setCollections]); + // const handleBackToCollections = useCallback(() => { + // setCollections((prev) => ({ + // ...prev, + // selectedId: null, + // showCollections: true, + // })); + // setCustomError(null); + // }, [setCollections]); const updateCollectionField = useCallback( (collectionId, fieldPath, value) => { setCollections((prev) => @@ -129,8 +117,6 @@ function useSelectedCollection() { ); const prevCollectionsRef = useRef(); - - // useEffect to log changes useEffect(() => { if (prevCollectionsRef.current) { console.log('Collections data updated:', collections); @@ -142,16 +128,15 @@ function useSelectedCollection() { selectedCollectionId: collections.selectedId, selectedCollection: getSelectedCollection, allCollections: Object.values(collections.byId), - showCollections: collectionsVisible, + showCollections: !!collections.showCollections, nivoTestData: nivoTestData, handleSelectCollection, - handleBackToCollections, + handleBackToCollections: resetCollection, updateCollectionField, resetCollection, updateCollectionsData, customError, - isCollectionShown, - toggleShowCollections, + // toggleShowCollections, addNewCollection, removeCollection, setCustomError, @@ -160,349 +145,3 @@ function useSelectedCollection() { } export default useSelectedCollection; - -// import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -// import { -// DEFAULT_COLLECTION, -// SELECTED_COLLECTION_ID, -// DEFAULT_CARDS_COUNT, -// } from '../../constants'; -// import useLocalStorage from '../../hooks/useLocalStorage'; -// import _set from 'lodash/set'; // Assuming lodash is installed -// function useSelectedCollection() { -// const prevSelectedCollectionRef = useRef(SELECTED_COLLECTION_ID); -// const [collections, setCollections] = useLocalStorage('collections', { -// allCollections: [DEFAULT_COLLECTION], -// selectedCollection: DEFAULT_COLLECTION, -// allIds: [SELECTED_COLLECTION_ID], -// selectedId: SELECTED_COLLECTION_ID, -// byId: { -// [SELECTED_COLLECTION_ID]: -// DEFAULT_COLLECTION.addMultipleDefaultCards(DEFAULT_CARDS_COUNT), -// }, -// prevId: SELECTED_COLLECTION_ID, -// showCollections: true, -// }); -// const [selectedCollectionId, setSelectedCollectionId] = useLocalStorage( -// 'selectedCollectionId', -// SELECTED_COLLECTION_ID -// ); -// const [customError, setCustomError] = useState(null); -// const isCollectionShown = useMemo( -// () => collections.selectedId !== null, -// [collections.selectedId] -// ); -// const getSelectedCollection = useMemo( -// () => collections.byId[collections.selectedId] || DEFAULT_COLLECTION, -// [collections.byId, collections.selectedId] -// ); -// const handleSelectCollection = useCallback( -// (collection) => { -// const collectionId = collection._id; -// console.log(collectionId); -// if (!collections.byId[collectionId]) { -// setCustomError('Invalid collection selected'); -// return; -// } -// setCollections((prev) => ({ -// ...prev, -// selectedId: collectionId, -// selectedCollection: collections.byId[collectionId], - -// showCollections: false, -// })); -// setCustomError(null); -// }, -// [setCollections] -// ); -// const handleBackToCollections = useCallback(() => { -// setCollections((prev) => ({ -// ...prev, -// selectedId: null, -// selectedCollection: null, -// showCollections: true, -// })); -// setCustomError(null); -// }, [setCollections]); -// const updateCollectionField = useCallback( -// (collectionId, fieldPath, value) => { -// setCollections((prev) => { -// const updatedCollections = { ...prev }; -// _set(updatedCollections.byId, `${collectionId}.${fieldPath}`, value); -// return updatedCollections; -// }); -// }, -// [setCollections] -// ); -// const resetCollection = useCallback(() => { -// setCollections((prev) => ({ -// ...prev, -// selectedId: null, // Reset selected collection ID to null -// showCollections: true, // Optionally, show all collections again -// })); -// setCustomError(null); // Clear any existing errors -// }, [setCollections]); - -// const updateCollectionsData = useCallback( -// (newCollections, collectionsData) => { -// setCollections((prev) => { -// const updatedById = { ...prev.byId, ...collectionsData }; // Assuming collectionsData is a map of {collectionId: collection} -// // Ensure newCollections are properly incorporated into the state -// newCollections.forEach((collection) => { -// updatedById[collection._id] = collection; -// }); -// let newSelectedId = prev.selectedId; -// // Check if the current selected collection needs to be updated -// if ( -// updatedById[newSelectedId] === undefined || -// newSelectedId === null -// ) { -// newSelectedId = newCollections[0]?._id || null; -// } -// return { -// ...prev, -// byId: updatedById, -// allIds: Object.keys(updatedById), -// selectedId: newSelectedId, -// showCollections: !newSelectedId, // Hide or show collections based on whether a new selection is made -// }; -// }); -// }, -// [setCollections] -// ); -// const addNewCollection = useCallback( -// (newCollection) => { -// setCollections((prev) => { -// const newId = new Date().getTime().toString(); // Simple unique ID generation -// return { -// ...prev, -// allIds: [...prev.allIds, newId], -// byId: { ...prev.byId, [newId]: newCollection }, -// }; -// }); -// }, -// [setCollections] -// ); - -// const removeCollection = useCallback( -// (collectionId) => { -// setCollections((prev) => { -// const { [collectionId]: removed, ...remainingCollections } = prev.byId; -// return { -// ...prev, -// allIds: prev.allIds.filter((id) => id !== collectionId), -// byId: remainingCollections, -// selectedId: prev.selectedId === collectionId ? null : prev.selectedId, -// }; -// }); -// }, -// [setCollections] -// ); - -// const toggleShowCollections = useCallback(() => { -// setCollections((prev) => ({ -// ...prev, -// showCollections: !prev.showCollections, -// })); -// }, [setCollections]); -// // useEffect(() => { -// // prevSelectedCollectionRef.current = collections.selectedId; -// // if (prevSelectedCollectionRef.current !== collections.selectedId) { -// // setSelectedCollectionId(collections.selectedId); -// // } -// // }, [collections.selectedId, setSelectedCollectionId]); - -// return { -// selectedCollectionId: collections.selectedId, -// selectedCollection: collections.selectedCollection, -// allCollections: Object.values(collections.byId), -// showCollections: collections.showCollections, -// handleSelectCollection, -// handleBackToCollections, -// updateCollectionField, -// addNewCollection, -// removeCollection, -// toggleShowCollections, -// customError, -// setCustomError, -// }; -// } - -// export default useSelectedCollection; - -// // function useSelectedCollection() { -// // const [selectedCollectionId, setSelectedCollectionId] = useLocalStorage( -// // 'selectedCollectionId', -// // SELECTED_COLLECTION_ID -// // ); -// // const [allCollectionIds, setAllCollectionIds] = useLocalStorage( -// // 'allCollectionIds', -// // [SELECTED_COLLECTION_ID] -// // ); -// // const [showCollections, setShowCollections] = useLocalStorage( -// // 'showCollections', -// // true -// // ); -// // const isCollectionShown = useMemo( -// // () => selectedCollectionId !== null, -// // [selectedCollectionId] -// // ); // Use useMemo for derived state - -// // const [collectionViewState, setCollectionViewState] = useState({ -// // mode: { -// // showListOfAllCollections: !isCollectionShown, -// // showSelectedCollection: isCollectionShown, -// // }, -// // visible: { -// // selected: selectedCollectionId, -// // all: allCollectionIds, -// // }, -// // toggleViewState: (state) => { -// // setCollectionViewState((prev) => ({ -// // ...prev, -// // mode: state, -// // })); -// // }, -// // }); -// // const [selectedCollection, setSelectedCollection] = useLocalStorage( -// // 'selectedCollection', -// // DEFAULT_COLLECTION.addMultipleDefaultCards(5) -// // ); -// // const [allCollections, setAllCollections] = useLocalStorage( -// // 'allCollections', -// // [DEFAULT_COLLECTION.addMultipleDefaultCards(5)] -// // ); -// // const [collectionData, setCollectionData] = useLocalStorage( -// // 'collectionData', -// // {} -// // ); -// // const [customError, setCustomError] = useState(null); - -// // const prevSelectedCollectionRef = useRef(DEFAULT_COLLECTION); - -// // const handleSelectCollection = useCallback( -// // (collectionId) => { -// // if (!collectionId) { -// // setCustomError('Invalid collection selected'); -// // return; -// // } -// // const collection = -// // allCollections.find((c) => c._id === collectionId) || -// // DEFAULT_COLLECTION; -// // setSelectedCollection(collection); -// // setSelectedCollectionId(collection._id); -// // setShowCollections(false); -// // setCustomError(null); -// // prevSelectedCollectionRef.current = collection; -// // }, -// // [ -// // allCollections, -// // setSelectedCollection, -// // setSelectedCollectionId, -// // setShowCollections, -// // setCustomError, -// // ] -// // ); - -// // const handleBackToCollections = useCallback(() => { -// // setSelectedCollectionId(null); -// // setSelectedCollection(DEFAULT_COLLECTION); -// // setShowCollections(true); -// // setCustomError(null); -// // }, [ -// // setSelectedCollectionId, -// // setSelectedCollection, -// // setShowCollections, -// // setCustomError, -// // ]); -// // const updateCollectionField = useCallback( -// // (fieldPath, value) => { -// // setSelectedState((prevCollection) => { -// // const updatedCollection = { ...prevCollection }; -// // let currentField = updatedCollection; -// // const keys = fieldPath.split('.'); -// // keys.forEach((key, index) => { -// // if (index === keys.length - 1) { -// // currentField[key] = value; -// // } else { -// // if (!currentField[key]) currentField[key] = {}; -// // currentField = currentField[key]; -// // } -// // }); -// // return updatedCollection; -// // }); -// // }, -// // [setSelectedState] -// // ); - -// // useEffect(() => { -// // // console.log('SELECTED COLLECTION ID:', selectedCollectionId); -// // // console.log('SELECTED COLLECTION:', selectedCollection); -// // // console.log('COLLECTION SHOWN:', isCollectionShown); -// // setShowCollections(isCollectionShown); -// // }, [ -// // selectedCollection, -// // selectedCollectionId, -// // isCollectionShown, -// // setShowCollections, -// // ]); -// // useEffect(() => { -// // const collection = localStorage.getItem('selectedCollection'); -// // setSelectedCollection(collection ? JSON.parse(collection) : null); -// // }, []); -// // const [selectedCollection, setSelectedCollection] = useLocalStorage( -// // 'selectedCollection', -// // DEFAULT_COLLECTION.addMultipleDefaultCards(5) -// // ); -// // const [allCollections, setAllCollections] = useLocalStorage( -// // 'allCollections', -// // [DEFAULT_COLLECTION.addMultipleDefaultCards(5)] -// // ); -// // const [collectionData, setCollectionData] = useLocalStorage( -// // 'collectionData', -// // {} -// // ); -// // const [selectedCollectionId, setSelectedCollectionId] = useLocalStorage( -// // 'selectedCollectionId', -// // null -// // ); -// // const [showCollections, setShowCollections] = useLocalStorage( -// // 'showCollections', -// // true -// // ); -// // const prevSelectedCollectionRef = useRef(DEFAULT_COLLECTION); -// // const [error, setError] = useState(null); - -// // const setSelectedState = useCallback( -// // (collection) => { -// // prevSelectedCollectionRef.current = selectedCollection; // Store current collection as previous -// // setSelectedCollection(collection); // Update selected collection -// // }, -// // [selectedCollection, setSelectedCollection, setSelectedCollectionId] -// // ); - -// // const handleSelectCollection = useCallback( -// // (collection) => { -// // setShowCollections(false); -// // setSelectedState(collection); -// // setSelectedCollectionId(collection._id); -// // }, -// // [ -// // setSelectedState, -// // setSelectedCollectionId, -// // setShowCollections, -// // setCustomError, -// // ] -// // ); -// // const handleBackToCollections = useCallback(() => { -// // console.log('BACK TO COLLECTIONS'); -// // setSelectedState(DEFAULT_COLLECTION.addMultipleDefaultCards(5)); -// // setSelectedCollectionId(null); -// // setShowCollections(true); -// // setCustomError(null); -// // }, [ -// // setSelectedCollectionId, -// // setSelectedState, -// // setShowCollections, -// // setCustomError, -// // ]); diff --git a/src/context/MAIN_CONTEXT/DeckContext/DeckContext.js b/src/context/MAIN_CONTEXT/DeckContext/DeckContext.js index c44cacb..419c15f 100644 --- a/src/context/MAIN_CONTEXT/DeckContext/DeckContext.js +++ b/src/context/MAIN_CONTEXT/DeckContext/DeckContext.js @@ -1,348 +1,107 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ -import React, { - createContext, - useState, - useEffect, - useCallback, - useContext, - useRef, - useMemo, -} from 'react'; -import { BASE_API_URL } from '../../Helpers.jsx'; +// /* eslint-disable @typescript-eslint/no-empty-function */ +import React, { createContext, useCallback, useContext, useMemo } from 'react'; import { + BASE_API_URL, calculateAndUpdateTotalPrice, - defaultContextValue, getCardQuantity, - shouldFetchDecks, -} from './helpers.jsx'; +} from '../../Helpers.jsx'; import { useAuthContext } from '../AuthContext/authContext.js'; import useFetchWrapper from '../../hooks/useFetchWrapper.jsx'; import useLogger from '../../hooks/useLogger.jsx'; import useApiResponseHandler from '../../hooks/useApiResponseHandler.jsx'; import { useLoading } from '../../hooks/useLoading.jsx'; -import { DEFAULT_DECK } from '../../constants.jsx'; +import useSelectedDeck from './useSelectedDeck.jsx'; // Import the deck management hook +import useDeckManager from './useDeckManager.jsx'; // Import the deck management hook -export const DeckContext = createContext(defaultContextValue); +export const DeckContext = createContext(); export const DeckProvider = ({ children }) => { - const { userId, authUser } = useAuthContext(); + const { userId } = useAuthContext(); const handleApiResponse = useApiResponseHandler(); const logger = useLogger('DeckProvider'); - const { fetchWrapper, responseCache } = useFetchWrapper(); - const prevUserIdRef = useRef(userId); - const [deckData, setDeckData] = useState({}); - const [allDecks, setAllDecks] = useState([DEFAULT_DECK]); - const [selectedDeck, setSelectedDeck] = useState(DEFAULT_DECK); - const [selectedCards, setSelectedCards] = useState(selectedDeck?.cards || []); - const [hasFetchedDecks, setHasFetchedDecks] = useState(false); - const [error, setError] = useState(null); + const { fetchWrapper } = useFetchWrapper(); const { startLoading, stopLoading, isLoading } = useLoading(); - const updateStates = useCallback((data) => { - setDeckData(data); - setAllDecks(data?.data || []); - setSelectedDeck(data?.data?.[0] || {}); - }, []); - const createApiUrl = useCallback( - (path) => - `${process.env.REACT_APP_SERVER}/api/users/${userId}/decks${path}`, - [userId] - ); - const updateSelectedDeck = useCallback((newDeck) => { - console.log('Updating selected deck:', newDeck); - setSelectedDeck(newDeck); - setSelectedCards(newDeck?.cards || []); - }, []); - const fetchAndUpdateDecks = useCallback(async () => { - const loadingID = 'fetchAndUpdateDecks'; - if (!userId || isLoading(loadingID) || hasFetchedDecks) return; - try { - const responseData = await fetchWrapper( - createApiUrl('/allDecks'), - 'GET', - null, - loadingID - ); - if ( - (responseData && responseData?.status === 200) || - responseData?.status === 201 - ) { - console.log('SUCCESS: fetching decks'); - updateStates(responseData); - } - } catch (error) { - console.error(error); - setError(error.message || 'Failed to fetch decks'); - logger.logEvent('Failed to fetch decks', error.message); - } finally { - setHasFetchedDecks(true); - } - }, [ - userId, - isLoading, - hasFetchedDecks, - createApiUrl, - fetchWrapper, - responseCache, - startLoading, - stopLoading, - logger, - setError, - setDeckData, - setAllDecks, + // Utilizing useSelectedDeck hook for managing selected deck and its operations + const { + selectedDeck, + allDecks, setSelectedDeck, - setHasFetchedDecks, - ]); - const updateDeckDetails = async (deckId, updatedInfo) => { - const loadingID = 'updateDeckDetails'; - setError(null); - const { name, description, tags, color } = updatedInfo; - console.log('Updating deck details:', updatedInfo); - const updatedDeck = { - ...selectedDeck, - name, - description, - tags, - color, - }; - - setSelectedDeck(updatedDeck); - setAllDecks((prevDecks) => - prevDecks.map((deck) => (deck._id === deckId ? updatedDeck : deck)) - ); - - try { - const deckEndpoint = createApiUrl( - `${userId}/decks/${deckId}/deckDetails` - ); - await fetchWrapper( - deckEndpoint, - 'PUT', - { - name: name, - description: description, - tags: tags, - color: color, - }, - loadingID - ); - } catch (error) { - setError(error); - console.error('Error updating deck details:', error); - } - }; - const createUserDeck = async (userId, newDeckInfo) => { - const loadingID = 'createUserDeck'; - setError(null); - try { - const url = `${BASE_API_URL}/${userId}/decks/createDeck`; - // const cards = Array.isArray(newDeckInfo.cards) - // ? newDeckInfo.cards.map(formatCardData) - // : []; - console.log('NEW DECK INFO:', newDeckInfo); - const data = await fetchWrapper( - url, - 'POST', - { - cards: [], - totalPrice: 0, - description: newDeckInfo?.description || '', - name: newDeckInfo?.name || '', - tags: newDeckInfo?.tags || [], - color: newDeckInfo?.color || '', - }, - loadingID - ); - console.log('NEW DECK DATA:', data); - setDeckData(data.data); - setSelectedDeck(data.data); - setAllDecks((prevDecks) => [...prevDecks, data.data]); - } catch (error) { - setError(error); - console.error(`Failed to create a new deck: ${error.message}`); - } - }; - const deleteUserDeck = async (deckId) => { - const loadingID = 'deleteUserDeck'; - setError(null); - try { - const response = await fetchWrapper( - `/${deckId}/deleteDeck`, - 'DELETE', - { - deckId, - }, - loadingID - ); - // Update local state to reflect the deletion - setAllDecks((prevDecks) => - prevDecks.filter((deck) => deck._id !== deckId) - ); - if (selectedDeck?._id === deckId) { - setSelectedDeck(null); // Clear the selected deck if it's the one being deleted - } - return response; - } catch (error) { - setError(error); - logger.logEvent(`Failed to delete deck: ${error.message}`, error); + fetchAndUpdateDecks, + updateDeckDetails, + createUserDeck, + deleteUserDeck, + addCardToDeck, + removeCardFromDeck, + } = useSelectedDeck(); - throw error; - } - }; - const addCardToDeck = async (cards, deck) => { - const loadingID = 'addCardToDeck'; - setError(null); - const newCards = []; - const updatedCards = []; - - for (const card of cards) { - const existingCard = deck?.cards?.find((c) => c.id === card.id); - - if (existingCard) { - // Assuming there's a function to update cards in a deck - existingCard.tag = 'incremented'; - updatedCards.push(existingCard); - } else { - card.tag = 'added'; - newCards.push({ ...card, quantity: 1 }); - } - } - - if (newCards.length > 0) { - logger.logEvent('addCardsToDeck ADD', { - newCards, - deck, - }); - const response = await fetchWrapper( - createApiUrl(`/${deck._id}/add`), - 'POST', - { cards: newCards }, - loadingID - ); - const data = handleApiResponse(response, 'addCardsToDeck'); - updateSelectedDeck(data.deck); // Assuming a function to update the current deck - } - if (updatedCards.length > 0) { - logger.logEvent('addCardsToDeck UPDATE', { - updatedCards, - deck, - }); - const response = await fetchWrapper( - createApiUrl(`/${deck._id}/update`), - 'PUT', - { cards: updatedCards, type: 'increment' }, - loadingID - ); - const data = handleApiResponse(response, 'addCardsToDeck'); - updateSelectedDeck(data.deck); // Assuming a function to update the current deck - } - }; - const removeCardFromDeck = async (cards, cardIds, deck) => { - const loadingID = 'removeCardFromDeck'; - setError(null); - const deckId = deck?._id; - const cardsToRemove = []; - const cardsToDecrement = []; - - for (const card of cards) { - const existingCard = deck?.cards?.find((c) => c.id === card.id); - if (existingCard) { - if (existingCard?.quantity > 1) { - // Decrement card quantity - existingCard.tag = 'decremented'; - cardsToDecrement.push(existingCard); - } else { - // Quantity is 1, remove the card - card.tag = 'removed'; - cardsToRemove.push(card); - } - } - } - - try { - if (cardsToRemove.length > 0) { - logger.logEvent( - 'removeCardsFromDeck REMOVE', - { - deckId, - cardsToRemove, - }, - loadingID - ); - const response = await fetchWrapper( - createApiUrl(`/${deckId}/remove`), - 'DELETE', - { cards: cardsToRemove } - ); - const data = handleApiResponse(response, 'removeCardsFromDeck'); - updateSelectedDeck(data); // Assuming a function to update the current deck - } + const { + fetchDecks, + createNewDeck, + deleteDeck, + updateDeck, + error, + hasFetchedDecks, + handleError, + setSelectedDeckError, + addCardsToDeck, + removeCardsFromDeck, + addOneToDeck, + removeOneFromDeck, + } = useDeckManager(); - if (cardsToDecrement.length > 0) { - logger.logEvent('removeCardsFromDeck DECREMENT', { - deckId, - cardsToDecrement, - }); - const response = await fetchWrapper( - createApiUrl(`/${deckId}/update`), - 'PUT', - { cards: cardsToDecrement, type: 'decrement' }, - loadingID - ); - const data = handleApiResponse(response, 'removeCardsFromDeck'); - if (data.includes('cards')) { - updateSelectedDeck(data); // Assuming a function to update the current deck - } - } - } catch (error) { - setError(error); - logger.logEvent('removeCardsFromDeck error', error); - throw error; - } - }; + const createApiUrl = useCallback( + (path) => `${BASE_API_URL}/api/users/${userId}/decks${path}`, + [userId] + ); const contextValue = useMemo( () => ({ - error, - - deckData, - allDecks, selectedDeck, - userDecks: allDecks, - selectedCards, - setSelectedCards, - // totalQuantity: selectedDeck?.cards?.length || 0, - getCardQuantity: (cardId) => getCardQuantity(cardId, selectedDeck), - // getTotalCost: () => - // selectedDeck?.cards?.reduce((acc, card) => acc + (card.cost || 0), 0) || - // 0, + allDecks, setSelectedDeck, + fetchAllDecksForUser: fetchDecks, + updateDeckDetails: updateDeckDetails, + cards: selectedDeck?.cards || [], + createUserDeck: createNewDeck, + deleteUserDeck: deleteDeck, + addOneToDeck, + removeOneFromDeck, + // addOneToDeck: (card, deck) => addCardToDeck([card], deck), + // removeOneFromDeck: (card, deck) => removeCardFromDeck([card], deck), + createApiUrl, + isLoading, + startLoading, + stopLoading, + fetchWrapper, + handleApiResponse, + logger, + error, + getCardQuantity: (cardId) => getCardQuantity(cardId, selectedDeck), getTotalCost: () => calculateAndUpdateTotalPrice(selectedDeck), - addOneToDeck: (card, deck) => addCardToDeck([card], deck), - removeOneFromDeck: (card, deck) => removeCardFromDeck([card], deck), - // addOneToDeck: (card, deck) => handleCardAddition(card, deck), - // removeOneFromDeck: (card, deck) => handleCardUpdate(card, deck), - updateDeckDetails: (deckId, updatedInfo) => - updateDeckDetails(deckId, updatedInfo), - createUserDeck, - deleteUserDeck, - // updateAndSyncDeck, - fetchAllDecksForUser: fetchAndUpdateDecks, + userDecks: allDecks, }), [ selectedDeck, allDecks, - selectedCards, - userId, - deckData, + setSelectedDeck, + fetchDecks, + updateDeckDetails, + createNewDeck, + deleteDeck, + addCardToDeck, removeCardFromDeck, - createUserDeck, - deleteUserDeck, - updateDeckDetails, - fetchAndUpdateDecks, + createApiUrl, + isLoading, + startLoading, + stopLoading, + fetchWrapper, + handleApiResponse, + logger, ] ); + return ( {children} ); @@ -355,213 +114,370 @@ export const useDeckStore = () => { } return context; }; -// !-------------- -// useEffect(() => { -// if (userId && !hasFetchedDecks) { -// fetchAndUpdateDecks(); -// } -// }, [userId, hasFetchedDecks, fetchAndUpdateDecks]); -// useEffect(() => { -// // This effect seems to be redundant if hasFetchedDecks is properly set in fetchAndUpdateDecks -// // Assuming deckData is the correct state being updated by fetchAndUpdateDecks -// if (hasFetchedDecks && deckData?.length > 0) { -// setAllDecks(deckData); -// setSelectedDeck(deckData[0]); -// } -// }, [hasFetchedDecks, deckData, setAllDecks, setSelectedDeck]); +// import React, { +// createContext, +// useState, +// useEffect, +// useCallback, +// useContext, +// useRef, +// useMemo, +// } from 'react'; +// import { BASE_API_URL } from '../../Helpers.jsx'; +// import { +// calculateAndUpdateTotalPrice, +// defaultContextValue, +// getCardQuantity, +// shouldFetchDecks, +// } from './helpers.jsx'; +// import { useAuthContext } from '../AuthContext/authContext.js'; +// import useFetchWrapper from '../../hooks/useFetchWrapper.jsx'; +// import useLogger from '../../hooks/useLogger.jsx'; +// import useApiResponseHandler from '../../hooks/useApiResponseHandler.jsx'; +// import { useLoading } from '../../hooks/useLoading.jsx'; +// import { DEFAULT_DECK } from '../../constants.jsx'; +// import useSelectedDeck from './useSelectedDeck.jsx'; // Import your newly created hook -// useEffect(() => { -// console.log('DECKCONTEXT:', contextValue); -// }, [contextValue]); +// export const DeckContext = createContext(defaultContextValue); -// useEffect(() => { -// if (shouldFetchDecks(prevUserIdRef.current, userId)) { -// fetchAndUpdateDecks(); -// } -// prevUserIdRef.current = userId; -// }, [userId, fetchAndUpdateDecks]); // fetchAndUpdateDecks is now stable and won't change unless necessary +// export const DeckProvider = ({ children }) => { +// const { userId, authUser } = useAuthContext(); +// const handleApiResponse = useApiResponseHandler(); +// const logger = useLogger('DeckProvider'); +// const { fetchWrapper, responseCache } = useFetchWrapper(); +// const prevUserIdRef = useRef(userId); +// const [deckData, setDeckData] = useState({}); +// // const [allDecks, setAllDecks] = useState([DEFAULT_DECK]); +// // const [selectedDeck, setSelectedDeck] = useState(DEFAULT_DECK); +// const [selectedCards, setSelectedCards] = useState(selectedDeck?.cards || []); +// const [hasFetchedDecks, setHasFetchedDecks] = useState(false); +// const [error, setError] = useState(null); +// const { startLoading, stopLoading, isLoading } = useLoading(); +// const { +// selectedDeckId, +// selectedDeck, +// allDecks, +// updateDeckField, +// addNewDeck, +// removeDeck, +// setSelectedDeck, +// } = useSelectedDeck(); +// const updateStates = useCallback((data) => { +// setDeckData(data); +// setAllDecks(data?.data || []); +// setSelectedDeck(data?.data?.[0] || {}); +// }, []); +// const createApiUrl = useCallback( +// (path) => +// `${process.env.REACT_APP_SERVER}/api/users/${userId}/decks${path}`, +// [userId] +// ); +// const updateSelectedDeck = useCallback((newDeck) => { +// console.log('Updating selected deck:', newDeck); +// setSelectedDeck(newDeck); +// setSelectedCards(newDeck?.cards || []); +// }, []); +// const fetchAndUpdateDecks = useCallback(async () => { +// const loadingID = 'fetchAndUpdateDecks'; +// if (!userId || isLoading(loadingID) || hasFetchedDecks) return; +// try { +// const responseData = await fetchWrapper( +// createApiUrl('/allDecks'), +// 'GET', +// null, +// loadingID +// ); -// const updateAndSyncDeck = async (newDeckData) => { -// try { -// if (Array.isArray(newDeckData)) { -// // Handle array of deck data -// newDeckData.forEach(async (deck) => { -// // Update each deck in the array -// setAllDecks((prevDecks) => { -// const updatedDecks = prevDecks.map((d) => -// d._id === deck._id ? deck : d -// ); -// return prevDecks.some((d) => d._id === deck._id) -// ? updatedDecks -// : [...updatedDecks, deck]; -// }); +// if ( +// (responseData && responseData?.status === 200) || +// responseData?.status === 201 +// ) { +// console.log('SUCCESS: fetching decks'); +// updateStates(responseData); +// } +// } catch (error) { +// console.error(error); +// setError(error.message || 'Failed to fetch decks'); +// logger.logEvent('Failed to fetch decks', error.message); +// } finally { +// setHasFetchedDecks(true); +// } +// }, [ +// userId, +// isLoading, +// hasFetchedDecks, +// createApiUrl, +// fetchWrapper, +// responseCache, +// startLoading, +// stopLoading, +// logger, +// setError, +// setDeckData, +// setAllDecks, +// setSelectedDeck, +// setHasFetchedDecks, +// ]); +// const updateDeckDetails = async (deckId, updatedInfo) => { +// const loadingID = 'updateDeckDetails'; +// setError(null); +// const { name, description, tags, color } = updatedInfo; +// console.log('Updating deck details:', updatedInfo); +// const updatedDeck = { +// ...selectedDeck, +// name, +// description, +// tags, +// color, +// }; -// // Synchronize each deck with backend -// if (deck._id && userId) { -// const url = `${BASE_API_URL}/${userId}/decks/${deck._id}/updateDeck`; -// console.log('Updating deck in backend:', deck); -// await fetchWrapper(url, 'PUT', { deck }); -// } else { -// console.error('No deck ID or user ID found.'); -// } -// }); -// setSelectedDeck(newDeckData[0]); -// } else if (newDeckData && typeof newDeckData === 'object') { -// // Handle single deck object -// setSelectedDeck(newDeckData); -// setDeckData(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]; -// }); +// setSelectedDeck(updatedDeck); +// setAllDecks((prevDecks) => +// prevDecks.map((deck) => (deck._id === deckId ? updatedDeck : deck)) +// ); -// // Synchronize with backend -// if (newDeckData._id && userId) { -// const url = `${BASE_API_URL}/${userId}/decks/${newDeckData._id}/updateDeck`; -// console.log('Updating deck in backend:', newDeckData); -// await fetchWrapper(url, 'PUT', { deck: newDeckData }); -// } else { -// console.error('No deck ID or user ID found.'); -// } -// } else { -// console.warn( -// 'Unable to determine the type of deck data for update.', -// newDeckData +// try { +// const deckEndpoint = createApiUrl( +// `${userId}/decks/${deckId}/deckDetails` // ); -// return; +// await fetchWrapper( +// deckEndpoint, +// 'PUT', +// { +// name: name, +// description: description, +// tags: tags, +// color: color, +// }, +// loadingID +// ); +// } catch (error) { +// setError(error); +// console.error('Error updating deck details:', error); // } -// } catch (error) { -// console.error(`Failed to update deck in backend: ${error.message}`); -// } -// }; -// !-------------- -// const deleteUserDeck = async (deckId) => { -// // if (!deckId) { -// // setSelectedDeck(allDecks[0]); -// // console.warn('No deck ID provided. Adding card to first deck in list.'); -// // } +// }; +// const createUserDeck = async (userId, newDeckInfo) => { +// const loadingID = 'createUserDeck'; +// setError(null); +// try { +// const url = `${BASE_API_URL}/${userId}/decks/createDeck`; +// // const cards = Array.isArray(newDeckInfo.cards) +// // ? newDeckInfo.cards.map(formatCardData) +// // : []; +// console.log('NEW DECK INFO:', newDeckInfo); +// const data = await fetchWrapper( +// url, +// 'POST', +// { +// cards: [], +// totalPrice: 0, +// description: newDeckInfo?.description || '', +// name: newDeckInfo?.name || '', +// tags: newDeckInfo?.tags || [], +// color: newDeckInfo?.color || '', +// }, +// loadingID +// ); +// console.log('NEW DECK DATA:', data); +// setDeckData(data.data); +// setSelectedDeck(data.data); +// setAllDecks((prevDecks) => [...prevDecks, data.data]); +// } catch (error) { +// setError(error); +// console.error(`Failed to create a new deck: ${error.message}`); +// } +// }; +// const deleteUserDeck = async (deckId) => { +// const loadingID = 'deleteUserDeck'; +// setError(null); +// try { +// const response = await fetchWrapper( +// `/${deckId}/deleteDeck`, +// 'DELETE', +// { +// deckId, +// }, +// loadingID +// ); +// // Update local state to reflect the deletion +// setAllDecks((prevDecks) => +// prevDecks.filter((deck) => deck._id !== deckId) +// ); +// if (selectedDeck?._id === deckId) { +// setSelectedDeck(null); // Clear the selected deck if it's the one being deleted +// } +// return response; +// } catch (error) { +// setError(error); +// logger.logEvent(`Failed to delete deck: ${error.message}`, error); -// try { -// const url = `${BASE_API_URL}/${userId}/decks/${deckId}/deleteDeck`; -// await fetchWrapper(url, 'DELETE'); +// throw error; +// } +// }; +// const addCardToDeck = async (cards, deck) => { +// const loadingID = 'addCardToDeck'; +// setError(null); +// const newCards = []; +// const updatedCards = []; -// // Update local state to reflect the deletion -// setAllDecks((prevDecks) => -// prevDecks.filter((deck) => deck._id !== deckId) -// ); -// if (selectedDeck?._id === deckId) { -// setSelectedDeck(null); // Clear the selected deck if it's the one being deleted +// for (const card of cards) { +// const existingCard = deck?.cards?.find((c) => c.id === card.id); + +// if (existingCard) { +// // Assuming there's a function to update cards in a deck +// existingCard.tag = 'incremented'; +// updatedCards.push(existingCard); +// } else { +// card.tag = 'added'; +// newCards.push({ ...card, quantity: 1 }); +// } // } -// } catch (error) { -// console.error(`Failed to delete deck: ${error.message}`); -// } -// }; -// const updateCardInDeck = async (deckId, card, action) => { -// try { -// const response = await fetchWrapper( -// createApiUrl(`${deckId}/updateCard`), -// 'PUT', -// { card, action } -// ); -// return handleApiResponse(response, 'updateCardInDeck'); -// } catch (error) { -// console.error(`Failed to update card in deck: ${error.message}`); -// } -// }; -// const fetchDeckData = async (deckId) => { -// try { -// const response = await fetchWrapper(createApiUrl(`${deckId}`), 'GET'); -// return handleApiResponse(response, 'fetchDeckData'); -// } catch (error) { -// console.error(`Failed to fetch deck data: ${error.message}`); -// return null; -// } -// }; -// const fetchAndSetDecks = useCallback(async () => { -// try { -// const data = await fetchDecksForUser(); -// // console.log('Response from server for fetch decks:', data); -// if (data && data?.length > 0) { -// const uniqueDecks = removeDuplicateDecks(data); -// setAllDecks((prevDecks) => -// removeDuplicateDecks([...prevDecks, ...uniqueDecks]) +// if (newCards.length > 0) { +// logger.logEvent('addCardsToDeck ADD', { +// newCards, +// deck, +// }); +// const response = await fetchWrapper( +// createApiUrl(`/${deck._id}/add`), +// 'POST', +// { cards: newCards }, +// loadingID // ); -// setDeckData(uniqueDecks[0] || {}); -// } else { -// console.log('No decks found for user.', data); -// // No decks found for user -// const shouldCreateDeck = window.confirm( -// 'No decks found. Would you like to create a new one?' +// const data = handleApiResponse(response, 'addCardsToDeck'); +// updateSelectedDeck(data.deck); // Assuming a function to update the current deck +// } +// if (updatedCards.length > 0) { +// logger.logEvent('addCardsToDeck UPDATE', { +// updatedCards, +// deck, +// }); +// const response = await fetchWrapper( +// createApiUrl(`/${deck._id}/update`), +// 'PUT', +// { cards: updatedCards, type: 'increment' }, +// loadingID // ); +// const data = handleApiResponse(response, 'addCardsToDeck'); +// updateSelectedDeck(data.deck); // Assuming a function to update the current deck +// } +// }; +// const removeCardFromDeck = async (cards, cardIds, deck) => { +// const loadingID = 'removeCardFromDeck'; +// setError(null); +// const deckId = deck?._id; +// const cardsToRemove = []; +// const cardsToDecrement = []; -// if (shouldCreateDeck) { -// const deckName = prompt('Enter the deck name:'); -// const deckDescription = prompt('Enter the deck description:'); -// await createUserDeck(userId, { -// name: deckName, -// description: deckDescription, -// }); +// for (const card of cards) { +// const existingCard = deck?.cards?.find((c) => c.id === card.id); +// if (existingCard) { +// if (existingCard?.quantity > 1) { +// // Decrement card quantity +// existingCard.tag = 'decremented'; +// cardsToDecrement.push(existingCard); +// } else { +// // Quantity is 1, remove the card +// card.tag = 'removed'; +// cardsToRemove.push(card); +// } // } // } -// } catch (error) { -// console.error(`Failed to fetch decks: ${error.message}`); -// } -// }, [fetchDecksForUser, userId]); -// const fetchDecksForUser = useCallback(async () => { -// if (!userId) { -// console.error('No user ID found.'); -// return null; -// } -// logger.logEvent('fetchDecksForUser', {}); -// try { -// const url = `${BASE_API_URL}/${userId}/decks/allDecks`; -// const response = await fetchWrapper(url, 'GET'); -// const data = handleApiResponse(response, 'fetchDecksForUser'); +// try { +// if (cardsToRemove.length > 0) { +// logger.logEvent( +// 'removeCardsFromDeck REMOVE', +// { +// deckId, +// cardsToRemove, +// }, +// loadingID +// ); +// const response = await fetchWrapper( +// createApiUrl(`/${deckId}/remove`), +// 'DELETE', +// { cards: cardsToRemove } +// ); +// const data = handleApiResponse(response, 'removeCardsFromDeck'); +// updateSelectedDeck(data); // Assuming a function to update the current deck +// } -// return data; -// } catch (error) { -// console.error(`Failed to fetch decks for user: ${error.message}`); -// return null; -// } -// }, [userId]); -// const addNewCardToDeck = async (cards, deck) => { -// const response = await fetchWrapper( -// createApiUrl(`${deck._id}/add`), -// 'POST', -// { -// cards: cards, +// if (cardsToDecrement.length > 0) { +// logger.logEvent('removeCardsFromDeck DECREMENT', { +// deckId, +// cardsToDecrement, +// }); +// const response = await fetchWrapper( +// createApiUrl(`/${deckId}/update`), +// 'PUT', +// { cards: cardsToDecrement, type: 'decrement' }, +// loadingID +// ); +// const data = handleApiResponse(response, 'removeCardsFromDeck'); +// if (data.includes('cards')) { +// updateSelectedDeck(data); // Assuming a function to update the current deck +// } +// } +// } catch (error) { +// setError(error); +// logger.logEvent('removeCardsFromDeck error', error); +// throw error; // } -// ); -// return handleApiResponse(response, 'addCardToDeck'); -// }; -// const addCardToDeck = async (cards, deck) => { -// if (!deck) { -// console.error('Invalid deck data', deck); -// return; -// } +// }; -// try { -// const deckId = deck._id; -// const updates = cards.map((card) => { -// const existsInDeck = cardExists(card.id, deckId); -// return existsInDeck -// ? updateCardInDeck(deckId, card, 'increment') -// : addNewCardToDeck(deckId, card); -// }); +// const contextValue = useMemo( +// () => ({ +// error, -// await Promise.all(updates); +// deckData, +// allDecks, +// selectedDeck, +// userDecks: allDecks, +// selectedCards, +// setSelectedCards, +// // totalQuantity: selectedDeck?.cards?.length || 0, +// getCardQuantity: (cardId) => getCardQuantity(cardId, selectedDeck), +// // getTotalCost: () => +// // selectedDeck?.cards?.reduce((acc, card) => acc + (card.cost || 0), 0) || +// // 0, +// setSelectedDeck, +// getTotalCost: () => calculateAndUpdateTotalPrice(selectedDeck), +// addOneToDeck: (card, deck) => addCardToDeck([card], deck), +// removeOneFromDeck: (card, deck) => removeCardFromDeck([card], deck), +// // addOneToDeck: (card, deck) => handleCardAddition(card, deck), +// // removeOneFromDeck: (card, deck) => handleCardUpdate(card, deck), +// updateDeckDetails: (deckId, updatedInfo) => +// updateDeckDetails(deckId, updatedInfo), +// createUserDeck, +// deleteUserDeck, +// // updateAndSyncDeck, +// fetchAllDecksForUser: fetchAndUpdateDecks, +// }), +// [ +// selectedDeck, +// allDecks, +// selectedCards, +// userId, +// deckData, +// addCardToDeck, +// removeCardFromDeck, +// createUserDeck, +// deleteUserDeck, +// updateDeckDetails, +// fetchAndUpdateDecks, +// ] +// ); +// return ( +// {children} +// ); +// }; -// // Fetch and update the deck with new data -// const updatedDeck = await fetchDeckData(deckId); -// updateAndSyncDeck(updatedDeck); -// } catch (error) { -// console.error(`Failed to add card(s) to deck: ${error.message}`); +// export const useDeckStore = () => { +// const context = useContext(DeckContext); +// if (!context) { +// throw new Error('useDeckStore must be used within a DeckProvider'); // } +// return context; // }; diff --git a/src/context/MAIN_CONTEXT/DeckContext/helpers.jsx b/src/context/MAIN_CONTEXT/DeckContext/helpers.jsx deleted file mode 100644 index 85c4452..0000000 --- a/src/context/MAIN_CONTEXT/DeckContext/helpers.jsx +++ /dev/null @@ -1,235 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ - -import { createChartDataEntry } from '../CollectionContext/helpers'; - -/** - * Wraps fetch API calls and implements a rate limit for each HTTP method type. - * @param {String} url - The API URL to make the request to. - * @param {String} method - The HTTP method for the request. - * @param {Object} [body=null] - The request payload if needed. - * @returns {Promise} - The response from the API call. - */ -export const removeDuplicateDecks = (decks) => { - const uniqueDecks = {}; - decks.forEach((deck) => (uniqueDecks[deck._id] = deck)); - return Object.values(uniqueDecks); -}; - -/** - * Removes duplicate cards from an array of cards. - * @param {Array} cards - Array of cards. - * @returns {Array} - Array of cards with no duplicates. - */ -export const calculateAndUpdateTotalPrice = (deck) => { - let totalPrice = 0; - if (deck && deck.cards) { - totalPrice = deck.cards.reduce((total, card) => { - return total + card.price * card.quantity; - }, 0); - } - return totalPrice; -}; - -/** - * Formats card data to be used in the app. - * @param {Object} card - The card object to format. - * @returns {Object} - The formatted card object. - */ -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]) - ), -}); - -export const defaultContextValue = { - deckData: {}, // Provide default values for context properties - allDecks: [], - selectedDeck: {}, - userDecks: [], - totalQuantity: () => 0, - setSelectedDeck: () => {}, - addOneToDeck: () => {}, - removeOneFromDeck: () => {}, - getTotalCost: () => 0, - getCardQuantity: () => 0, - updateAndSyncDeck: () => {}, - updateDeckDetails: () => {}, - createUserDeck: () => {}, - fetchAllDecksForUser: () => {}, - addCardsToDeck: () => {}, - removeCardsFromDeck: () => {}, - updateCardsInDeck: () => {}, - updateOneInDeck: () => {}, -}; - -export const handleCardAddition = (currentCards, cardToAdd) => { - const cardToAddId = String(cardToAdd.id); - console.log('cardToAddId', cardToAddId); - console.log('currentCards', currentCards); - const existingCardIndex = currentCards.findIndex((c) => c.id === cardToAddId); - - if (existingCardIndex !== -1) { - // Card already exists in the deck - const existingCard = currentCards[existingCardIndex]; - existingCard.quantity = existingCard.quantity + 1; - existingCard.totalPrice = existingCard.price * existingCard.quantity; - - // Update the card in the currentCards array - currentCards[existingCardIndex] = existingCard; - } else { - // Card does not exist in the deck, add it - const newCard = { - ...cardToAdd, - id: cardToAddId, - quantity: 1, - totalPrice: cardToAdd.price, // Assuming the cardToAdd has a 'price' field - }; - currentCards.push(newCard); - } - - return [...currentCards]; -}; - -/** - * Handles the updating of a card in the collection. - * @param {Array} currentCards - Current array of cards. - * @param {Object} cardToUpdate - Card object to update. - * @returns {Array} Updated array of cards. - * @example handleCardUpdate([{ id: 1, quantity: 1 }], { id: 1, quantity: 2 }); - **/ -export const handleCardUpdate = ( - cards, - cardUpdate, - deckId - // priceHistory, - // collectionId, - // cardPrice -) => { - return cards.map((card) => { - if (card.id === cardUpdate.id) { - return getUpdatedCard( - card, - cardUpdate, - deckId - // priceHistory, - // collectionId, - // cardPrice - ); - } - return card; - }); -}; -function getUpdatedCard(card, update, priceHistory, deckId, cardPrice) { - console.log('CARD UPDATE:', update); - console.log('CARD:', card); - const totalPrice = cardPrice * (update?.quantity || card?.quantity); - const quantity = update?.quantity || card?.quantity || 1; - const newChartDataEntry = createChartDataEntry(totalPrice); - - console.log('UPDATE QUANTITY: ', update.quantity); - console.log('CARD QUANTITY: ', card.quantity); - console.log('TOTAL PRICE: ', totalPrice); - console.log('NEW CHARTDAT EN', newChartDataEntry); - - return { - ...card, - ...update, - price: cardPrice || card.price, - quantity: quantity, - deckId: deckId, - totalPrice: totalPrice, - lastSavedPrice: { - num: card.price || card.card_prices[0].tcgplayer_price, - timestamp: new Date(), - }, - latestPrice: update.latestPrice, - tag: 'monitored', - chart_datasets: [...card.chart_datasets, newChartDataEntry], - card_prices: update.card_prices, - card_sets: update.card_sets, - card_images: update.card_images, - priceHistory: priceHistory, - }; -} -export const getCardQuantity = (cardId, selectedDeck) => { - const foundCard = selectedDeck?.cards.find((item) => item.id === cardId); - return foundCard?.quantity || 0; -}; -export const shouldFetchDecks = (prevUserId, currentUserId) => { - return prevUserId !== currentUserId && currentUserId != null; -}; -// export const addCardsToDeck = async (deckId, newCards) => { -// if (!Array.isArray(newCards)) { -// console.error('Invalid card data, expected an array'); -// return; -// } - -// try { -// const url = `${BASE_API_URL}/${userId}/decks/${deckId}/add`; -// const updatedDeck = await fetchWrapper(url, 'POST', { cards: newCards }); - -// // Update local state with the new deck data -// if (selectedDeck._id === deckId) { -// setSelectedDeck(updatedDeck.data); -// } -// updateAndSyncDeck(updatedDeck.data); -// } catch (error) { -// console.error(`Failed to add cards to deck: ${error.message}`); -// } -// }; -// export const removeCardsFromDeck = async (deckId, cardIdsToRemove) => { -// if (!Array.isArray(cardIdsToRemove)) { -// console.error('Invalid card IDs'); -// return; -// } - -// try { -// const url = `${BASE_API_URL}/${userId}/decks/${deckId}/remove`; -// const updatedDeck = await fetchWrapper(url, 'POST', { -// cardIds: cardIdsToRemove, -// }); - -// // Update local state with the new deck data -// if (selectedDeck._id === deckId) { -// setSelectedDeck(updatedDeck.data); -// } -// updateAndSyncDeck(updatedDeck.data); -// } catch (error) { -// console.error(`Failed to remove cards from deck: ${error.message}`); -// } -// }; -// export const updateCardsInDeck = async (deckId, updatedCards) => { -// if (!Array.isArray(updatedCards)) { -// console.error('Invalid card data, expected an array'); -// return; -// } - -// try { -// const url = `${BASE_API_URL}/${userId}/decks/${deckId}/update`; -// const updatedDeck = await fetchWrapper(url, 'PUT', { -// cards: updatedCards, -// }); - -// // Update local state with the new deck data -// if (selectedDeck._id === deckId) { -// setSelectedDeck(updatedDeck.data); -// } -// updateAndSyncDeck(updatedDeck.data); -// } catch (error) { -// console.error(`Failed to update cards in deck: ${error.message}`); -// } -// }; diff --git a/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx b/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx new file mode 100644 index 0000000..eb799ab --- /dev/null +++ b/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx @@ -0,0 +1,265 @@ +import { useState, useCallback, useEffect, useMemo, useRef } from 'react'; +import useLogger from '../../hooks/useLogger'; +import { DEFAULT_COLLECTION } from '../../constants'; +import useFetchWrapper from '../../hooks/useFetchWrapper'; +import { useAuthContext } from '../..'; +import { useCookies } from 'react-cookie'; +import useLocalStorage from '../../hooks/useLocalStorage'; +import useSelectedDeck from './useSelectedDeck'; + +const useDeckManager = () => { + const { fetchWrapper, status } = useFetchWrapper(); + const { userId, isLoggedIn } = useAuthContext(); + const logger = useLogger('useDeckManager'); + const { + selectedDeck, + allDecks, + selectedDeckId, + updateMultipleDecks, + handleSelectDeck, + addCardToSelectedDeck, + setCustomError: setSelectedDeckError, + } = useSelectedDeck(); + const [decks] = useLocalStorage('decks', []); + const [error, setError] = useState(null); + const [hasFetchedDecks, setHasFetchedDecks] = useState(false); + const handleError = useCallback( + (error, actionName) => { + const errorMessage = error.message || 'Failed to perform action'; + setError(errorMessage); + logger.logError(`Error in ${actionName}: ${errorMessage}`, error); + }, + [logger] + ); + const baseUrl = `${process.env.REACT_APP_SERVER}/api/users/${userId}/decks`; + const createApiUrl = useCallback((path) => `${baseUrl}/${path}`, [baseUrl]); + + const fetchDecks = useCallback(async () => { + if (!userId || !isLoggedIn || status === 'loading') return; + try { + const responseData = await fetchWrapper( + createApiUrl('allDecks'), + 'GET', + null, + 'fetchDecks' + ); + updateMultipleDecks(responseData?.data); + + // handleSelectDeck(responseData?.data[decks?.byId?.[selectedDeckId]]); + setHasFetchedDecks(true); + } catch (error) { + handleError(error, 'fetchDecks'); + } + }, [ + userId, + isLoggedIn, + status, + fetchWrapper, + createApiUrl, + updateMultipleDecks, + handleError, + handleSelectDeck, + ]); + + const performAction = useCallback( + async (path, method, data, actionName, options = {}) => { + if (!userId || !isLoggedIn || status === 'loading') { + setError('User is not logged in or request is in loading state.'); + return; + } + if (!selectedDeck) { + setSelectedDeckError('No deck selected'); + return; + } + + options.beforeAction?.(); + console.log( + 'PERFORM ACTION', + path, + method, + data, + actionName, + options.paramTypes + ); + + try { + await fetchWrapper(path, method, data, actionName); + options.afterAction?.(); + fetchDecks(); // Refresh decks after any action + } catch (error) { + handleError(error, actionName); + } + }, + [ + userId, + isLoggedIn, + status, + fetchWrapper, + createApiUrl, + fetchDecks, + handleError, + setSelectedDeckError, + selectedDeck, + ] + ); + + const createNewDeck = (data) => + performAction(createApiUrl('create'), 'POST', data, 'createNewDeck'); + const deleteDeck = (deckId) => + performAction(createApiUrl(`${deckId}/delete`), 'DELETE', {}, 'deleteDeck'); + const updateDeck = (deckId, updatedData) => + performAction( + createApiUrl(`${deckId}/update`), + 'PUT', + updatedData, + 'updateDeck' + ); + const updateDeckDetails = useCallback( + async (deckId, updatedInfo) => { + const loadingID = 'updateDeckDetails'; + setError(null); + const { name, description, tags, color } = updatedInfo; + + // Find and update the specific deck + let updatedDeck = {}; + updateDeck((prevDecks) => { + return prevDecks.map((deck) => { + if (deck._id === deckId) { + updatedDeck = { ...deck, name, description, tags, color }; + return updatedDeck; + } + return deck; + }); + // setDecks((prevDecks) => { + // return prevDecks.map((deck) => { + // if (deck._id === deckId) { + // updatedDeck = { ...deck, name, description, tags, color }; + // return updatedDeck; + // } + // return deck; + // }); + }); + + try { + const deckEndpoint = createApiUrl(`/${deckId}/deckDetails`); + await fetchWrapper( + deckEndpoint, + 'PUT', + { name, description, tags, color }, + loadingID + ); + } catch (error) { + setError(error); + console.error('Error updating deck details:', error); + } + + return updatedDeck; // Return updated deck for any further operations + }, + [createApiUrl, fetchWrapper, updateDeck, userId] + ); + const addCardsToDeck = useCallback( + (newCards, deck) => { + if (selectedDeck?._id === 'selectedDeckId') + return console.log('Deck ID has not been set'); + + // CHECK FOR EXISTING CARD IN DECK (newCards[0]) + const existingCard = deck?.cards?.find( + (card) => card.id === newCards[0].id + ); + + const options = { + beforeAction: () => { + if (existingCard) { + console.log('Card already exists in deck'); + return; + } + }, + afterAction: () => { + if (existingCard) { + console.log('Card already exists in deck'); + return; + } + }, + paramTypes: { + deckId: selectedDeckId, + }, + }; + + if (existingCard) { + // UPDATE EXISTING CARD + const cardParams = { cards: [newCards] }; + const fullParams = { ...cardParams, type: 'increment' }; + performAction( + createApiUrl(`${selectedDeck?._id}/cards/update`), + 'PUT', + fullParams, + 'addCardsToDeck', + options + ); + } else { + // ADD NEW CARD + const cardParams = { cards: newCards }; + const fullParams = { ...cardParams, type: 'addNew' }; + addCardToSelectedDeck(newCards); + + performAction( + createApiUrl(`${selectedDeck?._id}/cards/add`), + 'POST', + fullParams, + 'addCardsToDeck', + options + ); + // updateSelectedDeck(cardParams); + } + }, + [performAction, createApiUrl, selectedDeckId, selectedDeck?._id] + ); + + const removeCardsFromDeck = useCallback( + (cards, cardIds, deckId) => { + if (!deckId) { + console.error( + 'No deck selected or deck ID is missing.', + cards, + cardIds, + deckId + ); + return; + } + const arrayOfCards = [cards]; + const arrayOfCardIds = [cardIds]; + const existingCard = deckId?.cards?.find((card) => card.id === cardIds); + const removeType = existingCard?.quantity > 1 ? 'decrement' : 'delete'; + + performAction( + createApiUrl(`${deckId}/cards/remove`), + 'PUT', + { cards: arrayOfCards, cardIds: arrayOfCardIds, type: removeType }, + 'removeCardsFromDeck', + { paramTypes: ['object', 'array'] } + ); + }, + [performAction, createApiUrl] + ); + + return { + fetchDecks, + createNewDeck, + deleteDeck, + updateDeck, + selectedDeck, + allDecks, + error, + hasFetchedDecks, + handleError, + setSelectedDeckError, + addCardsToDeck, + removeCardsFromDeck, + addOneToDeck: (cards, deck) => addCardsToDeck(cards, deck), + removeOneFromDeck: (cards, cardIds, deck) => + removeCardsFromDeck(cards, cardIds, deck), + updateDeckDetails, + }; +}; + +export default useDeckManager; diff --git a/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx b/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx new file mode 100644 index 0000000..a22c1a2 --- /dev/null +++ b/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx @@ -0,0 +1,406 @@ +import { useState, useCallback, useMemo, useRef, useEffect } from 'react'; +import { + DEFAULT_CARDS_COUNT, + DEFAULT_DECK, + SELECTED_DECK_ID, +} from '../../constants'; +import useLocalStorage from '../../hooks/useLocalStorage'; +import _set from 'lodash/set'; + +const useSelectedDeck = () => { + const [decks, setDecks] = useLocalStorage('decks', { + allIds: [], + byId: { + [SELECTED_DECK_ID]: + DEFAULT_DECK.addMultipleDefaultCards(DEFAULT_CARDS_COUNT), + }, + selectedId: SELECTED_DECK_ID, + showDecks: true, + deckHasBeenSelected: false, + deckHasBeenUpdated: false, + }); + + const selectedDeckId = useState(decks?.selectedId)[0]; + const selectedDeck = decks.byId[selectedDeckId] || DEFAULT_DECK; + + const updateDeck = useCallback( + (updatedDeck, deckId = selectedDeckId) => { + setDecks((prev) => ({ + ...prev, + byId: { + ...prev.byId, + [deckId]: updatedDeck, + }, + deckHasBeenUpdated: true, + })); + }, + [setDecks, selectedDeckId] + ); + + const updateMultipleDecks = useCallback( + (decksArray) => { + setDecks((prev) => { + const updatedById = { ...prev.byId }; + const updatedAllIds = new Set(prev.allIds); + + decksArray.forEach((deck) => { + updatedById[deck._id] = deck; + updatedAllIds.add(deck._id); + }); + + return { + ...prev, + byId: updatedById, + allIds: Array.from(updatedAllIds), + deckHasBeenUpdated: true, + }; + }); + }, + [setDecks] + ); + const handleSelectDeck = useCallback( + (deck) => { + const deckId = deck?._id; + setDecks((prev) => ({ + ...prev, + selectedId: deckId, + selectedDeck: deck, + selectedDeckCards: deck?.cards, + deckHasBeenSelected: true, + showDecks: true, + })); + }, + [setDecks] + ); + const addNewDeck = useCallback( + (newDeck) => { + setDecks((prev) => ({ + ...prev, + byId: { + ...prev.byId, + [newDeck._id]: newDeck, + }, + allIds: [...prev.allIds, newDeck._id], + })); + }, + [setDecks] + ); + const removeDeck = useCallback( + (deckId) => { + setDecks((prev) => { + const { [deckId]: removedDeck, ...remainingById } = prev.byId; + return { + ...prev, + byId: remainingById, + allIds: prev.allIds.filter((id) => id !== deckId), + selectedId: prev.selectedId === deckId ? null : prev.selectedId, + }; + }); + }, + [setDecks] + ); + + const addCardToSelectedDeck = useCallback( + (card) => { + if (!selectedDeckId) return; + const updatedDeck = { + ...decks.byId[selectedDeckId], + cards: [...decks.byId[selectedDeckId].cards, card], + }; + updateDeck(updatedDeck); + }, + [decks.byId, selectedDeckId, updateDeck] + ); + + return { + selectedDeckId, + selectedDeck, + allDecks: Object.values(decks.byId), + showDecks: decks.showDecks, + deckHasBeenSelected: decks.deckHasBeenSelected, + deckHasBeenUpdated: decks.deckHasBeenUpdated, + + updateDeck, + updateMultipleDecks, + handleSelectDeck, + addNewDeck, + removeDeck, + addCardToSelectedDeck, + }; +}; + +export default useSelectedDeck; + +// const useSelectedDeck = () => { +// const [decks, setDecks] = useLocalStorage('decks', { +// allIds: [], +// byId: { +// [SELECTED_DECK_ID]: +// DEFAULT_DECK.addMultipleDefaultCards(DEFAULT_CARDS_COUNT), +// }, +// selectedId: SELECTED_DECK_ID, +// selectedDeck: null, +// selectedDeckCards: null, +// deckHasBeenSelected: false, +// deckHasBeenUpdated: false, +// showDecks: false, +// }); +// const [selectedDeckId, setSelectedDeckId] = useState(decks.selectedId); +// const [updatedCardsArray, setUpdatedCardsArray] = useState( +// decks?.byId?.[selectedDeckId]?.cards +// ); +// const prevSelectedDeckIdRef = useRef(null); +// useEffect(() => { +// prevSelectedDeckIdRef.current = selectedDeckId; +// }, [selectedDeckId]); +// const prevDecksRef = useRef(); + +// const handleSelectDeck = useCallback( +// (deck) => { +// const deckId = deck?._id; +// setSelectedDeckId(deckId); +// if (deckId && decks.byId[deckId]) { +// setDecks((prev) => ({ +// ...prev, +// selectedId: deckId, +// selectedDeck: deck, +// selectedDeckCards: deck?.cards, +// deckHasBeenSelected: true, +// showDecks: true, +// })); +// } +// }, +// [decks.byId, setDecks] +// ); + +// const getSelectedDeck = useMemo( +// () => decks.byId[decks.selectedId] || DEFAULT_DECK, +// [decks.byId, decks.selectedId] +// ); + +// const updateDeckField = useCallback( +// (deckId, fieldPath, value) => { +// setDecks((prev) => +// _set({ ...prev }, `byId.${deckId}.${fieldPath}`, value) +// ); +// }, +// [setDecks] +// ); + +// const resetDeck = useCallback(() => { +// setDecks((prev) => ({ +// ...prev, +// selectedId: null, +// showDecks: true, +// selectedDeck: null, +// selectedDeckCards: null, +// deckHasBeenSelected: false, +// })); +// }, [setDecks]); +// const updateDecksData = useCallback( +// (newDecks) => { +// setDecks((prev) => { +// const updatedById = { ...prev.byId }; +// newDecks.forEach((deck) => { +// updatedById[deck._id] = deck; +// }); +// return { +// ...prev, +// byId: updatedById, +// allIds: Object.keys(updatedById), +// }; +// }); +// }, +// [setDecks] +// ); +// const updateSelectedDeck = useCallback( +// (updatedFields) => { +// setDecks((prev) => { +// console.log('UPDATED FIELDS', updatedFields); +// const updatedDeck = { ...prev.byId[prev.selectedId], ...updatedFields }; +// console.log('UPDATED DECK', updatedDeck); +// return { +// ...prev, +// byId: { ...prev.byId, [prev.selectedId]: updatedDeck }, +// selectedDeck: updatedDeck, +// selectedDeckCards: updatedDeck.cards, +// // deckHasBeenSelected: true, +// deckHasBeenUpdated: true, +// }; +// }); +// }, +// [setDecks] +// ); + +// const addNewDeck = useCallback( +// (newDeck) => { +// updateDecksData([newDeck]); +// }, +// [updateDecksData] +// ); + +// const removeDeck = useCallback( +// (deckId) => { +// setDecks((prev) => { +// const { [deckId]: _, ...remainingById } = prev.byId; +// return { +// ...prev, +// allIds: prev.allIds.filter((id) => id !== deckId), +// byId: remainingById, +// selectedId: prev.selectedId === deckId ? null : prev.selectedId, +// }; +// }); +// }, +// [setDecks] +// ); +// const addCardToSelectedDeck = useCallback( +// (card) => { +// if (!selectedDeckId) return; +// console.log('Adding card to selected deck', card); +// const updatedArray = [...decks.byId[selectedDeckId].cards, card]; // Ensure the update is directly reflected in the byId structure +// setUpdatedCardsArray(updatedArray); // Update local state for rendering, though this might be redundant if we rely on the decks state directly +// // updateDeckField(selectedDeckId, 'cards', updatedArray); // Update the byId structure directly +// setDecks((prev) => { +// const updatedDeck = { +// ...prev.byId[selectedDeckId], +// cards: updatedArray, +// }; +// console.log('UPDATED DECK WITH NEW CARD', updatedDeck); +// return { +// ...prev, +// byId: { ...prev.byId, [selectedDeckId]: updatedDeck }, // Directly update the byId structure for the selected deck +// // Since we've updated byId above, we can derive selectedDeck and selectedDeckCards from it directly +// selectedDeck: updatedDeck, +// selectedDeckCards: updatedCardsArray, +// deckHasBeenUpdated: true, +// }; +// }); +// }, +// [setDecks, selectedDeckId, decks.byId] +// ); + +// useEffect(() => { +// if (prevDecksRef.current) { +// console.log('Decks data updated:', decks); +// // handleSelectDeck(decks.byId[decks.selectedId]); +// } +// prevDecksRef.current = decks; +// }, [decks]); // decks array ensures this runs only when collections change +// useEffect(() => { +// console.log('CARDS TWO:', updatedCardsArray); + +// // If you are trying to perform actions based on the updated decks, you should do it here. +// // For instance, if you need to re-select the deck to update UI or other elements, ensure that it's based on the current state. +// }, [updatedCardsArray]); // This will log whenever 'decks' changes. + +// return { +// selectedDeckId: selectedDeckId, +// selectedDeck: getSelectedDeck, +// allDecks: Object.values(decks.byId), +// showDecks: decks.showDecks, +// deckHasBeenSelected: decks.deckHasBeenSelected, +// deckHasBeenUpdated: decks.deckHasBeenUpdated, +// updatedCards: updatedCardsArray, +// addCardToSelectedDeck, +// handleSelectDeck, +// updateDeckField, +// resetDeck, +// updateDecksData, +// updateSelectedDeck, +// addNewDeck, +// removeDeck, +// }; +// }; + +// export default useSelectedDeck; +// const addCardToSelectedDeck = useCallback( +// (card) => { +// if (!selectedDeckId) return; +// console.log('Adding card to selected deck', card); +// const updatedArray = [...decks.selectedDeckCards, card]; +// setUpdatedCardsArray(updatedArray); +// setDecks((prev) => { +// const updatedDeck = { ...prev.byId[selectedDeckId] }; +// updatedDeck.cards = [...updatedDeck.cards, card]; + +// console.log('UPDATED DECK WITH NEW CARD', updatedDeck); +// return { +// ...prev, +// byId: { ...prev.byId, [selectedDeckId]: updatedDeck }, +// selectedDeck: updatedDeck, +// selectedDeckCards: updatedDeck.cards, +// deckHasBeenUpdated: true, +// }; +// }); +// }, +// [setDecks, selectedDeckId] +// ); +// useEffect(() => { +// if (prevSelectedDeckIdRef.current !== selectedDeckId) { +// handleSelectDeck(getSelectedDeck); +// } +// prevSelectedDeckIdRef.current = selectedDeckId; +// }, [selectedDeckId, getSelectedDeck, handleSelectDeck]); +// useEffect(() => { +// prevSelectedDeckIdRef.current = selectedDeckId; +// }, [selectedDeckId]); +// const handleSelectDeck = useCallback( +// (deck) => { +// const deckId = deck?._id; +// console.log('SELECTED DECK ID', deckId); +// const prevSelectedDeckId = prevSelectedDeckIdRef?.current; + +// console.log('Previous selected deck ID:', prevSelectedDeckId); +// setSelectedDeckId(deckId); +// currentSelectedDeckRef.current = deck; +// currentSelectedDeckIdRef.current = deckId; +// if (deckId && decks.byId[deckId]) { +// setDecks((prev) => ({ +// ...prev, +// selectedId: deckId, +// selectedDeck: deck, +// selectedDeckCards: deck?.cards, +// deckHasBeenSelected: true, +// showDecks: true, +// })); +// } +// }, +// [decks.byId, setDecks] +// ); +// const updateDecksData = useCallback( +// (newDecks) => { +// setDecks((prev) => { +// resetDeck(); +// const updatedById = { ...prev.byId }; +// let updatedSelectedDeck = prev.selectedDeck; +// let updatedSelectedDeckCards = prev.selectedDeckCards; +// if ( +// updatedById[currentSelectedDeckIdRef].cards !== +// decks.selectedDeckCards +// ) { +// console.log( +// 'Current Deck Cards:', +// currentSelectedDeckRef.current?.cards, +// decks?.selectedDeck?.cards +// ); +// } +// newDecks.forEach((deck) => { +// updatedById[deck._id] = deck; +// // Check if the updated deck is the currently selected deck +// if (deck._id === prev.selectedId) { +// updatedSelectedDeck = deck; +// updatedSelectedDeckCards = deck.cards; +// } +// }); + +// return { +// ...prev, +// byId: updatedById, +// allIds: Object.keys(updatedById), +// selectedDeck: updatedSelectedDeck, +// selectedDeckCards: updatedSelectedDeckCards, +// }; +// }); +// }, +// [setDecks] +// ); diff --git a/src/context/MAIN_CONTEXT/UserContext/UserContext.js b/src/context/MAIN_CONTEXT/UserContext/UserContext.js index e26a545..cd28a19 100644 --- a/src/context/MAIN_CONTEXT/UserContext/UserContext.js +++ b/src/context/MAIN_CONTEXT/UserContext/UserContext.js @@ -19,7 +19,7 @@ export const UserContext = createContext(defaultContextValue.USER_CONTEXT); export const UserProvider = ({ children }) => { const { userId, isLoggedIn } = useAuthContext(); // Assuming useAuthContext now provides userId directly - const [user, setUser] = useLocalStorage('user', {}); + const [user, setUser] = useLocalStorage('user', null); const logger = useLogger('UserProvider'); const { isLoading } = useLoading(); const { fetchWrapper, status } = useFetchWrapper(); @@ -30,27 +30,35 @@ export const UserProvider = ({ children }) => { [userId] ); + // Fetch user data from the server and update local storage if necessary const fetchUserData = useCallback(async () => { - if (!userId || !isLoggedIn || status === 'loading') return; + if (!userId || !isLoggedIn || isLoading('fetchUserData')) return; + + // Check if user data is already fetched or present in local storage + if (user && Object.keys(user).length !== 0) { + setHasFetchedUser(true); + return; + } + try { const responseData = await fetchWrapper( - `${process.env.REACT_APP_SERVER}/api/users/${userId}/userData`, + createApiUrl('userData'), 'GET', null, - 'getUserData' + 'fetchUserData' ); - setUser(responseData?.data); + setUser(responseData?.data); // Set user data in local storage setHasFetchedUser(true); } catch (error) { console.error('Error fetching user data:', error); setError(error.message || 'Failed to fetch user data'); logger.logEvent('Failed to fetch user data', error.message); } - }, [userId, isLoggedIn, fetchWrapper, setUser, logger]); + }, [userId, isLoggedIn, fetchWrapper, setUser, logger, user]); useEffect(() => { - fetchUserData(); - }, []); + if (userId && isLoggedIn && !hasFetchedUser) fetchUserData(); + }, [userId, isLoggedIn, hasFetchedUser]); const updateUser = useCallback( async (updatedUserData) => { diff --git a/src/context/MISC_CONTEXT/AppContext/AppContextProvider.jsx b/src/context/MISC_CONTEXT/AppContext/AppContextProvider.jsx index 4e20197..1031e32 100644 --- a/src/context/MISC_CONTEXT/AppContext/AppContextProvider.jsx +++ b/src/context/MISC_CONTEXT/AppContext/AppContextProvider.jsx @@ -1,4 +1,3 @@ -// CombinedContext.js import React, { createContext, useCallback, @@ -12,27 +11,51 @@ import { CartContext } from '../../MAIN_CONTEXT/CartContext/CartContext'; import { CollectionContext } from '../../MAIN_CONTEXT/CollectionContext/CollectionContext'; import { defaultContextValue } from '../../constants'; import useLocalStorage from '../../hooks/useLocalStorage'; +import useSelectedCollection from '../../MAIN_CONTEXT/CollectionContext/useSelectedCollection'; import useSelectedContext from '../../hooks/useSelectedContext'; -// Create the combined context -export const AppContext = createContext(defaultContextValue.APP_CONTEXT); +const AppContext = createContext(defaultContextValue.APP_CONTEXT); -// 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); const { selectedContext } = useSelectedContext(); const [cardsWithQuantities, setCardsWithQuantities] = useState([]); - const [allCardsWithQuantities, setAllCardsWithQuantities] = useLocalStorage( - 'allCardsWithQuantities', + // const [allCardsWithQuantities, setAllCardsWithQuantities] = useLocalStorage( + // 'allCardsWithQuantities', + // [] + // ); + const [collectionMetaData, setCollectionMetaData] = useLocalStorage( + 'collectionMetaData', [] ); - const { selectedCollection, allCollections } = Collection; + const { allIds } = useSelectedCollection(); const { selectedDeck, allDecks } = Deck; const { cartData } = Cart; + const compileCollectionMetaData = useCallback(() => { + if (!allCollections || allCollections.length === 0) return; + + const metaData = { + totalValue: allCollections?.reduce( + (total, collection) => total + collection?.totalPrice, + 0 + ), + numCollections: allIds?.length || 0, + topFiveCards: cardsWithQuantities + ?.sort((a, b) => b.price - a.price) + .slice(0, 5), + numCardsCollected: cardsWithQuantities?.length || 0, + }; + + setCollectionMetaData(metaData); + }, []); + + useEffect(() => { + compileCollectionMetaData(); + }, [compileCollectionMetaData]); // Re-calculate metadata when allCollections changes + const isCardInContext = useCallback( (card) => { const cardsList = { @@ -47,7 +70,7 @@ export const AppContextProvider = ({ children }) => { const compileCardsWithQuantities = () => { if (!selectedCollection && !selectedDeck && !cartData) return []; const deckCards = allDecks?.reduce((acc, deck) => { - if (deck.cards) { + if (deck?.cards) { acc = [...acc, ...deck.cards]; } return acc; @@ -64,16 +87,14 @@ export const AppContextProvider = ({ children }) => { if (acc[card.id]) { acc[card.id].quantity += card?.quantity; } else { - // Otherwise, add the card with its current quantity acc[card.id] = { ...card, quantity: card.quantity }; } return acc; }, {}); const quantities = Object.values(cardQuantities); - // console.log('cardsWithQuantities:', quantities); - setCardsWithQuantities(quantities); - setAllCardsWithQuantities(combinedCards); + setCardsWithQuantities(combinedCards); + // setAllCardsWithQuantities(combinedCards); return quantities; }; @@ -81,8 +102,6 @@ export const AppContextProvider = ({ children }) => { useEffect(() => { compileCardsWithQuantities(); }, []); // Dependency array based on when you want to recalculate - - // Combine the context values into one object const appContextValues = useMemo( () => ({ Deck, @@ -99,8 +118,16 @@ export const AppContextProvider = ({ children }) => { getCardQuantities: compileCardsWithQuantities, isCardInContext, checkIfCardIsInContext: isCardInContext, + collectionMetaData, }), - [Deck, Cart, Collection, isCardInContext] // Include cardsWithQuantities here + [ + Deck, + Cart, + Collection, + isCardInContext, + collectionMetaData, + // cardsWithQuantities, + ] // Include cardsWithQuantities here ); return ( diff --git a/src/context/MISC_CONTEXT/CardImagesContext/CardImagesContext.jsx b/src/context/MISC_CONTEXT/CardImagesContext/CardImagesContext.jsx deleted file mode 100644 index 6277838..0000000 --- a/src/context/MISC_CONTEXT/CardImagesContext/CardImagesContext.jsx +++ /dev/null @@ -1,114 +0,0 @@ -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 [imageSrc, setImageSrc] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - // const BASE_API_URL = 'http://localhost:3001/api/card-image'; - const BASE_API_URL = `${process.env.REACT_APP_SERVER}/api/card-image`; - - // Function to download card images - const downloadCardImages = async () => { - setIsLoading(true); - try { - const response = await fetchWrapper(BASE_API_URL + '/download', 'GET'); - const fetchedCards = response.data; - const imageUrls = fetchedCards - .map((card) => { - if (card.card_images && card.card_images.length > 0) { - return card.card_images[0].image_url + '?dummy=' + Date.now(); - } - return null; // or some placeholder image URL - }) - .filter(Boolean); // Remove any nulls - - setCards(fetchedCards); - setImages(imageUrls); // Set all image URLs at once - } catch (error) { - console.error('Error in downloadCardImages:', error); - setError(error.message); - } finally { - setIsLoading(false); - } - }; - - // useEffect(() => { - // downloadCardImages(); - // }, []); - - // Handle random card image selection - useEffect(() => { - if (cards && cards.length > 0) { - const randomCardIndex = Math.floor(Math.random() * cards.length); - const randomCard = cards[randomCardIndex]; - if ( - randomCard && - randomCard.card_images && - randomCard.card_images.length > 0 - ) { - setRandomCardImage(randomCard.card_images[0].image_url); - } - } - }, [cards]); // Dependency on cards means this will rerun when cards are fetched/updated - - 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} - - ); -}; diff --git a/src/context/MISC_CONTEXT/CardImagesContext/useCardManager.jsx b/src/context/MISC_CONTEXT/CardImagesContext/useCardManager.jsx deleted file mode 100644 index 131b9fe..0000000 --- a/src/context/MISC_CONTEXT/CardImagesContext/useCardManager.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useState } from 'react'; - -const useCardManager = (initialCards = []) => { - const [cards, setCards] = useState(initialCards); - - // General function to update a card - const updateCard = (cardId, updatedData) => { - setCards((currentCards) => - currentCards.map((card) => - card.id === cardId ? { ...card, ...updatedData } : card - ) - ); - }; - - // Add a new card - const addCard = (newCardData) => { - setCards([...cards, newCardData]); - }; - - // General utility to update a specific field of a card - const updateField = (cardId, field, value) => { - updateCard(cardId, { [field]: value }); - }; - - // Update a nested field like priceHistory or card_images - const updateNestedField = (cardId, field, newValue) => { - const card = cards.find((card) => card.id === cardId); - updateCard(cardId, { [field]: [...card[field], newValue] }); - }; - - return { - cards, - addCard, - updateCard: updateField, // for updating single fields - updateNestedField, // for updating nested fields like arrays - }; -}; - -export default useCardManager; diff --git a/src/context/MISC_CONTEXT/CardImagesContext/useCardVariantManager.jsx b/src/context/MISC_CONTEXT/CardImagesContext/useCardVariantManager.jsx deleted file mode 100644 index b5762fb..0000000 --- a/src/context/MISC_CONTEXT/CardImagesContext/useCardVariantManager.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import { useState } from 'react'; - -const useCardVariantManager = (initialCards = []) => { - const [cards, setCards] = useState(initialCards); - - // Function to identify the variant index - const findVariantIndex = (cardId, setCode) => - cards.findIndex( - (card) => card.id === cardId && card.card_set.set_code === setCode - ); - - // Function to determine overlay based on rarity - const getOverlayByRarity = (rarity) => { - // Define overlays for different rarities - const overlays = { - Common: 'commonOverlay', - Rare: 'rareOverlay', - 'Ultra Rare': 'ultraRareOverlay', - // ... other rarities - }; - return overlays[rarity] || 'defaultOverlay'; - }; - - // Function to update a specific variant field - const updateVariantField = (cardId, setCode, field, value) => { - const index = findVariantIndex(cardId, setCode); - if (index < 0) return; // Variant not found - - const updatedCards = [...cards]; - updatedCards[index] = { - ...updatedCards[index], - [field]: value, - // Update overlay based on rarity - overlay: getOverlayByRarity(updatedCards[index].card_set.set_rarity), - }; - setCards(updatedCards); - }; - - // Function to add a new variant - const addVariant = (cardId, newVariantData) => { - const updatedCards = [...cards]; - updatedCards.push({ - ...newVariantData, - id: cardId, - // Assign overlay based on rarity - overlay: getOverlayByRarity(newVariantData.card_set.set_rarity), - }); - setCards(updatedCards); - }; - - // Function to update nested fields like priceHistory for a specific variant - const updateNestedField = (cardId, setCode, field, newValue) => { - const index = findVariantIndex(cardId, setCode); - if (index < 0) return; // Variant not found - - const updatedCards = [...cards]; - const variant = updatedCards[index]; - updatedCards[index] = { - ...variant, - [field]: [...variant[field], newValue], - }; - setCards(updatedCards); - }; - - return { - cards, - addVariant, - updateVariantField, - updateNestedField, - }; -}; - -export default useCardVariantManager; diff --git a/src/context/SECONDARY_CONTEXT/StatisticsContext/StatisticsContext.jsx b/src/context/SECONDARY_CONTEXT/StatisticsContext/StatisticsContext.jsx index c7e1f3c..0bf2a8a 100644 --- a/src/context/SECONDARY_CONTEXT/StatisticsContext/StatisticsContext.jsx +++ b/src/context/SECONDARY_CONTEXT/StatisticsContext/StatisticsContext.jsx @@ -13,18 +13,17 @@ import { getTopCollection, calculateStatsForCollection, } from './helpers'; -import { useChartContext } from '../../MAIN_CONTEXT/ChartContext/ChartContext'; import { useCollectionStore } from '../../index'; import { defaultContextValue } from '../../constants'; import useSelectedCollection from '../../MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +import useTimeRange from '../../../components/forms/selectors/useTimeRange'; const StatisticsContext = createContext(defaultContextValue.STATISTICS_CONTEXT); export const StatisticsProvider = ({ children }) => { const { allXYValues, hasFetchedCollections } = useCollectionStore(); const { selectedCollection, allCollections } = useSelectedCollection(); - const { timeRange } = useChartContext(); - + const { selectedTimeRange } = useTimeRange(); if (!Array.isArray(allCollections)) { return null; } @@ -39,12 +38,12 @@ export const StatisticsProvider = ({ children }) => { ? allCollections?.reduce((acc, collection) => { acc[collection?._id] = calculateStatsForCollection( collection, - timeRange + selectedTimeRange ); return acc; }, {}) : {}, - [allCollections, timeRange] + [allCollections, selectedTimeRange] ); const totalValue = useMemo(() => { @@ -68,22 +67,16 @@ export const StatisticsProvider = ({ children }) => { [allCollections] ); - // if (hasFetchedCollections) { - // console.log('SELECTED', selectedCollection); - // console.log('ALL', allCollections); - // console.log('VALID', validCollections); - // } - // Function to create markers for a given collection const createMarkers = (selectedCollection) => { if (!selectedCollection || !selectedCollection.collectionStatistics) return []; - const { highPoint, lowPoint, avgPrice } = + const { highPoint, lowPoint, avgPrice, percentageChange } = selectedCollection.collectionStatistics; return [ { axis: 'y', - value: highPoint, + value: percentageChange, lineStyle: { stroke: '#b0413e', strokeWidth: 2 }, legend: `${selectedCollection.name} High`, legendOrientation: 'vertical', @@ -105,48 +98,28 @@ export const StatisticsProvider = ({ children }) => { ]; }; - // Example use of createMarkers within useMemo for selectedCollection const markers = useMemo(() => { - // Assuming selectedCollection is obtained from somewhere, e.g., state or context if (!selectedCollection) return []; - // console.log('SELECTED COLLECTION:', selectedCollection); - // const selectedCollection = allCollections.find( - // (collection) => collection._id === someSelectedCollectionId - // ); return createMarkers(selectedCollection); }, [allCollections]); // Add dependencies as necessary, e.g., someSelectedCollectionId - // const chartData = useMemo( - // () => - // validCollections - // ? allCollections.map((collection) => ({ - // id: collection._id, - // value: collection.totalPrice, - // label: collection.name, - // })) - // : [], - // [allCollections] - // ); - const contextValue = useMemo( () => ({ - // PRIMARY DATA stats: - calculateStatistics({ data: null }, timeRange, allCollections) || {}, + calculateStatistics( + { data: null }, + selectedTimeRange, + allCollections + ) || {}, allStats: [statsByCollectionId], statsByCollectionId: statsByCollectionId[selectedCollection?._id], selectedStat, markers, - // PRIMARY FUNCTIONS setSelectedStat, - // SECONDARY DATA totalValue, topFiveCards, - // chartData, - // SECONDARY FUNCTIONS calculateTotalPriceOfAllCollections, - // calculateStatsByCollectionId, calculatePriceChanges, getTopCard, getTopCollection, @@ -157,9 +130,8 @@ export const StatisticsProvider = ({ children }) => { markers, totalValue, topFiveCards, - // chartData, setSelectedStat, - timeRange, + selectedTimeRange, ] ); diff --git a/src/context/UTILITIES_CONTEXT/FormContext/FormContext.jsx b/src/context/UTILITIES_CONTEXT/FormContext/FormContext.jsx index 3a697cc..cd2d204 100644 --- a/src/context/UTILITIES_CONTEXT/FormContext/FormContext.jsx +++ b/src/context/UTILITIES_CONTEXT/FormContext/FormContext.jsx @@ -24,6 +24,7 @@ import useCollectionManager from '../../MAIN_CONTEXT/CollectionContext/useCollec import { useDeckStore } from '../../MAIN_CONTEXT/DeckContext/DeckContext'; import { useChartContext } from '../../MAIN_CONTEXT/ChartContext/ChartContext'; import { useLoading } from '../../hooks/useLoading'; +import useDeckManager from '../../MAIN_CONTEXT/DeckContext/useDeckManager'; const initializeFormState = (schema) => Object.keys(schema).reduce((acc, key) => ({ ...acc, [key]: false }), {}); @@ -33,9 +34,9 @@ export const FormProvider = ({ children }) => { const { signup, login, isLoggedIn, userId } = useAuthContext(); const { handleRequest, setSearchSettings, searchSettings } = useCardStoreHook(); - const { createNewCollection, updateAndSyncCollection, selectedCollection } = + const { createNewCollection, updateCollection, selectedCollection } = useCollectionManager(); - const { updateDeckDetails, deleteUserDeck, createUserDeck } = useDeckStore(); + const { updateDeckDetails, deleteDeck, createNewDeck } = useDeckManager(); const { setTimeRange } = useChartContext(); // ** * * * * * * * * * * * * * * * * * * * |/\ /\ /\| ** * * * * * * * * * * * * * * * * * * * @@ -100,7 +101,6 @@ export const FormProvider = ({ children }) => { prevForm === 'loginForm' ? 'signupForm' : 'loginForm' ); }; - const formHandlers = { signupForm: (formData) => signup( @@ -112,34 +112,46 @@ export const FormProvider = ({ children }) => { ), loginForm: (formData) => login(formData.username, formData.password), updateUserDataForm: (formData) => console.log(formData), + addCollectionForm: (formData, additionalData) => + createNewCollection(formData, additionalData), updateCollectionForm: (formData, additionalData) => - updateAndSyncCollection(additionalData, formData), + updateCollection(additionalData, formData), createCollectionForm: (formData, additionalData) => createNewCollection(additionalData, formData), - updateDeckForm: (formData, additionalData) => updateDeckDetails(formData), - addDeckForm: (formData, additionalData) => console.log(formData), - searchForm: (formData, additionalData) => setSearchSettings(formData), + updateDeckForm: (formData, additionalData) => + updateDeckDetails(formData, additionalData), + addDeckForm: (formData, additionalData) => + createNewDeck(formData, additionalData), + deleteDeckForm: (formData, additionalData) => + deleteDeck(formData, additionalData), + searchForm: (formData, additionalData) => + setSearchSettings(formData, additionalData), + collectionSearchForm: (formData, additionalData) => + console.log(formData, additionalData), timeRangeSelector: (formData, additionalData) => - handleTimeRangeChange(formData), + handleTimeRangeChange(formData, additionalData), searchSettingsSelector: (formData, additionalData) => - setSearchSettings(formData), + setSearchSettings(formData, additionalData), rememberMeForm: (formData) => { // Implement remember me form submission logic here console.log('Remember Me Form Data:', formData); }, authSwitch: (formData) => { console.log('Auth Switch Form Data:', formData); - toggleAuthMode(); }, }; const onSubmit = useCallback( - async (formData) => { + async (formData, additionalData) => { startLoading(currentSchemaKey); const formHandler = formHandlers[currentSchemaKey]; try { - console.log(`Submitting form: ${currentSchemaKey}`, formData); + console.log( + `Submitting form: ${currentSchemaKey}`, + `formdata ${formData}`, + `additionalData ${additionalData}` + ); await formHandler(formData); } catch (error) { console.error(`Error submitting ${currentSchemaKey}:`, error); @@ -159,20 +171,6 @@ export const FormProvider = ({ children }) => { useEffect(() => { setFormSchema(currentSchemaKey); }, [currentSchemaKey, setFormSchema]); - // const setFormSchema = useCallback( - // (formId) => { - // const schema = formSchemas[formId]; - // const defaultValues = getDefaultValuesFromSchema(schema); - // methods.reset({ ...defaultValues }); // Reset form with new default values - // methods.clearErrors(); // Clear any existing errors - // methods.setValue('formId', formId); // Optionally set formId as a form value - // }, - // [methods] - // ); - // useEffect(() => { - // setFormSchema(initialFormKey); // Initialize form with the first schema - // }, [setFormSchema, initialFormKey]); - const onChange = (formData, currentSchemaKey) => { console.log('Form data changed:', formData, currentSchemaKey); const validation = handleZodValidation( @@ -210,27 +208,23 @@ export const FormProvider = ({ children }) => { console.error('handleChange called without a valid event target'); return; } - const { name, value, type, checked } = e.target; - - // Ensure that the event target has a 'name' property if (typeof name === 'undefined') { console.error( 'handleChange called on an element without a "name" attribute' ); return; } - console.log('e.target:', e.target); const fieldValue = type === 'checkbox' ? checked : value; values.current[name] = fieldValue; console.log('Form field changed:', name, fieldValue); - console.log('Search form field changed:', name, fieldValue); + console.log('Currrent values:', name, values.current[name]); if (name === 'searchTerm' && typeof fieldValue === 'string') { console.log('Form data is valid:', fieldValue); handleRequest(fieldValue); // Use handleRequest for the search form } - handleFieldChange(e.target.formId, name, fieldValue); + // handleFieldChange(e.target.formId, name, fieldValue); }, []); const handleFocus = useCallback((e) => { const { name } = e.target; @@ -251,6 +245,23 @@ export const FormProvider = ({ children }) => { methods.reset(getDefaultValuesFromSchema(formSchemas[currentSchemaKey])); }, [currentSchemaKey, methods]); + const setInitialValues = useCallback( + (newInitialValues) => { + if (newInitialValues) { + console.log('Setting initial values:', newInitialValues); + methods.reset(newInitialValues); + } + }, + [methods] + ); + + // Ensure initial values are set when they change + // useEffect(() => { + // if (initialValues ) { + // setInitialValues(initialValues); + // } + // }, [initialValues, setInitialValues]); + const contextValue = useMemo( () => ({ ...methods, @@ -262,7 +273,8 @@ export const FormProvider = ({ children }) => { isFormDataLoading: isFormDataLoading, currentSchemaKey, currentForm: currentSchemaKey, - + forms, + getValues: methods.getValues, handleTimeRangeChange, handleSearchTermChange, handleFieldChange, @@ -271,6 +283,7 @@ export const FormProvider = ({ children }) => { handleBlur, setFormSchema, setCurrentForm: setFormSchema, + setInitialValues, onSubmit: methods.handleSubmit(onSubmit), onChange, // onChange: methods.handleChange(onChange), @@ -299,6 +312,8 @@ export const FormProvider = ({ children }) => { onSubmit, onChange, handleSetAllForms, + setInitialValues, + currentSchemaKey, ] ); diff --git a/src/context/UTILITIES_CONTEXT/FormContext/schemas.jsx b/src/context/UTILITIES_CONTEXT/FormContext/schemas.jsx index 523500e..c7d0486 100644 --- a/src/context/UTILITIES_CONTEXT/FormContext/schemas.jsx +++ b/src/context/UTILITIES_CONTEXT/FormContext/schemas.jsx @@ -24,10 +24,13 @@ const descriptionConstraint = z .string() .min(1, { message: 'Description is required' }); const tagConstraint = z.array(z.string()); -const colorConstraint = z.enum(['red', 'blue', 'green', 'yellow'], { - message: 'Invalid color', -}); +const colorConstraint = z.enum( + ['red', 'orange', 'yellow', 'green', 'teal', 'blue', 'purple', 'pink'], + { + message: 'Invalid color', + } +); const signupForm = z.object({ firstName: GenericStringConstraint, lastName: GenericStringConstraint, @@ -45,7 +48,6 @@ const rememberMeFormSchema = z.object({ const authSwitchSchema = z.object({ signupMode: z.boolean().default(false), }); -// Update User Data Form Schema const updateUserDataForm = z.object({ firstName: nameConstraint, lastName: nameConstraint, @@ -61,23 +63,21 @@ const updateUserDataForm = z.object({ status: z.string().min(1, { message: 'Status is required' }), signupMode: z.boolean(), }); -// Collection Form Schemas const addCollectionForm = z.object({ name: nameConstraint, description: descriptionConstraint, }); - const updateCollectionForm = addCollectionForm; -// Deck Form Schemas const addDeckForm = z.object({ name: nameConstraint, description: descriptionConstraint, +}); +const updateDeckForm = z.object({ + name: nameConstraint, + description: descriptionConstraint, tags: tagConstraint, color: colorConstraint, }); - -const updateDeckForm = addDeckForm; -// Search Form Schema const searchForm = z.object({ searchTerm: z.string().min(1, { message: 'Search term is required' }), searchParams: z.object({ @@ -89,7 +89,9 @@ const searchForm = z.object({ level: z.string().optional(), }), }); -// Customer Info Fields Schema +const collectionSearchForm = z.object({ + searchTerm: z.string().min(1, { message: 'Search term is required' }), +}); const customerInfoForm = z.object({ firstName: nameConstraint, lastName: nameConstraint, @@ -105,46 +107,11 @@ const customerInfoForm = z.object({ phone: z.string().min(1, { message: 'Phone is required' }), email: z.string().email({ message: 'Invalid email address' }), }); -// const TimeRangeSchema = z.object({ -// timeRange: z.object({ -// id: z.string( -// 'Invalid time range ID. Please select a valid time range from the list.' -// ), -// value: z.string( -// 'Invalid time range value. Please select a valid time range from the list.' -// ), -// name: z.string( -// 'Invalid time range name. Please select a valid time range from the list.' -// ), -// }), -// }); - -// const timeRangeSchema = z.object({ -// timeRange: z.enum(['24hr', '7d', '30d', '90d', '180d', '270d', '365d']), -// }); -const timeRangeOptionSchema = z.object({ - id: z.string( - 'Invalid time range ID. Please select a valid time range from the list.' - ), - value: z.string(), - name: z.string(), - data: z.array( - z.object({ - x: z.number(), - y: z.number(), - }) - ), -}); - -// const timeRangeSchema = z.object({ -// timeRange: timeRangeOptionSchema, -// }); const timeRangeSelectorSchema = z.object({ timeRange: z.string().nonempty({ message: 'You must select a time range.', }), }); -// Define a schema for a single filter's values const filterValueSchema = z.enum([ 'Unset', '1', @@ -187,7 +154,6 @@ const dataSchema = z.object({ initialState: initialStateSchema, filters: z.array(filterSchema), }); -// Usage example: const data = { initialState: { name: '', @@ -243,18 +209,12 @@ const data = { ], }; -// const parsedData = dataSchema.safeParse(data); -// if (parsedData.success) { -// console.log('Validation successful', parsedData.data); -// } else { -// console.error('Validation failed', parsedData.error); -// } export const formSchemas = { signupForm, loginForm, rememberMeForm: rememberMeFormSchema, authSwitch: authSwitchSchema, - + collectionSearchForm, updateUserDataForm, addCollectionForm, updateCollectionForm, @@ -327,16 +287,15 @@ export const defaultValues = { addDeckForm: { name: '', description: '', - tags: [], - // eslint-disable-next-line quotes - color: `red`, + // tags: [], + // // eslint-disable-next-line quotes + // color: `red`, }, updateDeckForm: { name: '', description: '', - tags: [], - // eslint-disable-next-line quotes - color: `red`, + tags: ['default'], + color: 'red', }, searchForm: { searchTerm: '', @@ -349,6 +308,9 @@ export const defaultValues = { level: '', }, }, + collectionSearchForm: { + searchTerm: '', + }, customerInfoForm: { firstName: '', lastName: '', diff --git a/src/context/UTILITIES_CONTEXT/PageContext/PageContext.jsx b/src/context/UTILITIES_CONTEXT/PageContext/PageContext.jsx deleted file mode 100644 index ea2af24..0000000 --- a/src/context/UTILITIES_CONTEXT/PageContext/PageContext.jsx +++ /dev/null @@ -1,152 +0,0 @@ -import React, { - createContext, - useState, - useContext, - useEffect, - useMemo, -} from 'react'; -import LoadingIndicator from '../../../components/reusable/indicators/LoadingIndicator'; -import ErrorIndicator from '../../../components/reusable/indicators/ErrorIndicator'; -import SplashPage2 from '../../../pages/SplashPage2'; -import useSnackBar from '../../hooks/useSnackBar'; -import { defaultContextValue } from '../../constants'; -import { DynamicSnackbar } from '../../../layout/REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; -import { useLoading } from '../../hooks/useLoading'; - -const PageContext = createContext(defaultContextValue.PAGE_CONTEXT); - -export const PageProvider = ({ children }) => { - const { - isLoading, - isAnyLoading, - startLoading, - stopLoading, - setError, - error, - clearLoading, - } = useLoading(); - const { snackbar, handleSnackBar, handleCloseSnackbar } = useSnackBar(); - - const handleLoadingCompletion = () => { - if (!isAnyLoading()) { - handleSnackBar('Loading completed', 'success'); - } - }; - const returnDisplay = () => { - if (error) { - return ; - } - if (isLoading('isPageLoading')) { - return ; - } else if (isAnyLoading()) { - return ; - } - return null; - }; - const contextValue = useMemo( - () => ({ - startLoading, - stopLoading, - setError, - error, - isLoading, - clearLoading, - returnDisplay, - // You can expose any additional functionalities from useLoading as needed. - }), - [ - startLoading, - stopLoading, - setError, - error, - isLoading, - clearLoading, - returnDisplay, - ] - ); - - // const { snackbar, handleSnackBar, handleCloseSnackbar } = useSnackBar(); - // // const [activelyLoading, setActivelyLoading] = useState(false); // [false, setActivelyLoading - // const [loadingStatus, setLoadingStatus] = useState({ - // isLoading: false, - // isDataLoading: false, - // isFormDataLoading: false, - // isPageLoading: false, - // loadingTimeoutExpired: false, - // error: null, - // loadingType: '', - // }); - // useEffect(() => { - // const isCompleted = Object.values(loadingStatus).every((status) => !status); - // if (isCompleted) { - // handleSnackBar( - // `Loading ${loadingStatus.loadingType} completed`, - // 'success' - // ); - // } - // }, [loadingStatus, handleSnackBar]); - // const setLoading = (type, status) => { - // setLoadingStatus((prevStatus) => ({ - // ...prevStatus, - // [type]: status, - // loadingType: status ? type : '', - // })); - // }; - // const returnDisplay = () => { - // if (loadingStatus.error) { - // return ; - // } - // switch (loadingStatus.loadingType) { - // case 'isLoading': - // case 'isDataLoading': - // case 'isFormDataLoading': - // return ; - // case 'isPageLoading': - // return ; - // default: - // return null; - // } - // }; - - // const contextValue = useMemo( - // () => ({ - // loadingStatus, - // error: loadingStatus.error, - // setActivelyLoading: (status) => setLoading('isLoading', status), - // returnDisplay, - // setError: (error) => setLoadingStatus((prev) => ({ ...prev, error })), - // setPageError: (error) => - // setLoadingStatus((prev) => ({ ...prev, error, isPageLoading: false })), - // setIsLoading: (status) => setLoading('isLoading', status), - // setIsDataLoading: (status) => setLoading('isDataLoading', status), - // setIsFormDataLoading: (status) => setLoading('isFormDataLoading', status), - // setIsPageLoading: (status) => setLoading('isPageLoading', status), - // setLoadingTimeoutExpired: (status) => - // setLoadingStatus((prev) => ({ - // ...prev, - // loadingTimeoutExpired: status, - // })), - // handleLoadingTimeout: () => - // setLoadingStatus((prev) => ({ - // ...prev, - // loadingTimeoutExpired: true, - // isPageLoading: false, - // })), - // }), - // [loadingStatus] - // ); - - return ( - - {children}{' '} - {/* */} - - ); -}; - -export const usePageContext = () => useContext(PageContext); diff --git a/src/context/UTILITIES_CONTEXT/SideBarContext/SideBarProvider.jsx b/src/context/UTILITIES_CONTEXT/SideBarContext/SideBarProvider.jsx index a074ec5..26b91ba 100644 --- a/src/context/UTILITIES_CONTEXT/SideBarContext/SideBarProvider.jsx +++ b/src/context/UTILITIES_CONTEXT/SideBarContext/SideBarProvider.jsx @@ -5,7 +5,7 @@ import CollectionsIcon from '@mui/icons-material/Collections'; import StoreIcon from '@mui/icons-material/Store'; import ShoppingBasketIcon from '@mui/icons-material/ShoppingBasket'; import PersonIcon from '@mui/icons-material/Person'; -import DeckBuilderIcon from '../../../components/reusable/icons/DeckBuilderIcon'; +import DeckBuilderIcon from '../../../layout/REUSABLE_COMPONENTS/icons/DeckBuilderIcon'; const SidebarContext = createContext(); export const SidebarProvider = ({ children }) => { diff --git a/src/context/UTILITIES_CONTEXT/SocketContext/SocketProvider.jsx b/src/context/UTILITIES_CONTEXT/SocketContext/SocketProvider.jsx deleted file mode 100644 index f3d0953..0000000 --- a/src/context/UTILITIES_CONTEXT/SocketContext/SocketProvider.jsx +++ /dev/null @@ -1,40 +0,0 @@ -import React, { - createContext, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; -import io from 'socket.io-client'; - -const SocketContext = createContext(); - -export const SocketProvider = ({ children }) => { - const [socket, setSocket] = useState(null); - - useEffect(() => { - const socketInstance = io( - process.env.REACT_APP_SERVER || 'ws://localhost:3001', - { - transports: ['websocket'], - } - ); - setSocket(socketInstance); - - return () => { - socketInstance.disconnect(); - }; - }, []); - - return ( - {children} - ); -}; - -export const useSocketContext = () => { - const context = useContext(SocketContext); - if (context === undefined) { - throw new Error('useSocketContext must be used within a SocketProvider'); - } - return context; -}; diff --git a/src/context/UTILITIES_CONTEXT/VisibilityContext.jsx b/src/context/UTILITIES_CONTEXT/VisibilityContext.jsx index 083acc1..abd40b2 100644 --- a/src/context/UTILITIES_CONTEXT/VisibilityContext.jsx +++ b/src/context/UTILITIES_CONTEXT/VisibilityContext.jsx @@ -1,38 +1,38 @@ -// VisibilityContext.js -import React, { createContext, useContext, useState } from 'react'; +// // VisibilityContext.js +// import React, { createContext, useContext, useState } from 'react'; -const VisibilityContext = createContext(); +// const VisibilityContext = createContext(); -export const useVisibilityContext = () => useContext(VisibilityContext); +// export const useVisibilityContext = () => useContext(VisibilityContext); -export const VisibilityProvider = ({ children }) => { - const [isCollectionVisible, setCollectionVisibility] = useState(true); - const [dialogStates, setDialogStates] = useState({ - isAddCollectionDialogOpen: false, - isSelectionErrorDialogOpen: false, - }); +// export const VisibilityProvider = ({ children }) => { +// const [isCollectionVisible, setCollectionVisibility] = useState(false); +// const [dialogStates, setDialogStates] = useState({ +// isAddCollectionDialogOpen: false, +// isSelectionErrorDialogOpen: false, +// }); - const toggleCollectionVisibility = () => { - setCollectionVisibility(!isCollectionVisible); - }; +// const toggleCollectionVisibility = () => { +// setCollectionVisibility(!isCollectionVisible); +// }; - const toggleDialog = (dialogName) => { - setDialogStates((prevState) => ({ - ...prevState, - [dialogName]: !prevState[dialogName], - })); - }; +// const toggleDialog = (dialogName) => { +// setDialogStates((prevState) => ({ +// ...prevState, +// [dialogName]: !prevState[dialogName], +// })); +// }; - return ( - - {children} - - ); -}; +// return ( +// +// {children} +// +// ); +// }; diff --git a/src/context/constants.jsx b/src/context/constants.jsx index d2cbba3..3a99761 100644 --- a/src/context/constants.jsx +++ b/src/context/constants.jsx @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-empty-function */ import { createNewPriceEntry } from './Helpers'; -import jsonData from './MAIN_CONTEXT/CollectionContext/nivoTestData.json'; +import jsonData from '../data/nivoTestData.json'; const { nivoTestData } = jsonData; import user from './user'; @@ -327,6 +327,20 @@ const defaultDeck = { tags: [''], color: '', cards: [defaultCard], + addDefaultCard: function () { + const newCard = createDefaultCard(); + this.cards.push(newCard); + this.updateTotalPrice(); + }, + updateTotalPrice: function () { + this.newTotalPrice = this.cards.reduce( + (acc, card) => acc + card.totalPrice, + 0 + ); + }, + addMultipleDefaultCards: function (numberOfCards) { + Array.from({ length: numberOfCards }).forEach(() => this.addDefaultCard()); + }, }; // ! DEFAULT VALUES FOR CART OBJECTS const defaultCart = { @@ -357,6 +371,7 @@ export const DEFAULT_ALLDECKS_ARRAY = [defaultDeck]; export const DEFAULT_CART = defaultCart; export const DEFAULT_USER = defaultUser; export const SELECTED_COLLECTION_ID = 'selectedCollectionId'; +export const SELECTED_DECK_ID = 'selectedDeckId'; export const DEFAULT_CARDS_COUNT = 5; // FUNCTIONS export const createNewCardObject = () => { diff --git a/src/context/hooks/index.jsx b/src/context/hooks/index.jsx index 8eeba11..db26ec4 100644 --- a/src/context/hooks/index.jsx +++ b/src/context/hooks/index.jsx @@ -145,7 +145,7 @@ const [isToggled, toggle] = useToggle(); * Description: Manages snack bar notifications in the application. * Useful for showing temporary messages or alerts to the user. */ -const { openSnackBar, closeSnackBar } = useSnackBar(); +// const { openSnackBar, closeSnackBar } = useSnackBar(); /** * Hook: useVisible diff --git a/src/context/hooks/style-hooks/usePortfolioStyles.jsx b/src/context/hooks/style-hooks/usePortfolioStyles.jsx index 998f911..9e48060 100644 --- a/src/context/hooks/style-hooks/usePortfolioStyles.jsx +++ b/src/context/hooks/style-hooks/usePortfolioStyles.jsx @@ -1,274 +1,223 @@ -import { - Box, - Button, - ButtonGroup, - CardMedia, - Container, - Grid, - Paper, - Popper, - Table, - TableBody, - TableCell, - TableFooter, - TableHead, - TableRow, - Typography, - TablePagination, -} from '@mui/material'; -// import TablePaginationActions from '@mui/material/TablePagination/TablePaginationActions'; +// import { +// Box, +// Button, +// ButtonGroup, +// CardMedia, +// Container, +// Grid, +// Paper, +// Popper, +// Table, +// TableBody, +// TableCell, +// TableFooter, +// TableHead, +// TableRow, +// Typography, +// TablePagination, +// } from '@mui/material'; +// // import TablePaginationActions from '@mui/material/TablePagination/TablePaginationActions'; +// import styled from 'styled-components'; + +import { CardMedia, Popper } from '@mui/material'; import styled from 'styled-components'; +export const MediaContainer = styled('div')({ + cursor: 'pointer', + position: 'relative', +}); -export const StatisticPaper = styled(Paper)(({ theme }) => ({ - padding: theme.spacing(2), - margin: 'auto', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - flexGrow: 1, - justifyContent: 'center', - minHeight: '100%', - height: '100%', +export const MediaPopover = styled(Popper)({ + pointerEvents: 'none', + height: 'auto', + width: 'auto', + maxWidth: '300px', + maxHeight: 'auto', +}); + +export const Media = styled(CardMedia)(({ theme }) => ({ width: '100%', - boxShadow: theme.shadows[3], + height: 'auto', + flexGrow: 1, + alignItems: 'flex-end', + padding: theme.spacing(0.5), })); - -// export const PortfolioBox = styled(Box)(({ theme }) => ({ +// export const StatisticPaper = styled(Paper)(({ theme }) => ({ +// padding: theme.spacing(2), +// margin: 'auto', // display: 'flex', // flexDirection: 'column', -// justifyContent: 'center', // alignItems: 'center', -// maxWidth: '100vw', -// width: '100%', +// flexGrow: 1, +// justifyContent: 'center', +// minHeight: '100%', // height: '100%', -// margin: theme.spacing(0, 'auto'), -// padding: theme.spacing(1, 2, 3), -// backgroundColor: theme.palette.backgroundA.lightest, -// color: theme.palette.text.primary, +// width: '100%', +// boxShadow: theme.shadows[3], // })); -// export const PortfolioBoxA = styled(Box)(({ theme }) => ({ +// // export const PortfolioBox = styled(Box)(({ theme }) => ({ +// // display: 'flex', +// // flexDirection: 'column', +// // justifyContent: 'center', +// // alignItems: 'center', +// // maxWidth: '100vw', +// // width: '100%', +// // height: '100%', +// // margin: theme.spacing(0, 'auto'), +// // padding: theme.spacing(1, 2, 3), +// // backgroundColor: theme.palette.backgroundA.lightest, +// // color: theme.palette.text.primary, +// // })); + +// // export const PortfolioBoxA = styled(Box)(({ theme }) => ({ +// // flexDirection: 'column', +// // flexGrow: 1, +// // height: 'auto', +// // width: '100%', +// // minHeight: '100vh', // Reducing height by 128px +// // boxShadow: theme.shadows[5], +// // [theme.breakpoints.down('sm')]: { +// // padding: theme.spacing(2), +// // }, +// // })); + +// // export const PortfolioBoxB = styled(Box)(({ theme }) => ({ +// // display: 'flex', +// // flexDirection: 'column', +// // gap: theme.spacing(4), +// // borderRadius: theme.shape.borderRadius, +// // flexGrow: 1, +// // background: theme.palette.backgroundD.dark, +// // padding: theme.spacing(4), +// // width: '100%', +// // height: '100%', +// // boxShadow: theme.shadows[5], +// // [theme.breakpoints.down('sm')]: { +// // padding: theme.spacing(2), +// // }, +// // })); + +// // export const StatisticHeader = styled('div')(({ theme }) => ({ +// // marginBottom: theme.spacing(1), +// // fontWeight: 'bold', +// // color: theme.palette.backgroundA.lighter, +// // })); +// export const StatisticHeader = styled(Typography)(({ theme }) => ({ +// fontWeight: 'bold', +// color: theme.palette.backgroundA.dark, +// marginBottom: theme.spacing(1), +// })); +// export const StatisticsContent = styled('div')(({ theme }) => ({ +// display: 'flex', // flexDirection: 'column', +// alignItems: 'center', +// justifyContent: 'center', // flexGrow: 1, -// height: 'auto', +// height: '100%', // width: '100%', -// minHeight: '100vh', // Reducing height by 128px -// boxShadow: theme.shadows[5], -// [theme.breakpoints.down('sm')]: { -// padding: theme.spacing(2), -// }, // })); -// export const PortfolioBoxB = styled(Box)(({ theme }) => ({ +// export const PortfolioPaper = styled(Paper)(({ theme }) => ({ +// background: theme.palette.backgroundD.darkest, +// color: theme.palette.text.primary, // display: 'flex', +// justifyContent: 'center', // flexDirection: 'column', -// gap: theme.spacing(4), -// borderRadius: theme.shape.borderRadius, -// flexGrow: 1, -// background: theme.palette.backgroundD.dark, -// padding: theme.spacing(4), +// margin: 'auto', // width: '100%', -// height: '100%', -// boxShadow: theme.shadows[5], -// [theme.breakpoints.down('sm')]: { -// padding: theme.spacing(2), -// }, +// padding: theme.spacing(2), +// borderRadius: theme.shape.borderRadius, // })); -// export const StatisticHeader = styled('div')(({ theme }) => ({ -// marginBottom: theme.spacing(1), -// fontWeight: 'bold', -// color: theme.palette.backgroundA.lighter, +// export const ComponentGridContainer = styled('div')(({ theme }) => ({ +// width: '100%', +// alignItems: 'center', +// justifyContent: 'space-between', +// padding: theme.spacing(2), +// backgroundColor: theme.palette.backgroundB.lighter, +// borderRadius: theme.shape.borderRadius, +// border: '3px solid', +// borderColor: theme.palette.backgroundB.lightest, // })); -export const StatisticHeader = styled(Typography)(({ theme }) => ({ - fontWeight: 'bold', - color: theme.palette.backgroundA.dark, - marginBottom: theme.spacing(1), -})); -export const StatisticsContent = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - flexGrow: 1, - height: '100%', - width: '100%', -})); - -export const PortfolioPaper = styled(Paper)(({ theme }) => ({ - background: theme.palette.backgroundD.darkest, - color: theme.palette.text.primary, - display: 'flex', - justifyContent: 'center', - flexDirection: 'column', - margin: 'auto', - width: '100%', - padding: theme.spacing(2), - borderRadius: theme.shape.borderRadius, -})); - -export const ComponentGridContainer = styled('div')(({ theme }) => ({ - width: '100%', - alignItems: 'center', - justifyContent: 'space-between', - padding: theme.spacing(2), - backgroundColor: theme.palette.backgroundB.lighter, - borderRadius: theme.shape.borderRadius, - border: '3px solid', - borderColor: theme.palette.backgroundB.lightest, -})); - -export const ComponentGridItem = styled(Grid)(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-end', - background: theme.palette.backgroundA.main, - borderRadius: theme.shape.borderRadius, -})); - -export const DialogButton = styled(Button)({ - textTransform: 'none', -}); -export const SelectCollectionListContainer = styled(Container)(({ theme }) => ({ - // maxHeight: '60vh', - overflowY: 'auto', - padding: theme.spacing(2), - height: '100%', - width: '100%', -})); - -export const DialogContent = styled('div')(({ theme }) => ({ - padding: theme.spacing(2), - background: theme.palette.backgroundA.lightest, -})); - -export const BoxStyle = styled(Box)(({ theme }) => ({ - padding: theme.spacing(2), - justifyContent: 'center', - margin: 'auto', -})); +// export const ComponentGridItem = styled(Grid)(({ theme }) => ({ +// display: 'flex', +// justifyContent: 'flex-end', +// background: theme.palette.backgroundA.main, +// borderRadius: theme.shape.borderRadius, +// })); -export const CardStyle = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - margin: 'auto', - padding: theme.spacing(2), - maxWidth: '100%', - maxHeight: '100%', -})); +// export const DialogButton = styled(Button)({ +// textTransform: 'none', +// }); -export const CardContentStyle = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - margin: 'auto', - padding: theme.spacing(2), - maxWidth: '100%', - maxHeight: '100%', -})); +// export const SelectCollectionListContainer = styled(Container)(({ theme }) => ({ +// // maxHeight: '60vh', +// overflowY: 'auto', +// padding: theme.spacing(2), +// height: '100%', +// width: '100%', +// })); -export const CardMediaStyle = styled('div')(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - margin: 'auto', - padding: theme.spacing(2), - maxWidth: '100%', - maxHeight: '100%', -})); +// export const DialogContent = styled('div')(({ theme }) => ({ +// padding: theme.spacing(2), +// background: theme.palette.backgroundA.lightest, +// })); -export const MediaContainer = styled('div')({ - cursor: 'pointer', - position: 'relative', -}); +// export const BoxStyle = styled(Box)(({ theme }) => ({ +// padding: theme.spacing(2), +// justifyContent: 'center', +// margin: 'auto', +// })); -export const MediaPopover = styled(Popper)({ - pointerEvents: 'none', - height: 'auto', - width: 'auto', - maxWidth: '300px', - maxHeight: 'auto', -}); +// export const CardStyle = styled('div')(({ theme }) => ({ +// display: 'flex', +// flexDirection: 'column', +// justifyContent: 'center', +// margin: 'auto', +// padding: theme.spacing(2), +// maxWidth: '100%', +// maxHeight: '100%', +// })); -export const Media = styled(CardMedia)(({ theme }) => ({ - width: '100%', - height: 'auto', - flexGrow: 1, - alignItems: 'flex-end', - padding: theme.spacing(0.5), -})); +// export const CardContentStyle = styled('div')(({ theme }) => ({ +// display: 'flex', +// flexDirection: 'column', +// justifyContent: 'center', +// margin: 'auto', +// padding: theme.spacing(2), +// maxWidth: '100%', +// maxHeight: '100%', +// })); -export const CardListContainerBox = styled(Box)(({ theme }) => ({ - maxWidth: '100%', - background: theme.palette.backgroundB.dark, - borderRadius: theme.shape.borderRadius, - width: { - xs: '100%', // Full width on mobile screens - md: '100%', - }, - padding: theme.spacing(4), -})); +// export const CardMediaStyle = styled('div')(({ theme }) => ({ +// display: 'flex', +// flexDirection: 'column', +// justifyContent: 'center', +// margin: 'auto', +// padding: theme.spacing(2), +// maxWidth: '100%', +// maxHeight: '100%', +// })); -export const CardListContainerGrid = styled(Grid)(({ theme }) => ({ - // background: theme.palette.backgroundB.default, - maxHeight: '100%', - width: '100%', -})); +// export const CardListContainerBox = styled(Box)(({ theme }) => ({ +// maxWidth: '100%', +// background: theme.palette.backgroundB.dark, +// borderRadius: theme.shape.borderRadius, +// width: { +// xs: '100%', // Full width on mobile screens +// md: '100%', +// }, +// padding: theme.spacing(4), +// })); -export const TablePaper = styled(Paper)(({ theme }) => ({ - maxWidth: 'lg', - margin: 'auto', - overflowX: 'auto', // Ensures table doesn't overflow the paper - background: theme.palette.backgroundB.default, - padding: theme.spacing(2), - [theme.breakpoints.up('xs')]: { - padding: theme.spacing(1), // Smaller padding for xs - }, - [theme.breakpoints.up('sm')]: { - padding: theme.spacing(2), // Medium padding for sm - }, - [theme.breakpoints.up('md')]: { - padding: theme.spacing(2), // Larger padding for md - }, - [theme.breakpoints.up('lg')]: { - padding: theme.spacing(2), // Extra larger padding for lg - }, - [theme.breakpoints.up('xl')]: { - padding: theme.spacing(2), // Maximum padding for xl - }, -})); +// export const CardListContainerGrid = styled(Grid)(({ theme }) => ({ +// // background: theme.palette.backgroundB.default, +// maxHeight: '100%', +// width: '100%', +// })); -export const PortfolioTableContainer = styled(Container)(({ theme }) => ({ - // maxHeight: '60vh', - maxWidth: 'lg', - overflowY: 'auto', // Allows table to scroll vertically - padding: theme.spacing(2), - margin: theme.spacing(1), - background: theme.palette.backgroundB.light, - borderRadius: theme.shape.borderRadius, -})); -export const PortfolioButtonGroup = styled(ButtonGroup)(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)', -})); -export const PortfolioButton = styled(Button)(({ theme }) => ({ - padding: '10px 20px', - border: 'none', - borderRadius: '5px', - cursor: 'pointer', - width: '100%', - color: theme.palette.getContrastText(theme.palette.backgroundA.dark), - backgroundColor: theme.palette.backgroundA.dark, - '&:hover': { - backgroundColor: theme.palette.backgroundA.darker, - }, -})); -// // ! PORTFOLIO TABLE STYLES -// export const PortfolioTablePaper = styled(Paper)(({ theme }) => ({ +// export const TablePaper = styled(Paper)(({ theme }) => ({ // maxWidth: 'lg', // margin: 'auto', // overflowX: 'auto', // Ensures table doesn't overflow the paper @@ -290,478 +239,530 @@ export const PortfolioButton = styled(Button)(({ theme }) => ({ // padding: theme.spacing(2), // Maximum padding for xl // }, // })); -// export const PortfolioTable = styled(Table)(({ theme }) => ({ -// minWidth: 300, -// minHeight: 300, -// width: '100%', -// height: '100%', -// background: theme.palette.backgroundA.lightest, -// })); -// // TABLE HEADER -// export const PortfolioTableHeader = styled(TableHead)(({ theme }) => ({ -// background: theme.palette.backgroundA.lighter, -// color: theme.palette.backgroundA.contrastTextA, -// [theme.breakpoints.up('md')]: { -// fontSize: '1rem', -// }, -// })); -// // TABLE BODY -// export const PortfolioTableBody = styled(TableBody)(({ theme }) => ({ -// background: theme.palette.backgroundA.lightest, -// border: '2px solid', -// borderColor: theme.palette.divider, -// })); -// export const PortfolioTableRow = styled(TableRow)(({ theme }) => ({ -// // '&:nth-of-type(odd)': { -// // background: theme.palette.backgroundA.light, -// // }, -// // '&:nth-of-type(even)': { -// // background: theme.palette.backgroundA.lighter, -// // }, + +// export const PortfolioTableContainer = styled(Container)(({ theme }) => ({ +// // maxHeight: '60vh', +// maxWidth: 'lg', +// overflowY: 'auto', // Allows table to scroll vertically +// padding: theme.spacing(2), +// margin: theme.spacing(1), +// background: theme.palette.backgroundB.light, +// borderRadius: theme.shape.borderRadius, // })); -// export const PortfolioTableCell = styled(TableCell)(({ theme }) => ({ -// border: '2px solid', -// borderColor: theme.palette.divider, -// // background: theme.palette.backgroundA.light, - -// [theme.breakpoints.down('sm')]: { -// padding: theme.spacing(1), -// fontSize: '0.7rem', -// }, -// [theme.breakpoints.up('md')]: { -// padding: theme.spacing(2), -// fontSize: '1rem', -// }, +// export const PortfolioButtonGroup = styled(ButtonGroup)(({ theme }) => ({ +// display: 'flex', +// flexDirection: 'column', +// justifyContent: 'center', +// alignItems: 'center', +// boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)', // })); -// // TABLE FOOTER -// export const PortfolioTableFooter = styled(TableFooter)(({ theme }) => ({ -// backgroundColor: theme.palette.backgroundA.lighter, -// borderTop: `1px solid ${theme.palette.divider}`, -// '& .MuiTableCell-root': { -// padding: theme.spacing(2), +// export const PortfolioButton = styled(Button)(({ theme }) => ({ +// padding: '10px 20px', +// border: 'none', +// borderRadius: '5px', +// cursor: 'pointer', +// width: '100%', +// color: theme.palette.getContrastText(theme.palette.backgroundA.dark), +// backgroundColor: theme.palette.backgroundA.dark, +// '&:hover': { +// backgroundColor: theme.palette.backgroundA.darker, // }, // })); -// export const PortfolioPaginationActionsTableRow = styled(TableRow)( -// ({ theme }) => ({ -// // Example styling, you can customize as needed -// backgroundColor: theme.palette.backgroundA.lighter, -// '&:hover': { -// backgroundColor: theme.palette.action.hover, -// }, -// '& .MuiTableCell-root': { -// textAlign: 'right', -// padding: theme.spacing(1), -// }, -// }) -// ); -// export const PortfolioPaginationActionsTableCell = styled(TableCell)( -// ({ theme }) => ({ -// // Example styling, you can customize as needed -// fontSize: '0.9rem', -// fontWeight: 'bold', -// color: theme.palette.backgroundA.contrastTextA, -// backgroundColor: theme.palette.backgroundA.lighter, - -// '&:hover': { -// color: theme.palette.backgroundA.contrastTextA, -// }, -// '& .MuiTableCell-root': { -// textAlign: 'right', -// padding: theme.spacing(1), -// }, -// }) -// ); -// export const PortfolioPaginationActionsTableContentsContainer = styled(Box)( -// ({ theme }) => ({ -// // '.MuiIconButton-root': { -// // padding: theme.spacing(1), -// // margin: '0 4px', -// // '&:hover': { -// // backgroundColor: theme.palette.action.hover, -// // }, -// // }, -// display: 'flex', -// flexShrink: 0, -// justifyContent: 'flex-end', -// // ml: 2.5, -// width: '100%', -// flexDirection: 'row', -// borderRadius: '5px', -// boxShadow: '0 2px 4px rgba(0,0,0,0.1)', -// '&:hover': { -// boxShadow: '0 4px 8px rgba(0,0,0,0.2)', -// }, -// }) -// ); -// // TABLE PRICE BOX -// export const PortfolioTablePriceBox = styled(Box)(({ theme }) => ({ +// // // ! PORTFOLIO TABLE STYLES +// // export const PortfolioTablePaper = styled(Paper)(({ theme }) => ({ +// // maxWidth: 'lg', +// // margin: 'auto', +// // overflowX: 'auto', // Ensures table doesn't overflow the paper +// // background: theme.palette.backgroundB.default, +// // padding: theme.spacing(2), +// // [theme.breakpoints.up('xs')]: { +// // padding: theme.spacing(1), // Smaller padding for xs +// // }, +// // [theme.breakpoints.up('sm')]: { +// // padding: theme.spacing(2), // Medium padding for sm +// // }, +// // [theme.breakpoints.up('md')]: { +// // padding: theme.spacing(2), // Larger padding for md +// // }, +// // [theme.breakpoints.up('lg')]: { +// // padding: theme.spacing(2), // Extra larger padding for lg +// // }, +// // [theme.breakpoints.up('xl')]: { +// // padding: theme.spacing(2), // Maximum padding for xl +// // }, +// // })); +// // export const PortfolioTable = styled(Table)(({ theme }) => ({ +// // minWidth: 300, +// // minHeight: 300, +// // width: '100%', +// // height: '100%', +// // background: theme.palette.backgroundA.lightest, +// // })); +// // // TABLE HEADER +// // export const PortfolioTableHeader = styled(TableHead)(({ theme }) => ({ +// // background: theme.palette.backgroundA.lighter, +// // color: theme.palette.backgroundA.contrastTextA, +// // [theme.breakpoints.up('md')]: { +// // fontSize: '1rem', +// // }, +// // })); +// // // TABLE BODY +// // export const PortfolioTableBody = styled(TableBody)(({ theme }) => ({ +// // background: theme.palette.backgroundA.lightest, +// // border: '2px solid', +// // borderColor: theme.palette.divider, +// // })); +// // export const PortfolioTableRow = styled(TableRow)(({ theme }) => ({ +// // // '&:nth-of-type(odd)': { +// // // background: theme.palette.backgroundA.light, +// // // }, +// // // '&:nth-of-type(even)': { +// // // background: theme.palette.backgroundA.lighter, +// // // }, +// // })); +// // export const PortfolioTableCell = styled(TableCell)(({ theme }) => ({ +// // border: '2px solid', +// // borderColor: theme.palette.divider, +// // // background: theme.palette.backgroundA.light, + +// // [theme.breakpoints.down('sm')]: { +// // padding: theme.spacing(1), +// // fontSize: '0.7rem', +// // }, +// // [theme.breakpoints.up('md')]: { +// // padding: theme.spacing(2), +// // fontSize: '1rem', +// // }, +// // })); +// // // TABLE FOOTER +// // export const PortfolioTableFooter = styled(TableFooter)(({ theme }) => ({ +// // backgroundColor: theme.palette.backgroundA.lighter, +// // borderTop: `1px solid ${theme.palette.divider}`, +// // '& .MuiTableCell-root': { +// // padding: theme.spacing(2), +// // }, +// // })); +// // export const PortfolioPaginationActionsTableRow = styled(TableRow)( +// // ({ theme }) => ({ +// // // Example styling, you can customize as needed +// // backgroundColor: theme.palette.backgroundA.lighter, +// // '&:hover': { +// // backgroundColor: theme.palette.action.hover, +// // }, +// // '& .MuiTableCell-root': { +// // textAlign: 'right', +// // padding: theme.spacing(1), +// // }, +// // }) +// // ); +// // export const PortfolioPaginationActionsTableCell = styled(TableCell)( +// // ({ theme }) => ({ +// // // Example styling, you can customize as needed +// // fontSize: '0.9rem', +// // fontWeight: 'bold', +// // color: theme.palette.backgroundA.contrastTextA, +// // backgroundColor: theme.palette.backgroundA.lighter, + +// // '&:hover': { +// // color: theme.palette.backgroundA.contrastTextA, +// // }, +// // '& .MuiTableCell-root': { +// // textAlign: 'right', +// // padding: theme.spacing(1), +// // }, +// // }) +// // ); +// // export const PortfolioPaginationActionsTableContentsContainer = styled(Box)( +// // ({ theme }) => ({ +// // // '.MuiIconButton-root': { +// // // padding: theme.spacing(1), +// // // margin: '0 4px', +// // // '&:hover': { +// // // backgroundColor: theme.palette.action.hover, +// // // }, +// // // }, +// // display: 'flex', +// // flexShrink: 0, +// // justifyContent: 'flex-end', +// // // ml: 2.5, +// // width: '100%', +// // flexDirection: 'row', +// // borderRadius: '5px', +// // boxShadow: '0 2px 4px rgba(0,0,0,0.1)', +// // '&:hover': { +// // boxShadow: '0 4px 8px rgba(0,0,0,0.2)', +// // }, +// // }) +// // ); +// // // TABLE PRICE BOX +// // export const PortfolioTablePriceBox = styled(Box)(({ theme }) => ({ +// // display: 'flex', +// // justifyContent: 'flex-end', +// // alignItems: 'center', +// // width: '100%', +// // padding: theme.spacing(2), +// // background: theme.palette.backgroundB.lighter, +// // })); +// // ! OVERLAY STYLES +// export const Overlay = styled(Box)(({ theme }) => ({ +// position: 'fixed', +// top: '0', +// left: '0', +// width: '100%', +// height: '100%', +// backgroundColor: 'rgba(0,0,0,0.5)', // display: 'flex', -// justifyContent: 'flex-end', +// justifyContent: 'center', // alignItems: 'center', -// width: '100%', -// padding: theme.spacing(2), -// background: theme.palette.backgroundB.lighter, -// })); -// ! OVERLAY STYLES -export const Overlay = styled(Box)(({ theme }) => ({ - position: 'fixed', - top: '0', - left: '0', - width: '100%', - height: '100%', - backgroundColor: 'rgba(0,0,0,0.5)', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - zIndex: 9999, -})); - -// ... more styles - -// import { makeStyles } from '@mui/material'; - -// export const usePortfolioStyles = makeStyles((theme) => ({ -// // portfolioBox: { -// // display: 'flex', -// // flexDirection: 'column', -// // justifyContent: 'center', -// // alignItems: 'center', -// // maxWidth: '100vw', -// // width: '100%', -// // height: '100%', -// // margin: theme.spacing(0, 'auto'), -// // padding: theme.spacing(1, 2, 3), -// // backgroundColor: theme.palette.backgroundA.lightest, -// // color: theme.palette.text.primary, -// // }, -// statisticPaper: { -// padding: theme.spacing(2), -// margin: 'auto', -// display: 'flex', -// flexDirection: 'column', -// alignItems: 'center', -// flexGrow: 1, -// justifyContent: 'center', -// minHeight: '100%', -// height: '100%', -// width: '100%', -// boxShadow: theme.shadows[3], -// }, -// statisticHeader: { -// marginBottom: theme.spacing(1), -// fontWeight: 'bold', -// color: theme.palette.backgroundA.lighter, -// }, -// statisticsContent: { -// display: 'flex', -// flexDirection: 'column', -// alignItems: 'center', -// justifyContent: 'center', -// flexGrow: 1, -// height: '100%', -// width: '100%', -// }, -// paper: { -// background: theme.palette.backgroundD.darkest, -// color: theme.palette.text.primary, -// display: 'flex', -// justifyContent: 'center', -// flexDirection: 'column', -// margin: 'auto', -// width: '100%', -// padding: { -// xs: theme.spacing(1), -// sm: theme.spacing(1), -// md: theme.spacing(2), -// lg: theme.spacing(2), -// }, -// borderRadius: theme.shape.borderRadius, -// }, - -// gridContainer: { -// width: '100%', -// alignItems: 'center', -// justifyContent: 'space-between', -// padding: theme.spacing(2), -// backgroundColor: theme.palette.backgroundB.lighter, -// borderRadius: theme.shape.borderRadius, -// border: '3px solid', -// borderColor: theme.palette.backgroundB.lightest, - -// // color: theme.palette.text.primary, -// // background: theme.palette.backgroundB.lighter, -// }, -// gridItem: { -// display: 'flex', -// justifyContent: 'flex-end', -// background: theme.palette.success.main, -// borderRadius: theme.shape.borderRadius, -// }, -// portfolioBoxA: { -// display: 'flex', -// flexDirection: 'column', -// gap: theme.spacing(4), -// borderRadius: theme.shape.borderRadius, -// flexGrow: 1, -// background: theme.palette.backgroundC.dark, -// padding: theme.spacing(4), -// height: 'auto', -// width: '100%', -// // Adjusting minHeight and maxHeight to take into account the header and additional space -// minHeight: 'calc(100vh - 64px)', // Reducing height by 128px -// maxHeight: 'calc(100vh - 128px)', // Adjusting maxHeight similarly -// boxShadow: theme.shadows[5], -// [theme.breakpoints.down('sm')]: { -// padding: theme.spacing(2), -// }, -// // Using margin to push the content upwards from the bottom by 64px -// // marginBottom: '64px', // Pushing upwards from the bottom -// }, - -// portfolioBoxB: { -// display: 'flex', -// flexDirection: 'column', -// gap: theme.spacing(4), -// borderRadius: theme.shape.borderRadius, -// flexGrow: 1, -// // background: theme.palette.success.main, -// background: theme.palette.backgroundD.dark, -// padding: theme.spacing(4), -// // height: 'auto', -// width: '100%', -// height: '100%', - -// // 100vh -// // minHeight: '90vh', -// boxShadow: theme.shadows[5], -// [theme.breakpoints.down('sm')]: { -// padding: theme.spacing(2), -// }, -// }, -// typography: { -// fontWeight: 700, -// color: theme.palette.backgroundC.contrastText, -// fontSize: '1.5rem', -// textAlign: 'left', -// paddingLeft: theme.spacing(2), -// [theme.breakpoints.down('sm')]: { -// fontSize: '1.25rem', -// }, -// }, - -// // Add more custom styles as needed -// // gridContainer: { -// // width: '100%', -// // alignItems: 'center', -// // justifyContent: 'space-between', -// // padding: theme.spacing(1), -// // }, -// // Additional styles -// dialogButton: { -// textTransform: 'none', // or other style adjustments for dialog buttons -// }, -// listContainer: { -// // marginTop: theme.spacing(2), -// maxHeight: '60vh', // or other appropriate height -// overflowY: 'auto', -// padding: theme.spacing(2), -// // background: theme.palette.backgroundC.lighter, -// }, -// dialogContent: { -// padding: theme.spacing(2), -// background: theme.palette.backgroundA.lightest, -// // height: '100%', -// }, -// boxStyle: { -// padding: theme.spacing(2), -// justifyContent: 'center', -// margin: 'auto', -// }, -// cardStyle: { -// display: 'flex', -// flexDirection: 'column', -// justifyContent: 'center', -// margin: 'auto', -// padding: theme.spacing(2), -// maxWidth: '100%', -// maxHeight: '100%', -// }, -// cardContentStyle: { -// display: 'flex', -// flexDirection: 'column', -// justifyContent: 'center', -// margin: 'auto', -// padding: theme.spacing(2), -// maxWidth: '100%', -// maxHeight: '100%', -// }, -// cardMediaStyle: { -// display: 'flex', -// flexDirection: 'column', -// justifyContent: 'center', -// margin: 'auto', -// padding: theme.spacing(2), -// maxWidth: '100%', -// maxHeight: '100%', -// }, - -// // CARD MEDIA SECTION -// mediaContainer: { -// cursor: 'pointer', -// position: 'relative', -// }, -// popover: { -// pointerEvents: 'none', -// height: 'auto', -// width: 'auto', -// maxWidth: '300px', -// maxHeight: 'auto', -// }, -// media: { -// width: '100%', -// height: 'auto', -// flexGrow: 1, -// alignItems: 'flex-end', -// [theme.breakpoints.down('xs')]: { -// padding: theme.spacing(1), -// }, -// [theme.breakpoints.down('sm')]: { -// padding: theme.spacing(1), -// }, -// [theme.breakpoints.down('md')]: { -// padding: theme.spacing(1), -// }, -// [theme.breakpoints.down('lg')]: { -// padding: theme.spacing(0.5), -// }, -// [theme.breakpoints.down('xl')]: { -// padding: theme.spacing(0.5), -// }, -// }, - -// // OFFICIAL UPDATED STYLES -// cardListContainerBox: { -// maxWidth: '100%', -// background: theme.palette.backgroundB.dark, -// borderRadius: theme.shape.borderRadius, -// width: { -// xs: '100%', // Full width on mobile screens -// md: '100%', -// }, -// padding: theme.spacing(4), -// }, -// cardListContainerGrid: { -// // background: theme.palette.backgroundB.default, -// maxHeight: '100%', -// width: '100%', -// }, -// tablePaper: { -// maxWidth: 'lg', -// margin: 'auto', -// overflowX: 'auto', // Ensures table doesn't overflow the paper -// background: theme.palette.backgroundB.default, -// padding: theme.spacing(2), -// [theme.breakpoints.up('xs')]: { -// padding: theme.spacing(1), // Smaller padding for xs -// }, -// [theme.breakpoints.up('sm')]: { -// padding: theme.spacing(2), // Medium padding for sm -// }, -// [theme.breakpoints.up('md')]: { -// padding: theme.spacing(2), // Larger padding for md -// }, -// [theme.breakpoints.up('lg')]: { -// padding: theme.spacing(2), // Extra larger padding for lg -// }, -// [theme.breakpoints.up('xl')]: { -// padding: theme.spacing(2), // Maximum padding for xl -// }, -// }, -// tableContainer: { -// // maxHeight: '60vh', -// maxWidth: 'lg', -// overflowY: 'auto', // Allows table to scroll vertically -// padding: theme.spacing(2), -// margin: theme.spacing(1), -// background: theme.palette.backgroundB.light, -// borderRadius: theme.shape.borderRadius, -// }, -// table: { -// minWidth: 300, -// width: '100%', -// background: theme.palette.backgroundA.lightest, -// }, -// tableRow: { -// // '&:nth-of-type(odd)': { -// // background: theme.palette.backgroundA.light, -// // }, -// // '&:nth-of-type(even)': { -// // background: theme.palette.backgroundA.lighter, -// // }, -// }, -// tableCell: { -// border: '2px solid', -// borderColor: theme.palette.divider, -// // background: theme.palette.backgroundA.light, - -// [theme.breakpoints.down('sm')]: { -// padding: theme.spacing(1), -// fontSize: '0.7rem', -// }, -// [theme.breakpoints.up('md')]: { -// padding: theme.spacing(2), -// fontSize: '1rem', -// }, -// }, -// tableHeader: { -// background: theme.palette.backgroundA.lighter, -// color: theme.palette.backgroundA.contrastTextA, -// [theme.breakpoints.up('md')]: { -// fontSize: '1rem', -// }, -// }, -// tableBody: { -// background: theme.palette.backgroundA.lightest, -// border: '2px solid', -// borderColor: theme.palette.divider, -// }, -// tableFooter: { -// background: theme.palette.backgroundA.lighter, -// }, -// tablePriceBox: { -// display: 'flex', -// justifyContent: 'flex-end', -// alignItems: 'center', -// width: '100%', -// padding: theme.spacing(2), -// background: theme.palette.backgroundB.lighter, -// }, -// buttonGroup: { -// display: 'flex', -// flexDirection: 'column', -// justifyContent: 'center', -// alignItems: 'center', -// boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)', -// }, -// button: { -// padding: '10px 20px', -// border: 'none', -// borderRadius: '5px', -// cursor: 'pointer', -// width: '100%', -// color: theme.palette.getContrastText(theme.palette.backgroundA.dark), -// backgroundColor: theme.palette.backgroundA.dark, -// '&:hover': { -// backgroundColor: theme.palette.backgroundA.darker, -// }, -// [theme.breakpoints.down('sm')]: { -// fontSize: '0.8rem', -// }, -// [theme.breakpoints.up('md')]: { -// fontSize: '1rem', -// }, -// }, -// // Add any other styles from CardPortfolio, PortfolioContent, and SelectCollection components +// zIndex: 9999, // })); -// export default usePortfolioStyles; +// // ... more styles + +// // import { makeStyles } from '@mui/material'; + +// // export const usePortfolioStyles = makeStyles((theme) => ({ +// // // portfolioBox: { +// // // display: 'flex', +// // // flexDirection: 'column', +// // // justifyContent: 'center', +// // // alignItems: 'center', +// // // maxWidth: '100vw', +// // // width: '100%', +// // // height: '100%', +// // // margin: theme.spacing(0, 'auto'), +// // // padding: theme.spacing(1, 2, 3), +// // // backgroundColor: theme.palette.backgroundA.lightest, +// // // color: theme.palette.text.primary, +// // // }, +// // statisticPaper: { +// // padding: theme.spacing(2), +// // margin: 'auto', +// // display: 'flex', +// // flexDirection: 'column', +// // alignItems: 'center', +// // flexGrow: 1, +// // justifyContent: 'center', +// // minHeight: '100%', +// // height: '100%', +// // width: '100%', +// // boxShadow: theme.shadows[3], +// // }, +// // statisticHeader: { +// // marginBottom: theme.spacing(1), +// // fontWeight: 'bold', +// // color: theme.palette.backgroundA.lighter, +// // }, +// // statisticsContent: { +// // display: 'flex', +// // flexDirection: 'column', +// // alignItems: 'center', +// // justifyContent: 'center', +// // flexGrow: 1, +// // height: '100%', +// // width: '100%', +// // }, +// // paper: { +// // background: theme.palette.backgroundD.darkest, +// // color: theme.palette.text.primary, +// // display: 'flex', +// // justifyContent: 'center', +// // flexDirection: 'column', +// // margin: 'auto', +// // width: '100%', +// // padding: { +// // xs: theme.spacing(1), +// // sm: theme.spacing(1), +// // md: theme.spacing(2), +// // lg: theme.spacing(2), +// // }, +// // borderRadius: theme.shape.borderRadius, +// // }, + +// // gridContainer: { +// // width: '100%', +// // alignItems: 'center', +// // justifyContent: 'space-between', +// // padding: theme.spacing(2), +// // backgroundColor: theme.palette.backgroundB.lighter, +// // borderRadius: theme.shape.borderRadius, +// // border: '3px solid', +// // borderColor: theme.palette.backgroundB.lightest, + +// // // color: theme.palette.text.primary, +// // // background: theme.palette.backgroundB.lighter, +// // }, +// // gridItem: { +// // display: 'flex', +// // justifyContent: 'flex-end', +// // background: theme.palette.success.main, +// // borderRadius: theme.shape.borderRadius, +// // }, +// // portfolioBoxA: { +// // display: 'flex', +// // flexDirection: 'column', +// // gap: theme.spacing(4), +// // borderRadius: theme.shape.borderRadius, +// // flexGrow: 1, +// // background: theme.palette.backgroundC.dark, +// // padding: theme.spacing(4), +// // height: 'auto', +// // width: '100%', +// // // Adjusting minHeight and maxHeight to take into account the header and additional space +// // minHeight: 'calc(100vh - 64px)', // Reducing height by 128px +// // maxHeight: 'calc(100vh - 128px)', // Adjusting maxHeight similarly +// // boxShadow: theme.shadows[5], +// // [theme.breakpoints.down('sm')]: { +// // padding: theme.spacing(2), +// // }, +// // // Using margin to push the content upwards from the bottom by 64px +// // // marginBottom: '64px', // Pushing upwards from the bottom +// // }, + +// // portfolioBoxB: { +// // display: 'flex', +// // flexDirection: 'column', +// // gap: theme.spacing(4), +// // borderRadius: theme.shape.borderRadius, +// // flexGrow: 1, +// // // background: theme.palette.success.main, +// // background: theme.palette.backgroundD.dark, +// // padding: theme.spacing(4), +// // // height: 'auto', +// // width: '100%', +// // height: '100%', + +// // // 100vh +// // // minHeight: '90vh', +// // boxShadow: theme.shadows[5], +// // [theme.breakpoints.down('sm')]: { +// // padding: theme.spacing(2), +// // }, +// // }, +// // typography: { +// // fontWeight: 700, +// // color: theme.palette.backgroundC.contrastText, +// // fontSize: '1.5rem', +// // textAlign: 'left', +// // paddingLeft: theme.spacing(2), +// // [theme.breakpoints.down('sm')]: { +// // fontSize: '1.25rem', +// // }, +// // }, + +// // // Add more custom styles as needed +// // // gridContainer: { +// // // width: '100%', +// // // alignItems: 'center', +// // // justifyContent: 'space-between', +// // // padding: theme.spacing(1), +// // // }, +// // // Additional styles +// // dialogButton: { +// // textTransform: 'none', // or other style adjustments for dialog buttons +// // }, +// // listContainer: { +// // // marginTop: theme.spacing(2), +// // maxHeight: '60vh', // or other appropriate height +// // overflowY: 'auto', +// // padding: theme.spacing(2), +// // // background: theme.palette.backgroundC.lighter, +// // }, +// // dialogContent: { +// // padding: theme.spacing(2), +// // background: theme.palette.backgroundA.lightest, +// // // height: '100%', +// // }, +// // boxStyle: { +// // padding: theme.spacing(2), +// // justifyContent: 'center', +// // margin: 'auto', +// // }, +// // cardStyle: { +// // display: 'flex', +// // flexDirection: 'column', +// // justifyContent: 'center', +// // margin: 'auto', +// // padding: theme.spacing(2), +// // maxWidth: '100%', +// // maxHeight: '100%', +// // }, +// // cardContentStyle: { +// // display: 'flex', +// // flexDirection: 'column', +// // justifyContent: 'center', +// // margin: 'auto', +// // padding: theme.spacing(2), +// // maxWidth: '100%', +// // maxHeight: '100%', +// // }, +// // cardMediaStyle: { +// // display: 'flex', +// // flexDirection: 'column', +// // justifyContent: 'center', +// // margin: 'auto', +// // padding: theme.spacing(2), +// // maxWidth: '100%', +// // maxHeight: '100%', +// // }, + +// // // CARD MEDIA SECTION +// // mediaContainer: { +// // cursor: 'pointer', +// // position: 'relative', +// // }, +// // popover: { +// // pointerEvents: 'none', +// // height: 'auto', +// // width: 'auto', +// // maxWidth: '300px', +// // maxHeight: 'auto', +// // }, +// // media: { +// // width: '100%', +// // height: 'auto', +// // flexGrow: 1, +// // alignItems: 'flex-end', +// // [theme.breakpoints.down('xs')]: { +// // padding: theme.spacing(1), +// // }, +// // [theme.breakpoints.down('sm')]: { +// // padding: theme.spacing(1), +// // }, +// // [theme.breakpoints.down('md')]: { +// // padding: theme.spacing(1), +// // }, +// // [theme.breakpoints.down('lg')]: { +// // padding: theme.spacing(0.5), +// // }, +// // [theme.breakpoints.down('xl')]: { +// // padding: theme.spacing(0.5), +// // }, +// // }, + +// // // OFFICIAL UPDATED STYLES +// // cardListContainerBox: { +// // maxWidth: '100%', +// // background: theme.palette.backgroundB.dark, +// // borderRadius: theme.shape.borderRadius, +// // width: { +// // xs: '100%', // Full width on mobile screens +// // md: '100%', +// // }, +// // padding: theme.spacing(4), +// // }, +// // cardListContainerGrid: { +// // // background: theme.palette.backgroundB.default, +// // maxHeight: '100%', +// // width: '100%', +// // }, +// // tablePaper: { +// // maxWidth: 'lg', +// // margin: 'auto', +// // overflowX: 'auto', // Ensures table doesn't overflow the paper +// // background: theme.palette.backgroundB.default, +// // padding: theme.spacing(2), +// // [theme.breakpoints.up('xs')]: { +// // padding: theme.spacing(1), // Smaller padding for xs +// // }, +// // [theme.breakpoints.up('sm')]: { +// // padding: theme.spacing(2), // Medium padding for sm +// // }, +// // [theme.breakpoints.up('md')]: { +// // padding: theme.spacing(2), // Larger padding for md +// // }, +// // [theme.breakpoints.up('lg')]: { +// // padding: theme.spacing(2), // Extra larger padding for lg +// // }, +// // [theme.breakpoints.up('xl')]: { +// // padding: theme.spacing(2), // Maximum padding for xl +// // }, +// // }, +// // tableContainer: { +// // // maxHeight: '60vh', +// // maxWidth: 'lg', +// // overflowY: 'auto', // Allows table to scroll vertically +// // padding: theme.spacing(2), +// // margin: theme.spacing(1), +// // background: theme.palette.backgroundB.light, +// // borderRadius: theme.shape.borderRadius, +// // }, +// // table: { +// // minWidth: 300, +// // width: '100%', +// // background: theme.palette.backgroundA.lightest, +// // }, +// // tableRow: { +// // // '&:nth-of-type(odd)': { +// // // background: theme.palette.backgroundA.light, +// // // }, +// // // '&:nth-of-type(even)': { +// // // background: theme.palette.backgroundA.lighter, +// // // }, +// // }, +// // tableCell: { +// // border: '2px solid', +// // borderColor: theme.palette.divider, +// // // background: theme.palette.backgroundA.light, + +// // [theme.breakpoints.down('sm')]: { +// // padding: theme.spacing(1), +// // fontSize: '0.7rem', +// // }, +// // [theme.breakpoints.up('md')]: { +// // padding: theme.spacing(2), +// // fontSize: '1rem', +// // }, +// // }, +// // tableHeader: { +// // background: theme.palette.backgroundA.lighter, +// // color: theme.palette.backgroundA.contrastTextA, +// // [theme.breakpoints.up('md')]: { +// // fontSize: '1rem', +// // }, +// // }, +// // tableBody: { +// // background: theme.palette.backgroundA.lightest, +// // border: '2px solid', +// // borderColor: theme.palette.divider, +// // }, +// // tableFooter: { +// // background: theme.palette.backgroundA.lighter, +// // }, +// // tablePriceBox: { +// // display: 'flex', +// // justifyContent: 'flex-end', +// // alignItems: 'center', +// // width: '100%', +// // padding: theme.spacing(2), +// // background: theme.palette.backgroundB.lighter, +// // }, +// // buttonGroup: { +// // display: 'flex', +// // flexDirection: 'column', +// // justifyContent: 'center', +// // alignItems: 'center', +// // boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)', +// // }, +// // button: { +// // padding: '10px 20px', +// // border: 'none', +// // borderRadius: '5px', +// // cursor: 'pointer', +// // width: '100%', +// // color: theme.palette.getContrastText(theme.palette.backgroundA.dark), +// // backgroundColor: theme.palette.backgroundA.dark, +// // '&:hover': { +// // backgroundColor: theme.palette.backgroundA.darker, +// // }, +// // [theme.breakpoints.down('sm')]: { +// // fontSize: '0.8rem', +// // }, +// // [theme.breakpoints.up('md')]: { +// // fontSize: '1rem', +// // }, +// // }, +// // // Add any other styles from CardPortfolio, PortfolioContent, and SelectCollection components +// // })); + +// // export default usePortfolioStyles; diff --git a/src/context/hooks/style-hooks/useResponsiveStyles.jsx b/src/context/hooks/style-hooks/useResponsiveStyles.jsx index 5b2e9ab..130e55a 100644 --- a/src/context/hooks/style-hooks/useResponsiveStyles.jsx +++ b/src/context/hooks/style-hooks/useResponsiveStyles.jsx @@ -1,7 +1,7 @@ import { useMediaQuery } from '@mui/material'; -import DeckOfCardsIcon from '../../../components/reusable/icons/DeckOfCardsIcon'; -import MoneyIcon from '../../../components/reusable/icons/MoneyIcon'; -import ChartsIcon from '../../../components/reusable/icons/ChartsIcon'; +import DeckOfCardsIcon from '../../../layout/REUSABLE_COMPONENTS/icons/DeckOfCardsIcon'; +import MoneyIcon from '../../../layout/REUSABLE_COMPONENTS/icons/MoneyIcon'; +import ChartsIcon from '../../../layout/REUSABLE_COMPONENTS/icons/ChartsIcon'; const useResponsiveStyles = (theme) => { const isXSmall = useMediaQuery(theme.breakpoints.down('xs')); diff --git a/src/context/hooks/useCardActions.jsx b/src/context/hooks/useCardActions.jsx index 333029f..6706f62 100644 --- a/src/context/hooks/useCardActions.jsx +++ b/src/context/hooks/useCardActions.jsx @@ -1,6 +1,5 @@ import { useCallback, useState } from 'react'; import useCounter from './useCounter'; -import useSnackBar from './useSnackBar'; import useDialog from './useDialog'; export const useCardActions = ( @@ -18,7 +17,6 @@ export const useCardActions = ( onFailure, page ) => { - const { handleSnackBar } = useSnackBar(); // Destructure handleSnackBar from useSnackBar hook const { openDialog } = useDialog(); // Destructure openDialog from useDialog hook const [isDialogOpen, setIsDialogOpen] = useState(false); @@ -36,26 +34,11 @@ export const useCardActions = ( const performAction = useCallback( (action) => { - // if (context === 'Collection' && !selectedCollection) { - // // Show alert and open dialog if no collection is selected - // handleSnackBar('Please select a collection.', 'error'); // Use handleSnackBar to show a snackbar - // openDialog('Collection'); // Open the 'Collection' dialog - // return; - // } - // if (context === 'Deck' && !selectedDeck) { - // // Show alert and open dialog if no deck is selected - // handleSnackBar('Please select a deck.', 'error'); // Use handleSnackBar to show a snackbar - // openDialog('Deck'); // Open the 'Deck' dialog - // return; - // } - // Increment or decrement based on the action const updateQuantity = (actionType) => { actionType === 'add' ? increment(card.id) : decrement(card.id); }; const array = placeObjectInArray(data); console.log('DATA =>=>=> ', data); - - // Action functions for different contexts const actionFunctions = { Collection: { add: () => { @@ -119,7 +102,6 @@ export const useCardActions = ( selectedCollection, selectedDeck, onSuccess, - handleSnackBar, ] ); diff --git a/src/context/hooks/useCardStore.jsx b/src/context/hooks/useCardStore.jsx index 1a7b209..2c3870f 100644 --- a/src/context/hooks/useCardStore.jsx +++ b/src/context/hooks/useCardStore.jsx @@ -33,6 +33,8 @@ export const useCardStoreHook = () => { [] ); const [searchData, setSearchData] = useLocalStorage('searchData', []); + const [randomCards, setRandomCards] = useLocalStorage('randomCards', []); + const [isDataValid, setIsDataValid] = useState(searchData.length > 0); const [initialLoad, setInitialLoad] = useState(true); // New state to track the initial load const { @@ -117,6 +119,27 @@ export const useCardStoreHook = () => { }, 100), [] ); // 500ms delay, adjust as needed + async function fetchRandomCardsAndSet() { + startLoading('fetchRandomCardsAndSet'); + try { + // Replace `http://your-server-address.com` with the actual server address + const response = await fetch( + `${process.env.REACT_APP_SERVER}/api/cards/randomCardData` + ); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const cards = await response.json(); + + // Assuming you have a function to update your UI with these cards + setRandomCards(cards); + } catch (error) { + console.error('Failed to fetch random cards:', error); + startLoading('fetchRandomCardsAndSet'); + // Optionally, update the UI to reflect the error state + // displayError('Failed to fetch random cards. Please try again later.'); + } + } useEffect(() => { // Log updated search values only if it's not the initial load @@ -132,12 +155,17 @@ export const useCardStoreHook = () => { isDataValid, setSearchData, cardsVersion, + setCardsVersion, + // toggleConfigurator, + // setIsConfiguratorOpen, + fetchRandomCardsAndSet, + handleRequest, + randomCards, // isConfiguratorOpen, // toggleConfigurator, // setPreviousSearchData, setIsDataValid, clearSearchData, - handleRequest, loadingSearchResults: isLoading('isSearchLoading'), setLoadingSearchResults: () => { startLoading('isSearchLoading'); diff --git a/src/context/hooks/useCustomSnackbar.jsx b/src/context/hooks/useCustomSnackbar.jsx new file mode 100644 index 0000000..a7329c8 --- /dev/null +++ b/src/context/hooks/useCustomSnackbar.jsx @@ -0,0 +1,29 @@ +// // useCustomSnackbar.js +// import React from 'react'; +// import { useSnackbar } from 'notistack'; +// import { Box, CircularProgress, Typography } from '@mui/material'; + +// const useCustomSnackbar = () => { +// const { enqueueSnackbar } = useSnackbar(); +// const showSnackbar = (title, subTitle, options = {}) => { +// const content = ( +// +// {title} +// +// {subTitle} +// +// +// ); + +// enqueueSnackbar(content, { +// ...options, +// action: options.persist +// ? (key) => +// : undefined, +// }); +// }; + +// return showSnackbar; +// }; + +// export default useCustomSnackbar; diff --git a/src/context/hooks/useDialog.jsx b/src/context/hooks/useDialog.jsx index ca69d76..41f41bb 100644 --- a/src/context/hooks/useDialog.jsx +++ b/src/context/hooks/useDialog.jsx @@ -1,9 +1,9 @@ import { useState, useCallback } from 'react'; -import useSnackBar from './useSnackBar'; +// import useSnackBar from './useSnackBar'; const useDialog = () => { const [dialogOpenStates, setDialogOpenStates] = useState({}); - const { handleSnackBar } = useSnackBar(); // Destructure handleSnackBar from useSnackBar hook + // const { handleSnackBar } = useSnackBar(); // Destructure handleSnackBar from useSnackBar hook // Toggle the state of the dialog and display snack bar message const toggleDialogState = useCallback( @@ -12,13 +12,13 @@ const useDialog = () => { ...prevStates, [dialogName]: isOpen, })); - const action = isOpen ? 'opened' : 'closed'; - handleSnackBar(`${dialogName} Dialog ${action}`, { - variant: 'info', - duration: 6000, - }); // Use enqueueSnackbar to show a snackbar + // const action = isOpen ? 'opened' : 'closed'; + // handleSnackBar(`${dialogName} Dialog ${action}`, { + // variant: 'info', + // duration: 6000, + // }); // Use enqueueSnackbar to show a snackbar }, - [handleSnackBar] + [setDialogOpenStates] ); const openDialog = useCallback( diff --git a/src/context/hooks/useFetchWrapper.jsx b/src/context/hooks/useFetchWrapper.jsx index d2b24a0..c6878e4 100644 --- a/src/context/hooks/useFetchWrapper.jsx +++ b/src/context/hooks/useFetchWrapper.jsx @@ -1,97 +1,25 @@ -// import { useState, useCallback } from 'react'; -// import useLogger from './useLogger'; -// import useLocalStorage from './useLocalStorage'; -// import { useLoading } from './useLoading'; - -// const useFetchWrapper = () => { -// const [status, setStatus] = useState('idle'); // 'idle', 'loading', 'success', 'error' -// const [data, setData] = useState(null); -// const [responseCache, setResponseCache] = useLocalStorage('apiResponses', {}); -// const [error, setError] = useState(null); -// const { logEvent } = useLogger('useFetchWrapper'); -// const { startLoading, stopLoading, isLoading } = useLoading(); - -// const fetchWrapper = useCallback( -// async (url, method = 'GET', body = null, loadingID) => { -// setStatus('loading'); -// startLoading(loadingID); -// try { -// const headers = { 'Content-Type': 'application/json' }; -// const options = { -// method, -// headers, -// ...(body && { body: JSON.stringify(body) }), -// }; -// const response = await fetch(url, options); -// const contentType = response.headers.get('content-type'); -// let responseData; - -// if (contentType && !contentType.includes('application/json')) { -// // Handle non-JSON responses first -// responseData = await response.text(); -// } else { -// // Fallback to JSON if the content type is application/json -// responseData = await response.json(); -// } -// if (!response.ok) { -// throw new Error(`An error occurred: ${response.statusText}`); -// } -// setStatus('success'); -// setData(responseData); -// setResponseCache((prevCache) => ({ -// ...prevCache, -// [loadingID]: responseData, // Use loadingID as the key -// })); - -// return responseData; -// } catch (error) { -// setError(error.toString()); -// setStatus('error'); -// logEvent('fetch error', { url, error: error.toString() }); -// } finally { -// stopLoading(loadingID); -// } -// }, -// [setResponseCache, startLoading, stopLoading, logEvent] -// ); -// return { -// status, -// data, -// responseCache, -// error, -// fetchWrapper, -// isLoading, -// }; -// }; - -// export default useFetchWrapper; import { useState, useCallback } from 'react'; import useLogger from './useLogger'; import useLocalStorage from './useLocalStorage'; import { useLoading } from './useLoading'; -import { useSnackbar } from 'notistack'; -import CircularProgress from '@mui/material/CircularProgress'; +import useSnackbarManager from './useSnackbarManager'; const useFetchWrapper = () => { - const [status, setStatus] = useState('idle'); // 'idle', 'loading', 'success', 'error' + const [status, setStatus] = useState('idle'); const [data, setData] = useState(null); const [responseCache, setResponseCache] = useLocalStorage('apiResponses', {}); const [error, setError] = useState(null); const { logEvent } = useLogger('useFetchWrapper'); const { startLoading, stopLoading, isLoading } = useLoading(); - const { enqueueSnackbar, closeSnackbar } = useSnackbar(); + const { showSuccess, showInfo, showError } = useSnackbarManager(); const fetchWrapper = useCallback( async (url, method = 'GET', body = null, loadingID) => { setStatus('loading'); startLoading(loadingID); - // Show loading snackbar - const snackbarKey = enqueueSnackbar('Loading...', { - variant: 'info', - persist: true, // Keep the snackbar open - action: (key) => , - }); + // Showing loading snackbar + const loadingSnackbar = showInfo('Loading', loadingID); try { const headers = { 'Content-Type': 'application/json' }; @@ -112,6 +40,7 @@ const useFetchWrapper = () => { if (!response.ok) { throw new Error(`An error occurred: ${response.statusText}`); } + setStatus('success'); setData(responseData); setResponseCache((prevCache) => ({ @@ -119,14 +48,22 @@ const useFetchWrapper = () => { [loadingID]: responseData, })); + // Showing success snackbar + showSuccess(`Your ${loadingID} data has been fetched successfully.`); + return responseData; } catch (error) { setError(error.toString()); setStatus('error'); logEvent('fetch error', { url, error: error.toString() }); + + // Showing error snackbar + showError(`Error fetching ${loadingID}: ${error.toString()}`); } finally { stopLoading(loadingID); - closeSnackbar(snackbarKey); // Close the loading snackbar + + // Closing loading snackbar + // closeSnackbar(loadingSnackbar); } }, [ @@ -134,8 +71,10 @@ const useFetchWrapper = () => { startLoading, stopLoading, logEvent, - enqueueSnackbar, - closeSnackbar, + showSuccess, + showInfo, + showError, + // closeSnackbar, ] ); diff --git a/src/context/hooks/useGridItems.jsx b/src/context/hooks/useGridItems.jsx index fe8c851..aff2361 100644 --- a/src/context/hooks/useGridItems.jsx +++ b/src/context/hooks/useGridItems.jsx @@ -1,33 +1,47 @@ import React, { useMemo } from 'react'; -import { Grid, Grow } from '@mui/material'; -import SkeletonDeckItem from '../../components/grids/gridItems/SkeletonDeckItem'; +import { Grid, Grow, IconButton, Tooltip } from '@mui/material'; import { useCardStoreHook } from './useCardStore'; import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; import GenericCard from '../../components/cards/GenericCard'; - -const useGridItems = ({ itemsPerPage, cards, pageContext, isLoading }) => { +import { SkeletonCard } from '../../layout/REUSABLE_COMPONENTS/SkeletonVariants'; +import DeleteIcon from '@mui/icons-material/Delete'; +import useMode from '../UTILITIES_CONTEXT/ColorModeContext/useMode'; +import HighlightOffRoundedIcon from '@mui/icons-material/HighlightOffRounded'; +import useDeckManager from '../MAIN_CONTEXT/DeckContext/useDeckManager'; +const useGridItems = ({ + itemsPerPage, + cards, + pageContext, + isLoading, + hasFetched, + allItems, + validSelection, + type, + deckId, +}) => { const { loadingSearchResults } = useCardStoreHook(); - const skeletonCount = isLoading ? itemsPerPage : cards.length; - + const skeletonCount = isLoading ? itemsPerPage : cards?.length; + const { theme } = useMode(); + const { removeOneFromDeck } = useDeckManager(); const calculateTimeout = (index) => index * 400; // Adjust this value for faster or slower animations - // Adjust the grid layout here const gridItems = useMemo(() => { + console.log('GRID ITEMS:', cards); return ( isLoading || loadingSearchResults ? Array.from({ length: itemsPerPage }) : cards - ).map((card, index) => ( + )?.map((card, index) => ( { height: '100%', display: 'flex', flexDirection: 'column', + position: 'relative', // Ensure the MDBox can act as a reference for the delete icon's positioning }} > - {/* Conditionally render Skeleton or Card based on loading state */} {isLoading || loadingSearchResults ? ( - + ) : ( - + + + {type === 'deck' && ( + + { + // Add delete logic here + console.log('Delete icon clicked for', card?.id); + removeOneFromDeck(card, card?.id, deckId); + console.log( + 'Card removed from deck', + card, + card?.id, + deckId + ); + }} + > + + + + )} + )} + {/* TODO: create a very small delete icon which appears on hover at the top right of the grid Item. It will need to be a higher z index or posiito naboslujte or somethihng to be visible above the card */} @@ -65,85 +128,5 @@ const useGridItems = ({ itemsPerPage, cards, pageContext, isLoading }) => { ); }; -// const gridItems = useMemo(() => { -// return isLoading || loadingSearchResults -// ? Array.from({ length: skeletonCount }).map((_, index) => ( -// -// -//
-// -//
-//
-//
-// )) -// : cards.map((card, index) => ( -// -// -// -// -// -// -// -// -// -// )); -// }, [cards, isLoading, loadingSearchResults, itemsPerPage]); - -// return <>{gridItems}; -// }; export default useGridItems; diff --git a/src/context/hooks/useLocalStorage.jsx b/src/context/hooks/useLocalStorage.jsx index 621f3ca..e8a6200 100644 --- a/src/context/hooks/useLocalStorage.jsx +++ b/src/context/hooks/useLocalStorage.jsx @@ -1,5 +1,13 @@ import { useState, useEffect, useRef } from 'react'; - +// This function tries to parse JSON and provides a fallback if parsing fails +function safeJsonParse(value) { + try { + return value ? JSON.parse(value) : null; + } catch (e) { + console.error('Error reading localStorage key:', e); + return null; + } +} function useLocalStorage(key, initialValue) { const [storedValue, setStoredValue] = useState(() => { if (typeof window === 'undefined') { @@ -8,7 +16,7 @@ function useLocalStorage(key, initialValue) { try { const item = window.localStorage.getItem(key); - return item ? JSON.parse(item) : initialValue; + return item ? safeJsonParse(item) : initialValue; } catch (error) { console.error(`Error reading localStorage key "${key}":`, error); return initialValue; @@ -52,8 +60,9 @@ function useLocalStorage(key, initialValue) { const valueToStore = value instanceof Function ? value(storedValue) : value; - window.localStorage.setItem(key, JSON.stringify(valueToStore)); + // window.localStorage.setItem(key, JSON.stringify(valueToStore)); setStoredValue(valueToStore); + window.localStorage.setItem(key, JSON.stringify(valueToStore)); // Optionally, for cross-tab communication, you might want to trigger a storage event manually window.dispatchEvent( diff --git a/src/context/hooks/useMasterCardList.jsx b/src/context/hooks/useMasterCardList.jsx deleted file mode 100644 index ce7cbbb..0000000 --- a/src/context/hooks/useMasterCardList.jsx +++ /dev/null @@ -1,43 +0,0 @@ -// import { useContext, useState, useEffect, useCallback } from 'react'; -// import { DeckContext } from '../../MAIN_CONTEXT/DeckContext/DeckContext'; -// import { CartContext } from '../../MAIN_CONTEXT/CartContext/CartContext'; -// import { CollectionContext } from '../../MAIN_CONTEXT/CollectionContext/CollectionContext'; -// import useLocalStorage from '../../hooks/useLocalStorage'; - -// const useMasterCardList = () => { -// const [masterCardList, setMasterCardList] = useState([]); -// const [allCardsWithQuantities, setAllCardsWithQuantities] = useLocalStorage( -// 'allCardsWithQuantities', -// [] -// ); -// const Deck = useContext(DeckContext); -// const Cart = useContext(CartContext); -// const Collection = useContext(CollectionContext); -// const [cardsWithQuantities, setCardsWithQuantities] = useState([]); -// const { selectedCollection, allCollections } = Collection; -// const { selectedDeck, allDecks } = Deck; -// const { cartData } = Cart; - -// const compileCardsWithQuantities = () => { -// if (!selectedCollection && !selectedDeck && !cartData) return []; -// const deckCards = allDecks?.reduce((acc, deck) => { -// if (deck.cards) { -// acc = [...acc, ...deck.cards]; -// } -// return acc; -// }, []); -// const cartCards = cartData?.cart || []; -// const collectionCards = allCollections?.reduce((acc, collection) => { -// if (collection?.cards) { -// acc = [...acc, ...collection.cards]; -// } -// return acc; -// }, []); -// return { -// cardsWithQuantities, -// allCardsWithQuantities /* any other values or functions to expose */, -// }; -// }; - - - diff --git a/src/context/hooks/usePagination.jsx b/src/context/hooks/usePagination.jsx index 5102d02..5695cae 100644 --- a/src/context/hooks/usePagination.jsx +++ b/src/context/hooks/usePagination.jsx @@ -2,25 +2,15 @@ import { useMemo, useState } from 'react'; const usePagination = (items, defaultPageSize) => { const [currentPage, setCurrentPage] = useState(1); - - // Setup for react-table pagination - // Assumes items is the total dataset you want to paginate over const data = useMemo(() => items, [items]); - - // Pagination settings for react-table const initialState = useMemo( () => ({ pageIndex: 0, - pageSize: defaultPageSize || 10, // Default page size + pageSize: defaultPageSize || 10, }), [defaultPageSize] ); - // The react-table hook will manage the pageIndex and pageSize, - // so you don't need to manage these states manually. - // You would pass initialState to the useTable hook along with 'data' and 'columns' - - // Calculate the total pages const pageCount = useMemo( () => Math.ceil(items.length / initialState.pageSize), [items.length, initialState.pageSize] @@ -32,13 +22,6 @@ const usePagination = (items, defaultPageSize) => { return data.slice(start, end); }, [data, currentPage, defaultPageSize]); - // You can still create a custom pagination component if you like, - // but you would control it using the state and actions provided by react-table. - - // Note: This example does not include the implementation of fetching data based on the current page, - // as it assumes 'items' contains the full dataset. If you're fetching data from a server, - // you would need to integrate that logic here, using the pageIndex and pageSize from react-table's state. - return { data, initialState, @@ -46,87 +29,7 @@ const usePagination = (items, defaultPageSize) => { paginatedData, currentPage, setCurrentPage, - // No need to return handlers like handleChangePage or handleChangeRowsPerPage - // as react-table's usePagination hook provides these functionalities. }; }; export default usePagination; - -// import { useState, useMemo } from 'react'; -// import CustomPagination from '../../components/reusable/CustomPagination'; - -// const usePagination = (items, itemsPerPage, totalCount) => { -// const [page, setPage] = useState(0); -// const [rowsPerPageOptions] = useState([ -// 5, -// 10, -// 25, -// { label: 'All', value: -1 }, -// ]); -// const [rowsPerPage, setRowsPerPage] = useState( -// itemsPerPage || rowsPerPageOptions[0] -// ); -// const [colsPerPage, setColsPerPage] = useState(1); - -// const emptyRows = useMemo( -// () => Math.max(0, (1 + page) * rowsPerPage - (items?.length || 0)), -// [page, rowsPerPage, items] -// ); - -// const handleChangePage = (newPage) => setPage(newPage); - -// const handleChangeRowsPerPage = (event) => { -// setRowsPerPage(parseInt(event.target.value, 10)); -// setPage(0); -// }; - -// const handleChangeColsPerPage = (event) => { -// setColsPerPage(parseInt(event.target.value, 10)); -// }; - -// const paginatedItems = useMemo(() => { -// const startIndex = page * rowsPerPage; -// const endIndex = -// rowsPerPage === -1 ? items?.length : startIndex + rowsPerPage; -// return items?.slice(startIndex, endIndex); -// }, [page, rowsPerPage, items]); -// const PaginationComponent = ( -// setPage(value)} -// /> -// ); - -// // Additional pagination actions -// const goToFirstPage = () => setPage(0); -// const goToLastPage = () => -// setPage(Math.max(0, Math.ceil(items?.length / rowsPerPage) - 1)); -// const goToNextPage = () => -// setPage((current) => -// Math.min(current + 1, Math.ceil(items?.length / rowsPerPage) - 1) -// ); -// const goToPreviousPage = () => setPage((current) => Math.max(current - 1, 0)); - -// return { -// page, -// rowsPerPage, -// colsPerPage, -// rowsPerPageOptions, -// emptyRows, -// paginatedItems, -// PaginationComponent, - -// handleChangePage, -// handleChangeRowsPerPage, -// handleChangeColsPerPage, -// goToFirstPage, -// goToLastPage, -// goToNextPage, -// goToPreviousPage, -// }; -// }; - -// export default usePagination; diff --git a/src/context/hooks/usePopover.jsx b/src/context/hooks/usePopover.jsx new file mode 100644 index 0000000..455fc46 --- /dev/null +++ b/src/context/hooks/usePopover.jsx @@ -0,0 +1,13 @@ +import { useState } from 'react'; + +export const usePopover = () => { + const [hoveredCard, setHoveredCard] = useState(null); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + return { + hoveredCard, + isPopoverOpen, + setHoveredCard, + setIsPopoverOpen, + }; +}; diff --git a/src/context/hooks/useSelectionDialog.jsx b/src/context/hooks/useSelectionDialog.jsx index 35feb18..65ac074 100644 --- a/src/context/hooks/useSelectionDialog.jsx +++ b/src/context/hooks/useSelectionDialog.jsx @@ -1,42 +1,42 @@ -import { useState, useCallback } from 'react'; -import { useCollectionStore } from '../MAIN_CONTEXT/CollectionContext/CollectionContext'; -import { useDeckStore } from '../MAIN_CONTEXT/DeckContext/DeckContext'; -import useSelectedCollection from '../MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +// import { useState, useCallback } from 'react'; +// import { useCollectionStore } from '../MAIN_CONTEXT/CollectionContext/CollectionContext'; +// import { useDeckStore } from '../MAIN_CONTEXT/DeckContext/DeckContext'; +// import useSelectedCollection from '../MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -export const useSelectionDialog = ( - context, - // selectedCollection, - selectedDeck, - // allCollections, - allDecks -) => { - const { selectedCollection, allCollections, handleSelectCollection } = - useSelectedCollection(); - const { setSelectedDeck } = useDeckStore(); - const [selectDialogOpen, setSelectDialogOpen] = useState(false); - const [itemsForSelection, setItemsForSelection] = useState([]); +// export const useSelectionDialog = ( +// context, +// // selectedCollection, +// selectedDeck, +// // allCollections, +// allDecks +// ) => { +// const { selectedCollection, allCollections, handleSelectCollection } = +// useSelectedCollection(); +// const { setSelectedDeck } = useDeckStore(); +// const [selectDialogOpen, setSelectDialogOpen] = useState(false); +// const [itemsForSelection, setItemsForSelection] = useState([]); - const openSelectionDialog = useCallback(() => { - if (!selectedCollection || !selectedDeck) { - setItemsForSelection( - context === 'Collection' ? allCollections : allDecks - ); - setSelectDialogOpen(true); - } - }, [selectedCollection, selectedDeck, allCollections, allDecks, context]); +// const openSelectionDialog = useCallback(() => { +// if (!selectedCollection || !selectedDeck) { +// setItemsForSelection( +// context === 'Collection' ? allCollections : allDecks +// ); +// setSelectDialogOpen(true); +// } +// }, [selectedCollection, selectedDeck, allCollections, allDecks, context]); - const handleSelection = (item) => { - context === 'Collection' - ? handleSelectCollection(item) - : setSelectedDeck(item); - setSelectDialogOpen(false); - }; +// const handleSelection = (item) => { +// context === 'Collection' +// ? handleSelectCollection(item) +// : setSelectedDeck(item); +// setSelectDialogOpen(false); +// }; - return { - selectDialogOpen, - itemsForSelection, - openSelectionDialog, - handleSelection, - setSelectDialogOpen, - }; -}; +// return { +// selectDialogOpen, +// itemsForSelection, +// openSelectionDialog, +// handleSelection, +// setSelectDialogOpen, +// }; +// }; diff --git a/src/context/hooks/useSnackBar.jsx b/src/context/hooks/useSnackBar.jsx deleted file mode 100644 index 08d7a64..0000000 --- a/src/context/hooks/useSnackBar.jsx +++ /dev/null @@ -1,79 +0,0 @@ -import { IconButton } from '@mui/material'; -import { useSnackbar } from 'notistack'; -import { useState, useCallback, useRef, useEffect } from 'react'; -import CloseIcon from '@mui/icons-material/Close'; -const useSnackBar = () => { - const { enqueueSnackbar, closeSnackbar } = useSnackbar(); - const defaultOptions = { - variant: 'info', - autoHideDuration: 6000, - }; - const isMountedRef = useRef(false); - - useEffect(() => { - isMountedRef.current = true; - return () => { - isMountedRef.current = false; - }; - }, []); - const handleSnackBar = useCallback( - (message, options = defaultOptions) => { - // Enqueue a new snackbar using notistack's enqueueSnackbar function - // `options` can include severity as `variant` and custom `duration` as `autoHideDuration` - // const { title, description } = message; - const { variant, autoHideDuration } = options; - if (open && message.title) { - enqueueSnackbar(message, { - message, - variant, - action: (key) => ( - closeSnackbar(key)}> - - - ), - }); - } - }, - [enqueueSnackbar] - ); - - // const [snackbar, setSnackbar] = useState({ - // open: false, - // message: '', - // severity: 'info', - // duration: 6000, - // }); - // const queueRef = useRef([]); - - // const showNextSnackbar = useCallback(() => { - // if (queueRef.current.length > 0 && isMountedRef.current) { - // const nextSnackbar = queueRef.current.shift(); - // setSnackbar({ ...nextSnackbar, open: true }); - // } - // }, []); - - // const handleSnackBar = useCallback( - // (message, severity = 'info', duration = 6000) => { - // queueRef.current.push({ message, severity, duration }); - // if (!snackbar.open && isMountedRef.current) { - // showNextSnackbar(); - // } - // }, - // [snackbar.open, showNextSnackbar] - // ); - - const handleCloseSnackbar = useCallback(() => { - if (isMountedRef.current) { - closeSnackbar(); - // setSnackbar((prevSnackbar) => ({ - // ...prevSnackbar, - // open: false, - // })); - // showNextSnackbar(); - } - }, [closeSnackbar]); - - return { handleSnackBar, handleCloseSnackbar }; -}; - -export default useSnackBar; diff --git a/src/context/hooks/useSnackbarManager.jsx b/src/context/hooks/useSnackbarManager.jsx new file mode 100644 index 0000000..01c7915 --- /dev/null +++ b/src/context/hooks/useSnackbarManager.jsx @@ -0,0 +1,59 @@ +// useSnackbarManager.js +import { useSnackbar } from 'notistack'; + +export default function useSnackbarManager() { + const { enqueueSnackbar } = useSnackbar(); + + const showSuccess = (message) => { + enqueueSnackbar(message, { + variant: 'success', + anchorOrigin: { + vertical: 'top', + horizontal: 'right', + }, + }); + }; + + const showInfo = (message) => { + enqueueSnackbar(message, { + variant: 'info', + anchorOrigin: { + vertical: 'top', + horizontal: 'right', + }, + }); + }; + + const showWarning = (message) => { + enqueueSnackbar(message, { + variant: 'warning', + anchorOrigin: { + vertical: 'top', + horizontal: 'right', + }, + }); + }; + + const showError = (message) => { + enqueueSnackbar(message, { + variant: 'error', + anchorOrigin: { + vertical: 'top', + horizontal: 'right', + }, + }); + }; + + // More examples: + const showCustom = (message, variant = 'default') => { + enqueueSnackbar(message, { + variant: variant, // could be default, error, success, warning, info, etc. + anchorOrigin: { + vertical: 'bottom', + horizontal: 'left', + }, + }); + }; + + return { showSuccess, showInfo, showWarning, showError, showCustom }; +} diff --git a/src/context/hooks/useVisibility.jsx b/src/context/hooks/useVisibility.jsx new file mode 100644 index 0000000..4b73776 --- /dev/null +++ b/src/context/hooks/useVisibility.jsx @@ -0,0 +1,27 @@ +import { useState, useCallback } from 'react'; + +export const useVisibility = () => { + const [isCollectionVisible, setCollectionVisibility] = useState(false); + const [dialogStates, setDialogStates] = useState({ + isAddCollectionDialogOpen: false, + isSelectionErrorDialogOpen: false, + }); + + const toggleCollectionVisibility = useCallback(() => { + setCollectionVisibility((isVisible) => !!isVisible); + }, []); + + const toggleDialog = useCallback((dialogName) => { + setDialogStates((prevState) => ({ + ...prevState, + [dialogName]: !prevState[dialogName], + })); + }, []); + + return { + isCollectionVisible, + toggleCollectionVisibility, + dialogStates, + toggleDialog, + }; +}; diff --git a/src/context/index.js b/src/context/index.js index 940c2b1..795336b 100644 --- a/src/context/index.js +++ b/src/context/index.js @@ -13,13 +13,14 @@ export { useAppContext } from './MISC_CONTEXT/AppContext/AppContextProvider'; export { usePopoverContext } from './UTILITIES_CONTEXT/PopoverContext/PopoverContext'; // export { useCronJobContext } from './SECONDARY_CONTEXT/CronJobContext/CronJobContext'; export { useStatisticsStore } from './SECONDARY_CONTEXT/StatisticsContext/StatisticsContext'; -export { useCardImages } from './MISC_CONTEXT/CardImagesContext/CardImagesContext'; +// export { useCardImages } from '../assets/currentlyUnused/CardImagesContext/CardImagesContext'; export { useAuthContext } from './MAIN_CONTEXT/AuthContext/authContext'; -export { usePageContext } from './UTILITIES_CONTEXT/PageContext/PageContext'; +// export { usePageContext } from './UTILITIES_CONTEXT/PageContext/PageContext'; export { useFormContext } from './UTILITIES_CONTEXT/FormContext/FormContext'; export { useMode } from './UTILITIES_CONTEXT/ColorModeContext/useMode'; export { useConfiguratorContext } from './UTILITIES_CONTEXT/ConfiguratorContext/ConfiguratorContext'; -export { useVisibilityContext } from './UTILITIES_CONTEXT/VisibilityContext'; +// export { useVisibilityContext } from './UTILITIES_CONTEXT/VisibilityContext'; +// export { useSnackbarContext } from './UTILITIES_CONTEXT/SnackbarContext/SnackbarContext'; // Contexts export { default as ErrorBoundary } from './ErrorBoundary'; @@ -37,11 +38,12 @@ export { ColorModeProvider } from './UTILITIES_CONTEXT/ColorModeContext/ColorMod export { SidebarProvider } from './UTILITIES_CONTEXT/SideBarContext/SideBarProvider'; export { ChartProvider } from './MAIN_CONTEXT/ChartContext/ChartContext'; export { AppContextProvider } from './MISC_CONTEXT/AppContext/AppContextProvider'; -export { PopoverProvider } from './UTILITIES_CONTEXT/PopoverContext/PopoverContext'; +// export { PopoverProvider } from './UTILITIES_CONTEXT/PopoverContext/PopoverContext'; // export { CronJobProvider } from './SECONDARY_CONTEXT/CronJobContext/CronJobContext'; export { StatisticsProvider } from './SECONDARY_CONTEXT/StatisticsContext/StatisticsContext'; export { FormProvider } from './UTILITIES_CONTEXT/FormContext/FormContext'; -export { PageProvider } from './UTILITIES_CONTEXT/PageContext/PageContext'; -export { CardImagesProvider } from './MISC_CONTEXT/CardImagesContext/CardImagesContext'; +// export { PageProvider } from '../assets/currentlyUnused/PageContext/PageContext'; +// export { CardImagesProvider } from '../assets/currentlyUnused/CardImagesContext/CardImagesContext'; export { ConfiguratorProvider } from './UTILITIES_CONTEXT/ConfiguratorContext/ConfiguratorContext'; -export { VisibilityProvider } from './UTILITIES_CONTEXT/VisibilityContext'; +// export { VisibilityProvider } from './UTILITIES_CONTEXT/VisibilityContext'; +// export { SnackbarContextProvider } from './UTILITIES_CONTEXT/SnackbarContext/SnackbarContext'; diff --git a/src/context/simplified_constants.jsx b/src/context/simplified_constants.jsx new file mode 100644 index 0000000..fdafce9 --- /dev/null +++ b/src/context/simplified_constants.jsx @@ -0,0 +1,17 @@ +import card_info from '../data/initialCardData.jsx'; +import user from './user.jsx'; + +const defaultCardData = card_info.data; +const defaultCard = defaultCardData[0]; +const defaultCards = Array(5).fill(defaultCard); // More efficient way to create an array with default values + +// const defaultUserData = user.data; +// const defaultUser = defaultUserData[0]; +// const defaultUsers = Array(5).fill(defaultUser); // More efficient way to create an array with default values + +export const defaultValues = { + defaultCard, + defaultCards, + // defaultUser, + // defaultUsers, +}; diff --git a/src/data/baseMenuItems.jsx b/src/data/baseMenuItems.jsx new file mode 100644 index 0000000..58a555b --- /dev/null +++ b/src/data/baseMenuItems.jsx @@ -0,0 +1,72 @@ +// menuItemsData.js + +import React from 'react'; +import { + Home as HomeIcon, + Store as StoreIcon, + ShoppingCart as CartIcon, + Assessment as CollectionIcon, + Person as ProfileIcon, +} from '@mui/icons-material'; +import DeckBuilderIcon from '../layout/REUSABLE_COMPONENTS/icons/DeckBuilderIcon'; +import { Badge } from '@mui/material'; +import MenuIcon from '@mui/icons-material/Menu'; + +export const baseMenuItems = ({ cartCardQuantity }) => [ + // { + // name: 'Menu', + // icon: , + // to: '/', + // requiresLogin: false, + // }, + // { name: 'Home', icon: , to: '/home', requiresLogin: false }, + { + name: 'Deck', + icon: , + to: '/deckbuilder', + requiresLogin: false, + }, + { + name: 'Collection', + icon: , + to: '/collection', + requiresLogin: true, + }, + { + name: 'Store', + icon: , + to: '/store', + requiresLogin: true, + }, + { + name: 'Cart', + icon: ( + + + + ), + to: '/cart', + requiresLogin: true, + }, + { + name: 'Profile', + icon: , + to: '/profile', + requiresLogin: false, + }, +]; diff --git a/src/assets/data/card_info.json b/src/data/card_info.json similarity index 100% rename from src/assets/data/card_info.json rename to src/data/card_info.json diff --git a/src/assets/data/card_sample.json b/src/data/card_sample.json similarity index 100% rename from src/assets/data/card_sample.json rename to src/data/card_sample.json diff --git a/src/layout/store/data/collectionPortfolioData.jsx b/src/data/collectionPortfolioData.jsx similarity index 64% rename from src/layout/store/data/collectionPortfolioData.jsx rename to src/data/collectionPortfolioData.jsx index fd61012..1a28d2a 100644 --- a/src/layout/store/data/collectionPortfolioData.jsx +++ b/src/data/collectionPortfolioData.jsx @@ -106,41 +106,5 @@ export default function prepareTableData(selectedCards) { [selectedCards] ); - // You don't need to return PaginationComponent, page, and rowsPerPage anymore - // since react-table handles pagination internally. You just need to provide data and columns. return { columns, data }; } - -// export default function data(selectedCards) { -// const roundToNearestTenth = (value) => Math.round(value * 10) / 10; -// const { -// data, -// PaginationComponent, // Assume this is your custom pagination component -// page, -// rowsPerPage, -// } = usePagination(selectedCards, 10, selectedCards?.length || 0); - -// const rows = data?.map((card, index) => ({ -// // Assuming each card has a unique 'id' for key, or use index as fallback -// key: card.id || `row-${index}`, -// name: , -// price: , -// tPrice: , -// quantity: , -// action: , -// })); - -// return { -// columns: [ -// { Header: 'name', accessor: 'name', width: '30%', align: 'left' }, -// { Header: 'price', accessor: 'price', align: 'left' }, -// { Header: 'total price', accessor: 'tPrice', align: 'center' }, -// { Header: 'quantity', accessor: 'quantity', align: 'center' }, -// { Header: 'action', accessor: 'action', align: 'center' }, -// ], -// rows, -// PaginationComponent, -// page, -// rowsPerPage, -// }; -// } diff --git a/src/data/iconData.jsx b/src/data/iconData.jsx new file mode 100644 index 0000000..fddae08 --- /dev/null +++ b/src/data/iconData.jsx @@ -0,0 +1,219 @@ +import AddCircleOutlineOutlined from '@mui/icons-material/AddCircleOutlineOutlined'; +import RemoveCircleOutlineOutlined from '@mui/icons-material/RemoveCircleOutlineOutlined'; +import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined'; +import { + FaDragon, + FaLevelUpAlt, + FaRegLightbulb, + FaShieldAlt, + FaVenusMars, +} from 'react-icons/fa'; +import AdjustSharpIcon from '@mui/icons-material/AdjustSharp'; +import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; +import { FaGithub, FaExternalLinkAlt } from 'react-icons/fa'; +import CloseIcon from '@mui/icons-material/Close'; +import LoginIcon from '@mui/icons-material/Login'; +import PersonAddIcon from '@mui/icons-material/PersonAdd'; +import SettingsIcon from '@mui/icons-material/Settings'; +import SaveIcon from '@mui/icons-material/Save'; +import AddIcon from '@mui/icons-material/Add'; +import LockIcon from '@mui/icons-material/Lock'; +import SearchIcon from '@mui/icons-material/Search'; +import DeleteIcon from '@mui/icons-material/Delete'; +import MonetizationOnIcon from '@mui/icons-material/MonetizationOn'; +import StackedLineChartRoundedIcon from '@mui/icons-material/StackedLineChartRounded'; +import FormatListNumberedRoundedIcon from '@mui/icons-material/FormatListNumberedRounded'; +import TrendingUpIcon from '@mui/icons-material/TrendingUp'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import CollectionsIcon from '@mui/icons-material/Collections'; +import { KeyboardArrowLeft, KeyboardArrowRight } from '@mui/icons-material'; +import AppsIcon from '@mui/icons-material/Apps'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import EditIcon from '@mui/icons-material/Edit'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import BarChartIcon from '@mui/icons-material/BarChart'; +import SelectAllIcon from '@mui/icons-material/SelectAll'; +import MenuIcon from '@mui/icons-material/Menu'; +import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; + +const iconData = [ + { + id: 1, + icon: , + component: 'ReusableLoadingButton', + }, + { + id: 2, + icon: , + component: 'AddCircleOutlineOutlined', + }, + { + id: 3, + icon: , + component: 'RemoveCircleOutlineOutlined', + }, + { + id: 4, + icon: , + component: 'CheckCircleOutlineOutlinedIcon', + }, + { + id: 5, + icon: , + component: 'FaDragon', + }, + { + id: 6, + icon: , + component: 'FaLevelUpAlt', + }, + { + id: 7, + icon: , + component: 'FaRegLightbulb', + }, + { + id: 8, + icon: , + component: 'FaShieldAlt', + }, + { + id: 9, + icon: , + component: 'FaVenusMars', + }, + { + id: 10, + icon: , + component: 'LockOutlinedIcon', + }, + { + id: 11, + icon: , + component: 'FaGithub', + }, + { + id: 12, + icon: , + component: 'FaExternalLinkAlt', + }, + { + id: 13, + icon: , + component: 'CloseIcon', + }, + { + id: 14, + icon: , + component: 'LoginIcon', + }, + { + id: 15, + icon: , + component: 'PersonAddIcon', + }, + { + id: 16, + icon: , + component: 'AddIcon', + }, + { + id: 17, + icon: , + component: 'SettingsIcon', + }, + { + id: 18, + icon: , + component: 'SaveIcon', + }, + { + id: 19, + icon: , + component: 'LockIcon', + }, + { + id: 20, + icon: , + component: 'SearchIcon', + }, + { + id: 21, + icon: , + component: 'DeleteIcon', + }, + { + id: 22, + icon: , + component: 'MonetizationOnIcon', + }, + { + id: 23, + icon: , + component: 'StackedLineChartRoundedIcon', + }, + { + id: 24, + icon: , + component: 'FormatListNumberedRoundedIcon', + }, + { + id: 25, + icon: , + component: 'TrendingUpIcon', + }, + { + id: 26, + icon: , + component: 'ArrowBackIcon', + }, + { + id: 27, + icon: , + component: 'CollectionsIcon', + }, + { + id: 28, + icon: , + component: 'KeyboardArrowLeft', + }, + { + id: 29, + icon: , + component: 'KeyboardArrowRight', + }, + { + id: 30, + icon: , + component: 'AppsIcon', + }, + { + id: 31, + icon: , + component: 'MoreVertIcon', + }, + { + id: 32, + icon: , + component: 'ListItemIcon', + }, + { + id: 33, + icon: , + component: 'EditIcon', + }, + { + id: 34, + icon: , + component: 'VisibilityIcon', + }, + { + id: 35, + icon: , + component: 'BarChartIcon', + }, +]; + +export default iconData; diff --git a/src/data/initialCardData.jsx b/src/data/initialCardData.jsx new file mode 100644 index 0000000..8640e2b --- /dev/null +++ b/src/data/initialCardData.jsx @@ -0,0 +1,155 @@ +import placeholder from '../assets/images/placeholder.jpeg'; +// const initialCardData = { +// archetype: [], +// atk: 3000, +// attribute: 'DARK', +// card_images: [ +// /* array of card image objects */ +// ], +// card_prices: [ +// /* array of card price objects */ +// ], +// card_set: { +// set_name: 'Ghosts From the Past: The 2nd Haunting', +// set_code: 'GFP2-EN125', +// set_rarity: 'Ultra Rare', +// set_rarity_code: '(UR)', +// set_price: { +// /* set price details */ +// }, +// }, +// card_sets: [ +// /* array of card set objects */ +// ], +// chart_datasets: [ +// /* array of chart data set objects */ +// ], +// collectionId: '658cf52f32298ed0e5a25e5a', +// // dailyPriceHistory tracks a card's price history by saving one price and timestamp every 24 hours +// dailyPriceHistory: [ +// { +// num: 1.03, +// timestamp: '2023-12-20T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec315a', +// }, +// { +// num: 1.21, +// timestamp: '2023-12-21T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec315a', +// }, +// { +// num: 1.17, +// timestamp: '2023-12-22T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec315a', +// }, +// { +// num: 1.18, +// timestamp: '2023-12-23T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec315a', +// }, +// { +// num: 1.27, +// timestamp: '2023-12-24T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec315a', +// }, +// { +// num: 1.4, +// timestamp: '2023-12-25T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec315a', +// }, +// { +// num: 1.49, +// timestamp: '2023-12-26T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec315a', +// }, +// { +// num: 1.7, +// timestamp: '2023-12-27T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec315a', +// }, +// { +// num: 1.73, +// timestamp: '2023-12-28T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec315a', +// }, +// ], +// dataOfLastPriceUpdate: '2023-12-28T05:10:42.524Z', +// def: 2500, +// // eslint-disable-next-line max-len +// desc: '"Dark Magician" + 1 Dragon monster\nThis card\'s name becomes "Dark Magician" while on the field or in the GY. Your opponent cannot target Spells/Traps you control with card effects, also they cannot be destroyed by your opponent\'s card effects.', +// frameType: 'fusion', +// id: '41721210', +// image: 'https://images.ygoprodeck.com/images/cards/41721210.jpg', +// lastSavedPrice: { +// num: 1.03, +// timestamp: '2023-12-28T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec3158', +// }, +// latestPrice: { +// num: 1.03, +// timestamp: '2023-12-28T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec3157', +// }, +// level: 8, +// name: 'Dark Magician the Dragon Knight', +// price: 1.03, +// priceHistory: [ +// { +// num: 1.03, +// timestamp: '2023-12-28T05:10:42.524Z', +// _id: '658d0676dc5934dc44ec3159', +// }, +// ], +// quantity: 1, +// race: 'Dragon', +// rarity: 'Ultra Rare', +// tag: '', +// totalPrice: 1.03, +// type: 'Fusion Monster', +// watchList: false, +// __v: 0, +// _id: '658d0676dc5934dc44ec3156', +// }; + +const initialCardData = { + data: [ + { + id: 999999, + name: 'Default Card', + type: 'Spell Card', + frameType: 'spell', + desc: 'During each of your Standby Phases, put 1 A-Counter on 1 face-up monster your opponent controls.', + race: 'Continuous', + archetype: 'Alien', + card_sets: [ + { + set_name: 'Force of the Breaker', + set_code: 'FOTB-EN043', + set_rarity: 'Common', + set_rarity_code: '(C)', + set_price: '1.31', + }, + ], + image: placeholder, + card_images: [ + { + id: 999999, + image_url: placeholder, + image_url_small: placeholder, + image_url_cropped: placeholder, + }, + ], + card_prices: [ + { + cardmarket_price: '0.02', + tcgplayer_price: '0.19', + ebay_price: '0.99', + amazon_price: '24.45', + coolstuffinc_price: '0.25', + }, + ], + }, + ], +}; + +export default initialCardData; diff --git a/src/data/messages.json b/src/data/messages.json new file mode 100644 index 0000000..c579e71 --- /dev/null +++ b/src/data/messages.json @@ -0,0 +1,29 @@ +{ + "specific": {}, + "general": { + "loading": { + "primary": "Loading...", + "sub": "Please wait while we fetch your data." + }, + "success": { + "dataFetch": { + "primary": "Success!", + "sub": "The data was fetched successfully." + }, + "formSubmit": { + "primary": "Submitted!", + "sub": "Your information has been successfully submitted." + } + }, + "error": { + "networkError": { + "primary": "Network Error", + "sub": "Please check your internet connection and try again." + }, + "formError": { + "primary": "Submission Error", + "sub": "There was an issue submitting your form. Please try again." + } + } + } +} diff --git a/src/context/MAIN_CONTEXT/CollectionContext/nivoTestData.json b/src/data/nivoTestData.json similarity index 100% rename from src/context/MAIN_CONTEXT/CollectionContext/nivoTestData.json rename to src/data/nivoTestData.json diff --git a/src/assets/data/pages.json b/src/data/pages.json similarity index 100% rename from src/assets/data/pages.json rename to src/data/pages.json diff --git a/src/layout/store/data/searchData.jsx b/src/data/searchData.jsx similarity index 52% rename from src/layout/store/data/searchData.jsx rename to src/data/searchData.jsx index d7e77de..3f84dde 100644 --- a/src/layout/store/data/searchData.jsx +++ b/src/data/searchData.jsx @@ -1,9 +1,40 @@ import Icon from '@mui/material/Icon'; // Images import MDTypography from '../MDTYPOGRAPHY/MDTypography'; -import React from 'react'; -import StoreItem from '../../../components/grids/gridItems/StoreItem'; +import React, { memo } from 'react'; import LoadingIndicator from '../../../components/reusable/indicators/LoadingIndicator'; +import GenericCard from '../components/cards/GenericCard'; +import { Container } from '@mui/system'; +import { Box } from '@mui/material'; +const SearchItem = memo(({ card, context, page, index }) => { + return ( + + + + + + ); +}); + +SearchItem.displayName = 'SearchItem'; export default function prepareTableData(selectedCards) { if (!selectedCards) return ; @@ -18,14 +49,12 @@ export default function prepareTableData(selectedCards) { accessor: 'card', id: 'card', Cell: ({ card, pageContext, index }) => ( - + ), }, ], [] ); - - // Map selectedCards to rows for react-table const data = React.useMemo( () => selectedCards.map((card) => ({ @@ -35,8 +64,5 @@ export default function prepareTableData(selectedCards) { })), [selectedCards] ); - - // You don't need to return PaginationComponent, page, and rowsPerPage anymore - // since react-table handles pagination internally. You just need to provide data and columns. return { columns, data }; } diff --git a/src/index.js b/src/index.js index dc4bb70..de806e2 100644 --- a/src/index.js +++ b/src/index.js @@ -2,16 +2,56 @@ import React, { StrictMode, useMemo } from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter as Router } from 'react-router-dom'; import App from './App'; +import * as serviceWorker from './serviceWorker'; // ==============================|| REACT DOM RENDER ||============================== // -import { AuthProvider, ColorModeProvider, PageProvider } from './context'; +import { AuthProvider, ColorModeProvider } from './context'; import { loadStripe } from '@stripe/stripe-js'; import { Elements } from '@stripe/react-stripe-js'; import { Helmet } from 'react-helmet'; +import { SnackbarProvider } from 'notistack'; const domNode = document.getElementById('root'); +const HelmetMetadata = () => ( + + {/* Basic */} + Enhanced Cardstore + + + + + + {/* SEO */} + + + {/* Social Media */} + + + + + + + {/* Responsive and mobile */} + + + {/* Additional links and styles */} + + + +); + const AppWrapper = () => { const stripePromise = useMemo( () => loadStripe(process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY), @@ -19,56 +59,18 @@ const AppWrapper = () => { ); return ( - // - - {/* Basic */} - Enhanced Cardstore - - - - - {/* SEO */} - - {/* Social Media */} - - - - - - {/* Responsive and mobile */} - - {/* Additional links and styles */} - - - + - + - + - // ); }; diff --git a/src/components/cards/AnimatedFeatureCard.jsx b/src/layout/AnimatedFeatureCard.jsx similarity index 66% rename from src/components/cards/AnimatedFeatureCard.jsx rename to src/layout/AnimatedFeatureCard.jsx index 5a22174..7a2b15b 100644 --- a/src/components/cards/AnimatedFeatureCard.jsx +++ b/src/layout/AnimatedFeatureCard.jsx @@ -6,9 +6,11 @@ import { CardListItem, CardUnorderedList, FeatureCard, -} from '../../pages/pageStyles/StyledComponents'; -import { useMode } from '../../context'; -import MDButton from '../../layout/REUSABLE_COMPONENTS/MDBUTTON'; +} from '../pages/pageStyles/StyledComponents'; +import { useMode } from '../context'; +import MDButton from './REUSABLE_COMPONENTS/MDBUTTON'; +import SimpleButton from './REUSABLE_COMPONENTS/unique/SimpleButton'; +import uniqueTheme from './REUSABLE_COMPONENTS/unique/uniqueTheme'; const AnimatedBox = animated(Box); @@ -53,7 +55,10 @@ export const AnimatedFeatureCard = ({ tier, onOpenModal }) => { subheader={tier.subheader} titleTypographyProps={{ align: 'center' }} subheaderTypographyProps={{ align: 'center' }} - sx={{ backgroundColor: theme.palette.backgroundA.dark }} + sx={{ + backgroundColor: theme.palette.backgroundA.dark, + height: '20%', + }} /> @@ -66,36 +71,37 @@ export const AnimatedFeatureCard = ({ tier, onOpenModal }) => { - onOpenModal(tier.title)} sx={{ - color: theme.palette.backgroundA.contrastText, - background: theme.palette.backgroundF.darker, - borderColor: theme.palette.backgroundB.darkest, - borderWidth: 2, - mt: 'auto', flexGrow: 1, justifySelf: 'bottom', bottom: 0, width: '100%', + mt: 'auto', + borderColor: theme.palette.chartTheme.greenAccent.darker, + borderWidth: 2, '&:hover': { - color: theme.palette.backgroundA.contrastTextC, - background: theme.palette.backgroundF.default, + borderColor: theme.palette.chartTheme.greenAccent.dark, }, }} > - {tier.buttonText} - + Manage {tier.title} + diff --git a/src/layout/CardChart.jsx b/src/layout/CardChart.jsx new file mode 100644 index 0000000..21d9e36 --- /dev/null +++ b/src/layout/CardChart.jsx @@ -0,0 +1,493 @@ +// import React, { useState, useEffect, useMemo } from 'react'; +// import { +// Box, +// Card, +// CardActions, +// CardContent, +// CardHeader, +// IconButton, +// List, +// ListItem, +// Paper, +// Typography, +// useMediaQuery, +// } from '@mui/material'; +// import MoreVertIcon from '@mui/icons-material/MoreVert'; +// import CardLinearChart from './CardLinearChart'; +// import { ErrorBoundary, useMode, usePageContext } from '../context'; +// import useCardCronJob from './useCardCronJob'; +// import initialCardData from '../data/initialCardData'; +// import { format } from 'date-fns'; +// import LoadingCardAnimation from '../assets/animations/LoadingCardAnimation'; +// import styled from 'styled-components'; +// import MDButton from './REUSABLE_COMPONENTS/MDBUTTON'; +// import { useLoading } from '../context/hooks/useLoading'; + +// const ChartArea = styled(Box)(({ theme }) => ({ +// width: '100%', +// height: '100%', +// padding: theme.spacing(2), +// display: 'flex', +// alignItems: 'center', +// justifyContent: 'center', +// border: '1px solid #000', +// borderRadius: '5px', +// })); +// const SquareChartContainer = styled(Box)(({ theme }) => ({ +// position: 'relative', +// width: '100%', +// paddingTop: '100%', +// overflow: 'hidden', +// '& > *': { +// position: 'absolute', +// top: 0, +// left: 0, +// right: 0, +// bottom: 0, +// }, +// })); + +// const CardChart = ({ cardData = initialCardData }) => { +// // STYLING AND MEDIA QUERY HOOKS +// const { theme } = useMode(); +// const isLgUp = useMediaQuery(theme.breakpoints.up('lg')); +// const [imageUrl, setImageUrl] = useState(null); +// const { startUpdates, pauseUpdates, resetData } = +// useCardCronJob(initialCardData); +// const formatTimestamp = (timestamp) => +// format(new Date(timestamp), "MMM do, yyyy 'at' HH:mm"); +// const [chartDimensions, setChartDimensions] = useState({ +// width: 0, +// height: 0, +// }); +// const { returnDisplay } = usePageContext(); +// const { isLoading } = useLoading(); +// useEffect(() => { +// if (cardData?.imageUrl) { +// console.log('Setting image url', cardData?.imageUrl); +// setImageUrl(cardData?.image); +// } +// }, [cardData?.imageUrl]); + +// const nivoReadyData = useMemo( +// () => [ +// { +// id: cardData?.name || 'default', +// data: cardData?.dailyPriceHistory?.map(({ timestamp, num }) => ({ +// x: timestamp, +// y: num, +// })), +// }, +// ], +// [cardData] +// ); +// const renderLoadingAnimation = () => +// isLgUp && ; +// useEffect(() => { +// if (isLoading('fetchCollections')) { +// console.log('Fetching collections'); +// } +// }, [isLoading('fetchCollections')]); +// useEffect(() => { +// const updateDimensions = () => { +// const width = window.innerWidth < 500 ? window.innerWidth : 500; +// const height = 300; +// setChartDimensions({ width, height }); +// }; + +// window.addEventListener('resize', updateDimensions); +// updateDimensions(); + +// return () => { +// window.removeEventListener('resize', updateDimensions); +// }; +// }, []); +// const renderHeaderWithAnimation = () => { +// return ( +// +// +// +// +// } +// title="Card Cron Job Simulator" +// subheader={cardData?.name || 'Card Name'} +// sx={{ +// padding: theme.spacing(1), +// margin: theme.spacing(1), +// }} +// /> +// {isLgUp && renderLoadingAnimation()} +// +// ); +// }; +// return ( +// +// +// +// +// {renderHeaderWithAnimation()} +// + +// +// +// {isLoading('fetchCollections') ? ( +// returnDisplay() +// ) : ( +// +// +// +// )} +// +// +// + +// +// +// {['Start Updates', 'Pause Updates', 'Reset Data'].map( +// (text, index) => ( +// { +// if (text === 'Start Updates') startUpdates(); +// else if (text === 'Pause Updates') pauseUpdates(); +// else if (text === 'Reset Data') resetData(); +// }} +// color="primary" +// variant="contained" +// sx={{ +// color: theme.palette.backgroundA.contrastText, +// background: theme.palette.backgroundF.darker, +// borderColor: theme.palette.backgroundB.darkest, +// borderWidth: 2, +// mt: 'auto', +// flexGrow: 1, +// justifySelf: 'bottom', +// bottom: 0, +// width: '100%', +// '&:hover': { +// color: theme.palette.backgroundA.contrastTextC, +// fontWeight: 'bold', +// background: theme.palette.backgroundF.dark, +// borderColor: theme.palette.backgroundB.darkest, +// border: `1px solid ${theme.palette.backgroundB.darkest}`, +// }, +// }} +// > +// {text} +// +// ) +// )} +// +// +// +// +// {cardData?.dailyPriceHistory?.map((entry, index) => ( +// +// +// Quantity: {cardData?.quantity} +// +// +// Price: ${entry?.num} +// +// +// {formatTimestamp(entry?.timestamp)} +// +// +// ))} +// +// +// +// +// +// +// ); +// }; + +// export default CardChart; +import React, { useState, useEffect, useMemo } from 'react'; +import { + Box, + Card, + CardContent, + CardHeader, + IconButton, + List, + ListItem, + Paper, + Typography, + useTheme, + useMediaQuery, + CardActions, +} from '@mui/material'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; +import CardLinearChart from './CardLinearChart'; +import { ErrorBoundary, useMode } from '../context'; +import useCardCronJob from './useCardCronJob'; +import initialCardData from '../data/initialCardData'; +import { format } from 'date-fns'; +import LoadingCardAnimation from '../assets/animations/LoadingCardAnimation'; +import MDButton from './REUSABLE_COMPONENTS/MDBUTTON'; +import { useLoading } from '../context/hooks/useLoading'; +import styled from 'styled-components'; +import uniqueTheme from './REUSABLE_COMPONENTS/unique/uniqueTheme'; +import SimpleButton from './REUSABLE_COMPONENTS/unique/SimpleButton'; + +const ChartArea = styled(Box)(({ theme }) => ({ + width: '100%', + height: '100%', + padding: theme.spacing(2), + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + border: '1px solid #000', + borderRadius: '5px', +})); + +const SquareChartContainer = styled(Box)({ + position: 'relative', + flex: 1, + overflow: 'hidden', +}); + +const CardChart = () => { + const { theme } = useMode(); + const isMobileView = useMediaQuery(theme.breakpoints.down('sm')); + const isLgUp = useMediaQuery(theme.breakpoints.up('lg')); + const { startUpdates, pauseUpdates, resetData } = + useCardCronJob(initialCardData); + const [cardData, setCardData] = useState(initialCardData); + const { isLoading } = useLoading(); + + const getResponsiveDimensions = () => { + return isMobileView + ? { width: window.innerWidth / 2, height: window.innerHeight * 0.5 } + : { width: 500, height: 300 }; + }; + + const [chartDimensions, setChartDimensions] = useState( + getResponsiveDimensions() + ); + + useEffect(() => { + const handleResize = () => { + setChartDimensions(getResponsiveDimensions()); + }; + + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, [isMobileView]); + + const nivoReadyData = useMemo( + () => [ + { + id: cardData.name || 'default', + data: cardData.dailyPriceHistory.map(({ timestamp, num }) => ({ + x: format(new Date(timestamp), 'Pp'), + y: num, + })), + }, + ], + [cardData] + ); + + return ( + + + + + + + ) + } + title="Card Cron Job Simulator" + subheader={cardData.name || 'Card Name'} + /> + {!isMobileView && isLgUp && isLoading('fetchCollections') && ( + + )} + + + + + + + + {isMobileView ? ( + + + + {cardData.dailyPriceHistory.map((entry, index) => ( + + + Quantity: {cardData.quantity} + + + Price: ${entry.num} + + + {format( + new Date(entry.timestamp), + "MMM do, yyyy 'at' HH:mm" + )} + + + ))} + + + + ) : ( + + {['Start Updates', 'Pause Updates', 'Reset Data'].map( + (action, index) => ( + { + if (action === 'Start Updates') startUpdates(); + else if (action === 'Pause Updates') pauseUpdates(); + else resetData(); + }} + > + {action} + + ) + )} + + )} + + + ); +}; + +export default CardChart; diff --git a/src/layout/CardLinearChart.jsx b/src/layout/CardLinearChart.jsx new file mode 100644 index 0000000..006513d --- /dev/null +++ b/src/layout/CardLinearChart.jsx @@ -0,0 +1,85 @@ +import { Box, Tooltip, Typography, useMediaQuery } from '@mui/material'; + +import { ResponsiveLine } from '@nivo/line'; +import { useCallback, useMemo, useState } from 'react'; +import { useMode } from '../context'; +import styled from 'styled-components'; +const ChartContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + height: 'auto', + [theme.breakpoints.down('sm')]: { + width: '150%', // Adjust width for mobile screens + height: '300px', // Adjust height for mobile screens + // transform: 'translateX(10%)', // Shift the chart to the right by 50% + }, +})); + +const parseDate = (dateString) => { + const date = new Date(dateString); + if (isNaN(date.getTime())) { + console.error(`Invalid date: ${dateString}`); + return null; // or a sensible default, or throw an error, depending on your needs + } + return date; +}; +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 CardLinearChart = ({ nivoReadyData, dimensions }) => { + const { theme } = useMode(); + const processedData = useMemo(() => { + return nivoReadyData?.map((series) => ({ + ...series, + data: series?.data?.map((point) => ({ + ...point, + x: parseDate(point?.x) || point?.x, + })), + })); + }, [nivoReadyData]); + + const chartProps = useMemo( + () => ({ + data: processedData, + margin: { top: 20, right: 20, bottom: 20, left: 35 }, + xScale: { + type: 'time', + format: 'time:%Y-%m-%dT%H:%M:%S.%LZ', + useUTC: false, + precision: 'second', + }, + axisBottom: { + tickRotation: 0, + legend: 'Time', + legendOffset: 36, + legendPosition: 'middle', + tickSize: 5, + tickPadding: 5, + tickValues: 'every 2 days', + format: '%b %d', + }, + enableSlices: 'x', + yScale: { type: 'linear', min: 'auto', max: 'auto' }, + }), + [nivoReadyData, processedData] + ); + + if (!processedData || !processedData?.length) { + return No data available; + } + return ( + + + + ); +}; + +export default CardLinearChart; diff --git a/src/components/reusable/indicators/ErrorIndicator.js b/src/layout/ErrorIndicator.js similarity index 97% rename from src/components/reusable/indicators/ErrorIndicator.js rename to src/layout/ErrorIndicator.js index 566d59e..7592742 100644 --- a/src/components/reusable/indicators/ErrorIndicator.js +++ b/src/layout/ErrorIndicator.js @@ -5,7 +5,7 @@ import Typography from '@mui/material/Typography'; import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; import Container from '@mui/material/Container'; import { Alert, AlertTitle } from '@mui/material'; -import { useMode } from '../../../context'; +import { useMode } from '../context'; // Styled components with theme utilization const StyledContainer = styled(Container)(({ theme }) => ({ diff --git a/src/components/reusable/indicators/LoadingIndicator.js b/src/layout/LoadingIndicator.js similarity index 100% rename from src/components/reusable/indicators/LoadingIndicator.js rename to src/layout/LoadingIndicator.js diff --git a/src/layout/LoadingOverlay.jsx b/src/layout/LoadingOverlay.jsx new file mode 100644 index 0000000..d7c6aa1 --- /dev/null +++ b/src/layout/LoadingOverlay.jsx @@ -0,0 +1,44 @@ +import React, { useEffect, useState } from 'react'; +import useTheme from '@material-ui/core/styles/useTheme'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; +import { CircularProgress, Box } from '@material-ui/core'; +import { useMode } from '../context'; + +const LoadingOverlay = () => { + const { theme } = useMode(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + const [isVisible, setIsVisible] = useState(true); + + useEffect(() => { + const timer = setTimeout(() => setIsVisible(false), 2000); + return () => clearTimeout(timer); + }, []); + + if (!isVisible) return null; + + return ( + + + + ); +}; + +export default LoadingOverlay; diff --git a/src/components/reusable/PrivateRoute.jsx b/src/layout/PrivateRoute.jsx similarity index 90% rename from src/components/reusable/PrivateRoute.jsx rename to src/layout/PrivateRoute.jsx index 7953c3d..c235a9b 100644 --- a/src/components/reusable/PrivateRoute.jsx +++ b/src/layout/PrivateRoute.jsx @@ -1,6 +1,6 @@ import React, { useContext, useEffect } from 'react'; import { Navigate, useNavigate } from 'react-router-dom'; -import { useAuthContext } from '../../context'; +import { useAuthContext } from '../context'; const PrivateRoute = ({ children }) => { const { isLoggedIn, user } = useAuthContext(); diff --git a/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx b/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx index 04f7fb8..ab1d9ac 100644 --- a/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx +++ b/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx @@ -4,26 +4,45 @@ import FlexBetween from './FlexBetween'; import PropTypes from 'prop-types'; import { useMode } from '../../context'; -const BoxHeader = ({ icon, title, subtitle, sideText }) => { +const BoxHeader = ({ + icon, + title, + subtitle, + sideText, + sx, + useSX, + titleVariant, + colorVariant, + paddingVariant, +}) => { const { theme } = useMode(); return ( {icon} - + {title} - {subtitle} + {subtitle !== 'none' && ( + {subtitle} + )} {sideText} diff --git a/src/layout/Containers/DashBoardLayout.jsx b/src/layout/REUSABLE_COMPONENTS/DashBoardLayout.jsx similarity index 93% rename from src/layout/Containers/DashBoardLayout.jsx rename to src/layout/REUSABLE_COMPONENTS/DashBoardLayout.jsx index 44af0a2..ecfc110 100644 --- a/src/layout/Containers/DashBoardLayout.jsx +++ b/src/layout/REUSABLE_COMPONENTS/DashBoardLayout.jsx @@ -1,7 +1,7 @@ import { useMode, useSidebarContext } from '../../context'; import { useLocation } from 'react-router-dom'; import PropTypes from 'prop-types'; -import MDBox from '../REUSABLE_COMPONENTS/MDBOX/index'; +import MDBox from './MDBOX/index'; function DashboardLayout({ children }) { const { pathname } = useLocation(); @@ -12,7 +12,7 @@ function DashboardLayout({ children }) { return ( ({ - p: 3, + // p: 3, position: 'relative', marginLeft: isSidebarOpen ? theme.functions.pxToRem(250) diff --git a/src/layout/Containers/GridLayout.jsx b/src/layout/REUSABLE_COMPONENTS/GridLayout.jsx similarity index 91% rename from src/layout/Containers/GridLayout.jsx rename to src/layout/REUSABLE_COMPONENTS/GridLayout.jsx index 747efe1..4ffec68 100644 --- a/src/layout/Containers/GridLayout.jsx +++ b/src/layout/REUSABLE_COMPONENTS/GridLayout.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { Container, Grid } from '@mui/material'; -import MDBox from '../REUSABLE_COMPONENTS/MDBOX'; +import MDBox from './MDBOX'; const GridLayout = ({ children, containerStyles }) => ( (props) => { -// const { enqueueSnackbar, closeSnackbar } = useSnackbar(); -// // const { handleSnackBar } = useSnackBar(); // Obtain enqueueSnackbar function from useSnackBar hook - -import { Snackbar, IconButton, Box, Grow } from '@mui/material'; -import { useSnackbar } from 'notistack'; -import { Close as CloseIcon } from '@mui/icons-material'; -import styled from 'styled-components'; - -// eslint-disable-next-line react/display-name -const withDynamicSnackbar = (WrappedComponent) => (props) => { - const { enqueueSnackbar, closeSnackbar } = useSnackbar(); - - const showSnackbar = (message, variant = 'success') => { - enqueueSnackbar(message, { - variant, - action: (key) => ( - closeSnackbar(key)}> - - - ), - }); - }; - - return ; -}; - -export { withDynamicSnackbar }; -// import { Transition } from 'react-transition-group'; -// import { Snackbar, IconButton, Box, Grow } from '@mui/material'; -// import { Close as CloseIcon } from '@mui/icons-material'; -// import styled from 'styled-components'; -// import CheckRoundedIcon from '@mui/icons-material/CheckRounded'; -// import { useMode } from '../../../context'; -// import { useSnackbar } from 'notistack'; - -// const DynamicSnackbar = ({ open, message, variant, onClose }) => { -// const { enqueueSnackbar, closeSnackbar } = useSnackbar(); - -// // Use enqueueSnackbar for showing messages -// React.useEffect(() => { -// if (open && message?.title) { -// enqueueSnackbar(message.title, { -// variant, -// action: (key) => ( -// closeSnackbar(key)}> -// -// -// ), -// }); -// } -// }, [open, message, variant, enqueueSnackbar, closeSnackbar]); - -// // Since snackbar management is now handled by notistack, the component itself doesn't need to render anything. -// return null; -// }; - -// export default DynamicSnackbar; -// import useSnackBar from '../../../context/hooks/useSnackBar'; - -// const DynamicSnackbar = ({ open, message, variant, onClose, loading }) => { -// const { theme } = useMode(); -// const { handleCloseSnackbar } = useSnackBar(); // Obtain handleCloseSnackbar function from useSnackBar hook - -// const handleClose = (_, reason) => { -// if (reason === 'clickaway') { -// return; -// } -// onClose(); -// }; - -// console.log('DynamicSnackbar:', open, message, variant, loading); -// return ( -// -// {/* */} -// -// {(status) => ( -// -// -//
-//

{message.title}

-//

{message.description}

-//
-// -// {' '} -// {/* Use handleCloseSnackbar instead of onClose */} -// -// -//
-// )} -//
-//
-// ); -// }; - -// DynamicSnackbar.displayName = 'DynamicSnackbar'; // Add display name - -// eslint-disable-next-line react/display-name -// HOC to provide snackbar functionality to wrapped components -// const withDynamicSnackbar = (WrappedComponent) => (props) => { -// const { enqueueSnackbar, closeSnackbar } = useSnackbar(); - -// // Function to show snackbar -// const showSnackbar = (message, variant = 'success') => { -// enqueueSnackbar(message, { -// variant, -// action: (key) => ( -// closeSnackbar(key)}> -// -// -// ), -// }); -// }; - -// return ; -// }; - -// const withDynamicSnackbar = (WrappedComponent) => (props) => { -// const { enqueueSnackbar, closeSnackbar } = useSnackbar(); -// // const { handleSnackBar } = useSnackBar(); // Obtain enqueueSnackbar function from useSnackBar hook - -// const [snackbarOpen, setSnackbarOpen] = React.useState(false); -// const [snackbarMessage, setSnackbarMessage] = React.useState({ -// title: '', -// description: '', -// }); -// const [snackbarVariant, setSnackbarVariant] = React.useState('success'); -// const [loading, setLoading] = React.useState(false); - -// const showSnackbar = (message, variant = 'success') => { -// console.log('showSnackbar:', message, variant); -// const destructuredMessage = { -// title: message.title, -// description: message.description, -// }; -// setSnackbarMessage(destructuredMessage); -// setSnackbarVariant(variant); -// setSnackbarOpen(true); -// }; - -// const hideSnackbar = () => { -// setSnackbarOpen(false); -// }; - -// return ( -// -// -// -// -// ); -// }; - -// export { withDynamicSnackbar }; - -// const positioningStyles = { -// entering: 'translateX(0)', -// entered: 'translateX(0)', -// exiting: 'translateX(500px)', -// exited: 'translateX(500px)', -// unmounted: 'translateX(500px)', -// }; - -const grey = { - 50: '#fafafa', - 100: '#f5f5f5', - 200: '#eeeeee', - 300: '#e0e0e0', - 400: '#bdbdbd', - 500: '#9e9e9e', - 600: '#757575', - 700: '#616161', - 800: '#424242', - 900: '#212121', -}; - -const StyledSnackbar = styled(Snackbar)( - ({ theme, variant }) => ` - position: fixed; - z-index: 5500; - display: flex; - bottom: 16px; - right: 16px; - max-width: 560px; - min-width: 300px; - gap: 8px; - overflow: hidden; - background-color: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'}; - border-radius: 8px; - border: 1px solid ${theme.palette.mode === 'dark' ? grey[700] : grey[200]}; - box-shadow: ${ - theme.palette.mode === 'dark' - ? '0 2px 16px rgba(0,0,0, 0.5)' - : `0 2px 16px ${grey[200]}` - }; - padding: 0.75rem; - color: ${theme.palette.mode === 'dark' ? grey[50] : grey[900]}; - font-family: 'IBM Plex Sans', sans-serif; - font-weight: 500; - text-align: start; - - & .snackbar-message { - flex: 1 1 0%; - max-width: 100%; - } - - & .snackbar-title { - margin: 0; - line-height: 1.5rem; - margin-right: 0.5rem; - } - - & .snackbar-description { - margin: 0; - line-height: 1.5rem; - font-weight: 400; - color: ${theme.palette.mode === 'dark' ? grey[400] : grey[800]}; - } - - & .snackbar-close-icon { - cursor: pointer; - flex-shrink: 0; - padding: 2px; - border-radius: 4px; - color: ${theme.palette.mode === 'dark' ? grey[50] : grey[900]}; - background: transparent; - - &:hover { - background: ${theme.palette.mode === 'dark' ? grey[800] : grey[50]}; - } - } - ` -); diff --git a/src/layout/REUSABLE_COMPONENTS/HOC/withDynamicSnackbar.jsx b/src/layout/REUSABLE_COMPONENTS/HOC/withDynamicSnackbar.jsx deleted file mode 100644 index 0e7098a..0000000 --- a/src/layout/REUSABLE_COMPONENTS/HOC/withDynamicSnackbar.jsx +++ /dev/null @@ -1,76 +0,0 @@ -import { Snackbar, IconButton, Box, Grow } from '@mui/material'; -import { useSnackbar } from 'notistack'; -import { Close as CloseIcon } from '@mui/icons-material'; - -// eslint-disable-next-line react/display-name -const withDynamicSnackbar = (WrappedComponent) => (props) => { - const { enqueueSnackbar, closeSnackbar } = useSnackbar(); - - const showSnackbar = (message, variant = 'success') => { - enqueueSnackbar(message, { - variant, - action: (key) => ( - closeSnackbar(key)}> - - - ), - }); - }; - - return ; -}; - -export { withDynamicSnackbar }; - -const positioningStyles = { - entering: 'translateX(0)', - entered: 'translateX(0)', - exiting: 'translateX(500px)', - exited: 'translateX(500px)', - unmounted: 'translateX(500px)', -}; - -// const [snackbarOpen, setSnackbarOpen] = React.useState(false); -// const [snackbarMessage, setSnackbarMessage] = React.useState({ -// title: '', -// description: '', -// }); -// const [snackbarVariant, setSnackbarVariant] = React.useState('success'); -// const [loading, setLoading] = React.useState(false); - -// const showSnackbar = (message, variant = 'success') => { -// console.log('showSnackbar:', message, variant); -// const destructuredMessage = { -// title: message.title, -// description: message.description, -// }; -// setSnackbarMessage(destructuredMessage); -// setSnackbarVariant(variant); -// setSnackbarOpen(true); -// }; - -// const hideSnackbar = () => { -// setSnackbarOpen(false); -// }; - -// return ( -// -// -// -// -// ); -// }; diff --git a/src/layout/REUSABLE_COMPONENTS/Icons.jsx b/src/layout/REUSABLE_COMPONENTS/Icons.jsx index 5cc02f0..70866c1 100644 --- a/src/layout/REUSABLE_COMPONENTS/Icons.jsx +++ b/src/layout/REUSABLE_COMPONENTS/Icons.jsx @@ -1,10 +1,10 @@ -const allIconsMap = { - // Add all icons here - 'attach-money': AttachMoneyIcon, - collections: CollectionsIcon, - 'attach-money': AttachMoneyIcon, - 'format-list-numbered': FormatListNumberedIcon, - 'trending-up': TrendingUpIcon, - 'pie-chart': PieChartIcon, - 'emoji-events': EmojiEventsIcon, -}; +// const allIconsMap = { +// // Add all icons here +// 'attach-money': AttachMoneyIcon, +// collections: CollectionsIcon, +// 'attach-money': AttachMoneyIcon, +// 'format-list-numbered': FormatListNumberedIcon, +// 'trending-up': TrendingUpIcon, +// 'pie-chart': PieChartIcon, +// 'emoji-events': EmojiEventsIcon, +// }; diff --git a/src/layout/REUSABLE_COMPONENTS/MDPROGRESS/MDProgress.jsx b/src/layout/REUSABLE_COMPONENTS/MDPROGRESS/MDProgress.jsx index 70dfad6..f457310 100644 --- a/src/layout/REUSABLE_COMPONENTS/MDPROGRESS/MDProgress.jsx +++ b/src/layout/REUSABLE_COMPONENTS/MDPROGRESS/MDProgress.jsx @@ -1,18 +1,3 @@ -/** -========================================================= -* Material Dashboard 2 React - v2.2.0 -========================================================= - -* Product Page: https://www.creative-tim.com/product/material-dashboard-react -* Copyright 2023 Creative Tim (https://www.creative-tim.com) - -Coded by www.creative-tim.com - - ========================================================= - -* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -*/ - import { forwardRef } from 'react'; // prop-types is a library for typechecking of props diff --git a/src/layout/collection/collectionGrids/cards-chart/NivoContainer.jsx b/src/layout/REUSABLE_COMPONENTS/NivoContainer.jsx similarity index 100% rename from src/layout/collection/collectionGrids/cards-chart/NivoContainer.jsx rename to src/layout/REUSABLE_COMPONENTS/NivoContainer.jsx diff --git a/src/pages/NotFoundPage.js b/src/layout/REUSABLE_COMPONENTS/NotFoundPage.js similarity index 100% rename from src/pages/NotFoundPage.js rename to src/layout/REUSABLE_COMPONENTS/NotFoundPage.js diff --git a/src/layout/Containers/PageLayout.jsx b/src/layout/REUSABLE_COMPONENTS/PageLayout.jsx similarity index 95% rename from src/layout/Containers/PageLayout.jsx rename to src/layout/REUSABLE_COMPONENTS/PageLayout.jsx index 1e0f8c3..f816cbe 100644 --- a/src/layout/Containers/PageLayout.jsx +++ b/src/layout/REUSABLE_COMPONENTS/PageLayout.jsx @@ -1,6 +1,6 @@ import { useLocation } from 'react-router-dom'; import PropTypes from 'prop-types'; -import MDBox from '../REUSABLE_COMPONENTS/MDBOX'; +import MDBox from './MDBOX'; import { Grid } from '@mui/material'; import { useMode } from '../../context'; diff --git a/src/layout/REUSABLE_COMPONENTS/ProgressCircle.jsx b/src/layout/REUSABLE_COMPONENTS/ProgressCircle.jsx index 9170135..b42d0fb 100644 --- a/src/layout/REUSABLE_COMPONENTS/ProgressCircle.jsx +++ b/src/layout/REUSABLE_COMPONENTS/ProgressCircle.jsx @@ -1,26 +1,96 @@ +/* eslint-disable max-len */ +import React from 'react'; import { Box, useTheme } from '@mui/material'; +import PropTypes from 'prop-types'; +import RCToolTip from './RCTOOLTIP/RCToolTip'; import { useMode } from '../../context'; +import useSkeletonLoader from '../collection/collectionGrids/cards-datatable/useSkeletonLoader'; -const ProgressCircle = ({ progress = '0.75', size = '40' }) => { - const { theme } = useMode(); - const colors = theme.palette.chartTheme; - const primary = colors.primary.default; - const blue = colors.blueAccent.default; - const green = colors.greenAccent.default; +const ProgressCircleSkeleton = ({ size = 120 }) => { + const theme = useTheme(); + const { theme: modeTheme } = useMode(); + const { SkeletonLoader } = useSkeletonLoader(); - const angle = progress * 360; return ( + > + + + ); +}; + +// ProgressCircle now expects an array of collections instead of a single progress value. +const ProgressCircle = ({ collections, size = 120 }) => { + const { theme } = useMode(); + + if (!collections || collections.length === 1) { + return ; + } + + // Calculate the total value of all collections. + const totalValue = collections?.reduce( + (sum, { totalPrice }) => sum + totalPrice, + 0 ); + + // Generate a conic-gradient background based on the collections' value distribution. + let cumulativePercentage = 0; + const background = collections + ?.reduce((gradient, collection) => { + const collectionPercentage = (collection?.totalPrice / totalValue) * 100; + const nextCumulativePercentage = + cumulativePercentage + collectionPercentage; + const color = theme.palette.chartTheme.blueAccent.default; // Color for each segment + gradient += `${color} ${cumulativePercentage}% ${nextCumulativePercentage}%,`; + cumulativePercentage = nextCumulativePercentage; + return gradient; + }, '') + .slice(0, -1); // Remove the trailing comma + + // Prepare tooltip content displaying the name and value of each collection. + const tooltipContent = collections + ?.map( + (collection) => + `${collection.name}: $${collection.totalPrice?.toFixed(2)} (${((collection.totalPrice / totalValue) * 100)?.toFixed(2)}%)` + ) + .join('\n'); + + return ( + + + + ); +}; + +ProgressCircle.propTypes = { + collections: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + totalPrice: PropTypes.number.isRequired, + }) + ).isRequired, + size: PropTypes.number, }; export default ProgressCircle; diff --git a/src/layout/REUSABLE_COMPONENTS/RC/RCChange.jsx b/src/layout/REUSABLE_COMPONENTS/RC/RCChange.jsx new file mode 100644 index 0000000..bc2bf94 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RC/RCChange.jsx @@ -0,0 +1,75 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Box, Typography } from '@mui/material'; +import ProgressCircle from '../ProgressCircle'; +import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import MDTypography from '../MDTYPOGRAPHY/MDTypography'; +import MDBox from '../MDBOX'; +import { useMode } from '../../../context'; + +const useStyles = (theme) => ({ + container: { + width: '100%', + p: '20px', + borderRadius: theme.spacing(4), + }, + flexBox: { + display: 'flex', + justifyContent: 'space-between', + }, + percentageText: { + variant: 'h6', + fontWeight: 'medium', + color: theme.palette.chartTheme.grey.default, + }, + rangeText: { + variant: 'h5', + color: theme.palette.chartTheme.greenAccent.default, + mt: '2px', + }, +}); + +const RCChange = ({ progress, increase, change, rangeLevel }) => { + const { theme } = useMode(); + const styles = useStyles(theme); + + return ( + + + + Change + + {increase ? : } + {`${change}%`} + + + {/* + + */} + + + {`In ${rangeLevel}`} + + + ); +}; + +RCChange.propTypes = { + progress: PropTypes.number.isRequired, + increase: PropTypes.bool.isRequired, + change: PropTypes.number.isRequired, + rangeLevel: PropTypes.string.isRequired, +}; + +export default RCChange; diff --git a/src/layout/REUSABLE_COMPONENTS/RCCARDLIST/RCCardList.jsx b/src/layout/REUSABLE_COMPONENTS/RCCARDLIST/RCCardList.jsx new file mode 100644 index 0000000..8e88d9c --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCCARDLIST/RCCardList.jsx @@ -0,0 +1,93 @@ +import { Link } from 'react-router-dom'; + +// prop-types is library for typechecking of props +import PropTypes from 'prop-types'; +import MDBox from '../MDBOX'; +import MDAvatar from '../MDAVATAR'; +import MDTypography from '../MDTYPOGRAPHY/MDTypography'; +import MDButton from '../MDBUTTON'; +import { Box, Card, Chip, Paper } from '@mui/material'; +import FlexBetween from '../FlexBetween'; + +function RCCardList({ title, items, shadow }) { + const renderCards = items.map( + ({ name, description, totalQuantity, action, image, cards }) => ( + + + + + + + {name} + + + {description} + + + + + + + + + {name} + + + + ) + ); + + return ( + + + + {title} + + + + + {renderCards} + + + + ); +} + +// Setting default props for the ProfilesList +RCCardList.defaultProps = { + shadow: true, +}; + +// Typechecking props for the ProfilesList +RCCardList.propTypes = { + title: PropTypes.string.isRequired, + decks: PropTypes.arrayOf(PropTypes.object).isRequired, + shadow: PropTypes.bool, +}; + +export default RCCardList; diff --git a/src/layout/REUSABLE_COMPONENTS/RCCARDLIST/RCCardListRoot.jsx b/src/layout/REUSABLE_COMPONENTS/RCCARDLIST/RCCardListRoot.jsx new file mode 100644 index 0000000..9afbcd2 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCCARDLIST/RCCardListRoot.jsx @@ -0,0 +1,97 @@ +// import { Link } from 'react-router-dom'; + +// // prop-types is library for typechecking of props +// import PropTypes from 'prop-types'; +// import MDBox from '../MDBOX'; +// import MDAvatar from '../MDAVATAR'; +// import MDTypography from '../MDTYPOGRAPHY/MDTypography'; +// import MDButton from '../MDBUTTON'; +// import { Card } from '@mui/material'; + +// function RCCardList({ title, profiles, shadow }) { +// const renderProfiles = profiles.map( +// ({ image, name, description, action }) => ( +// +// +// +// +// +// +// {name} +// +// +// {description} +// +// +// +// {action.type === 'internal' ? ( +// +// {action.label} +// +// ) : ( +// +// {action.label} +// +// )} +// +// +// ) +// ); + +// return ( +// +// +// +// {title} +// +// +// +// +// {renderProfiles} +// +// +// +// ); +// } + +// // Setting default props for the ProfilesList +// RCCardList.defaultProps = { +// shadow: true, +// }; + +// // Typechecking props for the ProfilesList +// RCCardList.propTypes = { +// title: PropTypes.string.isRequired, +// profiles: PropTypes.arrayOf(PropTypes.object).isRequired, +// shadow: PropTypes.bool, +// }; + +// export default RCCardList; diff --git a/src/layout/REUSABLE_COMPONENTS/RCHeader.jsx b/src/layout/REUSABLE_COMPONENTS/RCHeader.jsx new file mode 100644 index 0000000..45a092c --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCHeader.jsx @@ -0,0 +1,25 @@ +import { Typography, Box } from '@mui/material'; +import { useMode } from '../../context'; + +const RCHeader = ({ title, subtitle }) => { + const { theme } = useMode(); + const grey = theme.palette.chartTheme.grey.default; + const greenAccent = theme.palette.chartTheme.greenAccent.default; + return ( + + + {title} + + + {subtitle} + + + ); +}; + +export default RCHeader; diff --git a/src/layout/REUSABLE_COMPONENTS/RCICON/RCIcon.jsx b/src/layout/REUSABLE_COMPONENTS/RCICON/RCIcon.jsx new file mode 100644 index 0000000..1143021 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCICON/RCIcon.jsx @@ -0,0 +1,48 @@ +// import React from 'react'; +// import { styled } from '@mui/material/styles'; +// import * as MuiIcons from '@mui/icons-material'; +// import uniqueTheme from '../unique/uniqueTheme'; +// import { Box } from '@mui/material'; +// import { useMode } from '../../../context'; + +// const StyledIcon = styled(Box)(({ theme }) => ({ +// borderRadius: '50%', +// border: `1px solid ${theme.palette.chartTheme.primary.dark}`, +// padding: '5px', +// display: 'inline-flex', +// fontSize: '30px', +// alignItems: 'center', +// justifyContent: 'center', +// bgColor: theme.palette.chartTheme.primary.main, +// })); +// // function ColoredAvatars() { +// // return ( +// // +// // G +// // B +// // P +// // +// // ); +// // } + +// const RCIcon = ({ +// iconName, +// size = '30px', +// color = uniqueTheme.colorPrimary, +// }) => { +// const { theme } = useMode(); +// // Dynamically get the icon component; ensure it's a valid icon name +// const IconComponent = MuiIcons[iconName] || null; + +// if (!IconComponent) { +// return Icon not found; // Display error message if the icon is not found +// } + +// return ( +// +// +// +// ); +// }; + +// export default RCIcon; diff --git a/src/layout/REUSABLE_COMPONENTS/RCInfoItem.jsx b/src/layout/REUSABLE_COMPONENTS/RCInfoItem.jsx new file mode 100644 index 0000000..f93ac75 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCInfoItem.jsx @@ -0,0 +1,97 @@ +// import React from 'react'; +// import { Grid, CardContent, useMediaQuery } from '@mui/material'; +// import { useTheme } from '@mui/material/styles'; +// import MDTypography from '../REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; +// import { useMode } from '../../context'; + +// const RCInfoItem = ({ +// label, +// value, +// gridSizes = { xs: 12, sm: 6, md: 3 }, +// externalTheme = null, +// }) => { +// const { theme } = useMode(); +// const isMobileView = useMediaQuery(theme.breakpoints.down('sm')); + +// return ( +// +// +// +// {`${label}:`} +// +// +// {value} +// +// +// +// ); +// }; + +// export default RCInfoItem; +import React from 'react'; +import { Grid, CardContent, useMediaQuery, Skeleton } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; +import MDTypography from '../REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; +import { useMode } from '../../context'; + +const RCInfoItem = ({ + label, + value, + gridSizes = { xs: 12, sm: 6, md: 3 }, + externalTheme = null, +}) => { + const { theme } = useMode(); + const isMobileView = useMediaQuery(theme.breakpoints.down('sm')); + + return ( + + + {label !== undefined && value !== undefined ? ( + <> + + {`${label}:`} + + + {value} + + + ) : ( + <> + + + + )} + + + ); +}; + +export default RCInfoItem; diff --git a/src/layout/REUSABLE_COMPONENTS/RCLOGOSECTION/RCLogoSection.jsx b/src/layout/REUSABLE_COMPONENTS/RCLOGOSECTION/RCLogoSection.jsx new file mode 100644 index 0000000..b2f7bb5 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCLOGOSECTION/RCLogoSection.jsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { ButtonBase, Typography, Avatar } from '@mui/material'; +import { Link, useNavigate } from 'react-router-dom'; +import config from '../../../config'; +import DeckBuilderIcon from '../icons/DeckBuilderIcon'; // Import DeckBuilderIcon + +// ==============================|| MAIN LOGO ||============================== // + +const RCLogoSection = () => { + const navigate = useNavigate(); + + const handleClick = () => { + navigate(config?.defaultPath); + }; + + return ( + + {/* Encapsulate DeckBuilderIcon within an Avatar */} + + + + + DeckMaster + + + ); +}; + +export default RCLogoSection; diff --git a/src/layout/REUSABLE_COMPONENTS/RCTOOLTIP/RCToolTip.jsx b/src/layout/REUSABLE_COMPONENTS/RCTOOLTIP/RCToolTip.jsx new file mode 100644 index 0000000..15acd73 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCTOOLTIP/RCToolTip.jsx @@ -0,0 +1,147 @@ +// import React, { useState, forwardRef } from 'react'; +// import Popper from '@mui/material/Popper'; +// import { +// useFloating, +// offset, +// flip, +// shift, +// autoUpdate, +// } from '@floating-ui/react'; + +// const RCToolTip = forwardRef(({ tooltipContent, children, ...props }, ref) => { +// const [anchorEl, setAnchorEl] = useState(null); +// const open = Boolean(anchorEl); +// const id = open ? 'simple-popper' : undefined; + +// // Configure Floating UI +// const { x, y, strategy, refs, update } = useFloating({ +// open, +// onOpenChange: (isOpen) => setAnchorEl(isOpen ? anchorEl : null), +// placement: 'top', +// middleware: [offset(10), flip(), shift()], +// whileElementsMounted: autoUpdate, +// }); + +// // Handle mouse enter and leave events to control the tooltip visibility +// const handleMouseEnter = (event) => { +// setAnchorEl(event.currentTarget); +// update(); +// }; + +// const handleMouseLeave = () => { +// setAnchorEl(null); +// }; + +// // Clone the child element to attach event handlers for controlling tooltip visibility +// const child = React.cloneElement(children, { +// ...props, +// 'aria-describedby': id, +// onMouseEnter: handleMouseEnter, +// onMouseLeave: handleMouseLeave, +// ref: refs.setReference, +// }); + +// return ( +// <> +// {child} +// +//
{tooltipContent}
+//
+// +// ); +// }); + +// RCToolTip.displayName = 'RCToolTip'; + +// export default forwardRef(RCToolTip); +import React, { useState, forwardRef } from 'react'; +import Popper from '@mui/material/Popper'; +import { + useFloating, + offset, + flip, + shift, + autoUpdate, +} from '@floating-ui/react'; + +// Correctly apply forwardRef. It now accepts props and ref as arguments. +const RCToolTip = forwardRef(({ tooltipContent, children, ...props }, ref) => { + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + const id = open ? 'simple-popper' : undefined; + + // Configure Floating UI + const { x, y, strategy, refs, update } = useFloating({ + open, + onOpenChange: (isOpen) => setAnchorEl(isOpen ? anchorEl : null), + placement: 'top', + middleware: [offset(10), flip(), shift({ padding: 8 })], + whileElementsMounted: autoUpdate, + }); + + // Handle mouse enter and leave events to control the tooltip visibility + const handleMouseEnter = (event) => { + setAnchorEl(event.currentTarget); + update(); + }; + + const handleMouseLeave = () => { + setAnchorEl(null); + }; + + // Clone the child element to attach event handlers for controlling tooltip visibility + const childWithHandlers = React.cloneElement(children, { + 'aria-describedby': id, + onMouseEnter: handleMouseEnter, + onMouseLeave: handleMouseLeave, + ref: refs.setReference, + ...props, // Spread additional props to the cloned child + }); + + return ( + <> + {childWithHandlers} + +
{tooltipContent}
+
+ + ); +}); + +RCToolTip.displayName = 'RCToolTip'; + +export default RCToolTip; diff --git a/src/layout/REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIcon.jsx b/src/layout/REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIcon.jsx new file mode 100644 index 0000000..5cbfc0e --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIcon.jsx @@ -0,0 +1,18 @@ +import { forwardRef } from 'react'; +import RCWrappedIconRoot from './RCWrappedIconRoot'; +import { useMode } from '../../../context'; + +const RCWrappedIcon = forwardRef(({ color, size, children, ...rest }, ref) => { + const { theme } = useMode(); + + return ; +}); + +RCWrappedIcon.displayName = 'RCWrappedIcon'; + +RCWrappedIcon.defaultProps = { + color: 'black', + size: '3rem', +}; + +export default RCWrappedIcon; diff --git a/src/layout/REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIconRoot.jsx b/src/layout/REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIconRoot.jsx new file mode 100644 index 0000000..9697256 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIconRoot.jsx @@ -0,0 +1,31 @@ +import { Box } from '@mui/material'; +import styled from 'styled-components'; +import { useMode } from '../../../context'; + +export default styled(Box)(({ color }) => { + const { theme } = useMode(); + // const { color, size } = ownerstate; + return { + borderRadius: '50%', + width: 40, + height: 40, + minHeight: '4rem', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: + color === 'success' + ? theme.palette.chartTheme.greenAccent.light + : 'black', + // [theme.breakpoints.down('md')]: { + // width: 30, + // height: 30, + // }, + [theme.breakpoints.down('sm')]: { + fontSize: '1.2rem', + }, + [theme.breakpoints.down('xs')]: { + fontSize: '1rem', + }, + }; +}); diff --git a/src/layout/REUSABLE_COMPONENTS/SkeletonVariants.jsx b/src/layout/REUSABLE_COMPONENTS/SkeletonVariants.jsx index a4eef10..fd453eb 100644 --- a/src/layout/REUSABLE_COMPONENTS/SkeletonVariants.jsx +++ b/src/layout/REUSABLE_COMPONENTS/SkeletonVariants.jsx @@ -1,10 +1,76 @@ import React from 'react'; -import { Box } from '@mui/material'; -import { useMode } from '../../context'; +import { + Box, + Card, + CardHeader, + CardContent, + makeStyles, + Skeleton, + Grid, + Collapse, + CardActionArea, +} from '@mui/material'; import useSkeletonLoader from '../collection/collectionGrids/cards-datatable/useSkeletonLoader'; +import MDBox from './MDBOX'; +import { useMode } from '../../context'; +import { + AspectRatioBoxSkeleton, + StyledSkeletonCard, +} from '../../pages/pageStyles/StyledComponents'; + +const LoadingCardSkeleton = () => { + return ( + + } + subheader={} + avatar={} + /> + + + + + + ); +}; + +const PageHeaderSkeleton = () => { + const { SkeletonLoader } = useSkeletonLoader(); + + return ( + + + + + + + + + + + + ); +}; const HeroSectionSkeleton = () => { - const { theme } = useMode(); const { SkeletonLoader } = useSkeletonLoader(); return ( @@ -29,7 +95,7 @@ const HeroSectionSkeleton = () => { '-webkit-backdrop-filter': 'blur(20px)', border: '1px solid rgba(255, 255, 255, 0.3)', borderRadius: '25px', - padding: '30px 0px', + padding: '30px', width: 'min(1200px, 100%)', display: 'flex', flexDirection: 'column', @@ -37,21 +103,16 @@ const HeroSectionSkeleton = () => { justifyContent: 'center', }} > - {/* Title Skeleton */} - - {/* Subtitle Skeleton */} - - {/* Image Slider Skeleton */} { ); }; -export default HeroSectionSkeleton; +const SkeletonCard = () => { + const { theme } = useMode(); + + return ( + + + + + + + + + + + + + + ); +}; + +const CollectionListItemSkeleton = ({ count, index }) => ( + + + + + + + + + + + + + +); + +const DeckListItemSkeleton = ({ count, index }) => ( + + + + + + + + + + + + + +); + +const DynamicSkeletonList = ({ itemType, count, gridItemProps, context }) => ( + + {Array(count) + .fill(0) + .map((_, index) => ( + + + + ))} + +); + +export { + LoadingCardSkeleton, + HeroSectionSkeleton, + PageHeaderSkeleton, + DynamicSkeletonList, + SkeletonCard, + CollectionListItemSkeleton, + DeckListItemSkeleton, +}; diff --git a/src/pages/SplashPage2.jsx b/src/layout/REUSABLE_COMPONENTS/SplashPage2.jsx similarity index 88% rename from src/pages/SplashPage2.jsx rename to src/layout/REUSABLE_COMPONENTS/SplashPage2.jsx index 2cc474b..ff25135 100644 --- a/src/pages/SplashPage2.jsx +++ b/src/layout/REUSABLE_COMPONENTS/SplashPage2.jsx @@ -1,13 +1,12 @@ import React, { useEffect, useRef } from 'react'; import * as THREE from 'three'; -import placeholder from '../assets/images/placeholder.jpeg'; +import placeholder from '../../assets/images/placeholder.jpeg'; import { Box } from '@mui/material'; const SplashPage2 = () => { const containerRef = useRef(null); useEffect(() => { - // Scene setup const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, @@ -18,8 +17,6 @@ const SplashPage2 = () => { const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); - - // Append renderer to the container ref if (containerRef.current) { containerRef.current.appendChild(renderer.domElement); } @@ -92,26 +89,20 @@ const SplashPage2 = () => { renderer.render(scene, camera); }; - // Handle window resize window.addEventListener('resize', onWindowResize, false); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); - // Recalculate number of cards per row cardsPerRow = calculateCardsPerRow(); - // Update the rows with new card count updateRowsWithNewCardCount(); } - // Update rows with new card count when window is resized function updateRowsWithNewCardCount() { cardRows.forEach((row) => { - // Remove existing cards while (row.children.length) { row.remove(row.children[0]); } - // Add new cards based on updated count for (let j = 0; j < cardsPerRow; j++) { const card = new THREE.Mesh(geometry, material); card.position.x = (j - cardsPerRow / 2) * cardSpacing; @@ -121,15 +112,13 @@ const SplashPage2 = () => { } animate(); - - // Cleanup return () => { if (containerRef.current) { containerRef.current.removeChild(renderer.domElement); } window.removeEventListener('resize', onWindowResize, false); }; - }, []); // Empty dependency array ensures this effect runs once when the component mounts + }, []); return ( { +const StatBox = ({ title, subtitle, icon, progress, increase, wrapIcon }) => { const { theme } = useMode(); const colors = theme.palette.chartTheme; const primary = colors.primary.default; const blue = colors.blueAccent.default; const green = colors.greenAccent.default; const greenliht = colors.greenAccent.light; - const grey = colors.grey.default; - + const IconWrapper = ({ children }) => ( + + {children} + + ); return ( - - - {icon} + + {/* */} + {/* */} + + + {icon} + + {title} - - + {/* */} + + {/* */} + {/* */} + {/* - + */} diff --git a/src/components/reusable/icons/CartIcon.jsx b/src/layout/REUSABLE_COMPONENTS/icons/CartIcon.jsx similarity index 100% rename from src/components/reusable/icons/CartIcon.jsx rename to src/layout/REUSABLE_COMPONENTS/icons/CartIcon.jsx diff --git a/src/components/reusable/icons/ChartsIcon.jsx b/src/layout/REUSABLE_COMPONENTS/icons/ChartsIcon.jsx similarity index 100% rename from src/components/reusable/icons/ChartsIcon.jsx rename to src/layout/REUSABLE_COMPONENTS/icons/ChartsIcon.jsx diff --git a/src/components/reusable/icons/CollectionIcon.jsx b/src/layout/REUSABLE_COMPONENTS/icons/CollectionIcon.jsx similarity index 100% rename from src/components/reusable/icons/CollectionIcon.jsx rename to src/layout/REUSABLE_COMPONENTS/icons/CollectionIcon.jsx diff --git a/src/layout/REUSABLE_COMPONENTS/icons/DeckBuilderIcon.jsx b/src/layout/REUSABLE_COMPONENTS/icons/DeckBuilderIcon.jsx new file mode 100644 index 0000000..2d5d8d9 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/icons/DeckBuilderIcon.jsx @@ -0,0 +1,38 @@ +/* eslint-disable max-len */ +import * as React from 'react'; +import { SvgIcon } from '@mui/material'; + +export default function DeckBuilderIcon(props) { + const { customPaths, iconColor, ...otherProps } = props; + const defaultPaths = [ + 'M1050 1666 c-129 -45 -387 -136 -572 -201 -186 -65 -347 -127 -358 -137 -24 -22 -26 -70 -5 -92 48 -48 491 -413 513 -423 65 -29 98 -20 679 183 309 108 576 205 592 215 25 15 31 25 31 53 0 38 -4 42 -348 330 -160 134 -198 156 -259 156 -21 -1 -143 -38 -273 -84z', + 'M60 1188 c0 -7 6 -23 13 -35 14 -25 477 -417 522 -441 19 -11 55 -16 100 -17 66 0 104 12 633 198 310 108 577 204 593 213 29 14 49 48 49 81 0 13 -4 12 -24 -6 -28 -27 0 -16 -656 -247 -583 -205 -602 -210 -686 -168 -22 10 -149 111 -282 224 -134 113 -248 207 -253 208 -5 2 -9 -2 -9 -10z', + 'M70 1079 c0 -36 7 -43 253 -249 138 -117 266 -219 284 -226 18 -8 59 -14 92 -14 53 0 133 25 630 199 313 110 579 204 590 210 28 16 51 51 51 80 l0 24 -23 -21 c-36 -34 -1183 -432 -1243 -432 -89 0 -111 14 -370 232 -137 116 -252 215 -256 222 -4 6 -8 -5 -8 -25z', + 'M70 972 c0 -35 4 -38 324 -305 181 -152 203 -167 253 -177 30 -7 71 -9 92 -5 44 8 1109 379 1167 406 41 19 64 52 64 91 l0 21 -23 -21 c-14 -13 -243 -100 -602 -227 -742 -262 -679 -258 -902 -69 -68 57 -179 152 -248 210 -122 103 -125 105 -125 76z', + 'M70 873 c0 -18 7 -38 15 -47 38 -37 490 -413 514 -426 17 -10 52 -14 101 -14 71 1 104 11 634 198 307 108 572 204 588 212 30 15 48 49 48 87 -1 19 -2 20 -13 7 -7 -8 -19 -21 -28 -27 -20 -16 -1140 -408 -1192 -417 -21 -4 -63 -2 -93 4 -51 11 -72 26 -282 202 -125 105 -242 204 -259 221 l-33 31 0 -31z', + 'M70 778 c0 -30 10 -39 324 -301 181 -152 203 -167 253 -177 30 -7 71 -9 92 -5 44 8 1109 379 1167 406 22 11 46 31 52 45 19 39 14 49 -11 26 -36 -34 -1183 -432 -1243 -432 -89 0 -111 14 -370 232 -137 116 -252 215 -256 222 -5 6 -8 -1 -8 -16z', + ]; + const paths = customPaths || defaultPaths; + return ( + + + {paths?.map((d, i) => ( + + ))} + + + ); +} diff --git a/src/components/reusable/icons/DeckOfCardsIcon.jsx b/src/layout/REUSABLE_COMPONENTS/icons/DeckOfCardsIcon.jsx similarity index 100% rename from src/components/reusable/icons/DeckOfCardsIcon.jsx rename to src/layout/REUSABLE_COMPONENTS/icons/DeckOfCardsIcon.jsx diff --git a/src/components/reusable/icons/GlassyIcon.jsx b/src/layout/REUSABLE_COMPONENTS/icons/GlassyIcon.jsx similarity index 100% rename from src/components/reusable/icons/GlassyIcon.jsx rename to src/layout/REUSABLE_COMPONENTS/icons/GlassyIcon.jsx diff --git a/src/components/reusable/icons/MoneyIcon.jsx b/src/layout/REUSABLE_COMPONENTS/icons/MoneyIcon.jsx similarity index 100% rename from src/components/reusable/icons/MoneyIcon.jsx rename to src/layout/REUSABLE_COMPONENTS/icons/MoneyIcon.jsx diff --git a/src/components/reusable/icons/ReusableIconButton.jsx b/src/layout/REUSABLE_COMPONENTS/icons/ReusableIconButton.jsx similarity index 100% rename from src/components/reusable/icons/ReusableIconButton.jsx rename to src/layout/REUSABLE_COMPONENTS/icons/ReusableIconButton.jsx diff --git a/src/components/reusable/icons/TestingIcon.jsx b/src/layout/REUSABLE_COMPONENTS/icons/TestingIcon.jsx similarity index 100% rename from src/components/reusable/icons/TestingIcon.jsx rename to src/layout/REUSABLE_COMPONENTS/icons/TestingIcon.jsx diff --git a/src/components/reusable/icons/index.jsx b/src/layout/REUSABLE_COMPONENTS/icons/index.jsx similarity index 100% rename from src/components/reusable/icons/index.jsx rename to src/layout/REUSABLE_COMPONENTS/icons/index.jsx diff --git a/src/layout/REUSABLE_COMPONENTS/unique/SimpleButton.jsx b/src/layout/REUSABLE_COMPONENTS/unique/SimpleButton.jsx index 2891fe3..a485fc8 100644 --- a/src/layout/REUSABLE_COMPONENTS/unique/SimpleButton.jsx +++ b/src/layout/REUSABLE_COMPONENTS/unique/SimpleButton.jsx @@ -8,21 +8,52 @@ const SimpleButton = ({ isAccent, isDefault, isDisabled, + customColor, + customTextColor, + customSize, ...rest }) => { + const calculateStyles = (size) => { + switch (size) { + case 'sm': + return { + width: '75px', + // p: '1rem', + p: '0.5rem 0.75rem', + // m: '0.5rem', + fontSize: '0.875rem', + }; + case 'md': + return { + width: '100px', + padding: '0.75rem 1rem', + fontSize: '1rem', + }; + default: + return { + width: '140px', + padding: '1rem 1.25rem', + fontSize: '1.125rem', + }; + } + }; const baseStyle = { position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', - minWidth: '140px', - padding: `1.035rem ${theme.lenMd1}`, + minWidth: calculateStyles(customSize).width, + padding: calculateStyles(customSize).padding, + // minWidth: customSize === 'md' ? 100 : customSize === 'sm' ? 75 : 140, + // padding: `1.035rem ${theme.lenMd1}`, borderRadius: theme.borderRadius, + fontSize: calculateStyles(customSize).fontSize, + transitionProperty: 'color, background, box-shadow', transitionDuration: '0.35s', - background: theme.colorDefaultBackground, - color: theme.colorDefaultText, + background: !customColor ? theme.colorDefaultBackground : customColor, + color: !customTextColor ? theme.colorDefaultText : customTextColor, boxShadow: isDefault ? `0 0 0 4px ${rgba(theme.colorDefaultBackground || 'white', 0.74)}` : 'none', @@ -51,7 +82,9 @@ const SimpleButton = ({ width: '100%', height: '100%', borderRadius: theme.borderRadius, - background: 'rgba(0, 0, 0, 0.075)', + background: customColor + ? rgba(customColor, 0.15) // Lighter shade of customColor if provided + : 'rgba(0, 0, 0, 0.075)', opacity: 0, pointerEvents: 'none', transition: 'opacity 0.35s', diff --git a/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx b/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx index 23dc327..043fba5 100644 --- a/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx +++ b/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx @@ -1,37 +1,16 @@ import React from 'react'; import MDTypography from '../MDTYPOGRAPHY/MDTypography'; import styled from 'styled-components'; -// const CardContainer = styled.div` -// padding: ${({ theme, hasTitle }) => (hasTitle ? 0 : theme.lenMd3)}; -// margin-bottom: ${({ theme, noBottomMargin }) => -// noBottomMargin ? 0 : theme.lenMd1}; -// border-radius: ${({ theme }) => theme.borderRadius}; -// background: ${({ theme, isPrimary, isAccent }) => -// isPrimary -// ? theme.colorPrimary -// : isAccent -// ? theme.colorAccent -// : theme.colorCardBackground}; -// color: ${({ theme, isPrimary, isAccent }) => -// isPrimary -// ? theme.colorPrimaryText -// : isAccent -// ? theme.colorAccentText -// : theme.colorText}; -// `; -// const Content = styled.div` -// padding: ${({ theme }) => `0 ${theme.lenMd3} ${theme.lenMd3}`}; -// `; +import { AspectRatio, CardContent, IconButton, Typography } from '@mui/joy'; +import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined'; +import { useMode } from '../../../context'; +import { Icon, useMediaQuery } from '@mui/material'; +import MDBox from '../MDBOX'; +import SaveIcon from '@mui/icons-material/Save'; +import AddIcon from '@mui/icons-material/Add'; +import CollectionsIcon from '@mui/icons-material/Collections'; +import FlexBetween from '../FlexBetween'; -// const Title = styled.div` -// display: flex; -// align-items: center; -// justify-content: center; -// height: ${({ theme }) => theme.lenXl2}; -// padding: ${({ theme }) => `0 ${theme.lenMd1}`}; -// color: ${({ theme }) => theme.colorLabel}; -// font-size: ${({ theme }) => theme.lenMd2}; -// `; const getPrimaryStyle = (theme, isPrimary) => ({ background: isPrimary ? theme.colorPrimary : undefined, color: isPrimary ? theme.colorPrimaryText : undefined, @@ -42,11 +21,57 @@ const getAccentStyle = (theme, isAccent) => ({ color: isAccent ? theme.colorAccentText : undefined, }); -const CardContent = ({ theme, children }) => ( -
{children}
-); +const getTableOrChartStyle = (theme, isTableOrChart) => ({ + background: isTableOrChart ? theme.colorCardBackground : undefined, + color: isTableOrChart ? theme.colorPrimary : undefined, +}); + +const getFormHeaderStyle = (theme, isFormHeader) => ({ + background: isFormHeader ? theme.colorCardBackground : undefined, + color: isFormHeader ? theme.colorPrimary : undefined, + maxWidth: 'md', + padding: theme.lenMd3, // Updated to use theme's spacing method if available + borderRadius: '24px', + boxShadow: '0px 3px 10px 0px rgba(0, 0, 0, 0.2)', // Custom shadow with blur + margin: 'auto', + width: '80%', +}); + +const getSearchFormHeaderStyle = (theme, isSearchFormHeader) => ({ + background: isSearchFormHeader ? theme.colorCardBackground : undefined, + color: isSearchFormHeader ? theme.colorPrimary : undefined, + maxWidth: 'lg', + // padding: theme.lenMd3, // Updated to use theme's spacing method if available + borderRadius: '24px', + boxShadow: '0px 3px 10px 0px rgba(0, 0, 0, 0.2)', // Custom shadow with blur + margin: 'auto', + marginTop: '1rem', + // marginLeft: '5rem', + // marginRight: '5rem', + // marginTop: 'auto', + // marginBottom: 'auto', + width: '98%', +}); + +const getHeroDisplayStyles = (theme, isHeroDisplay) => ({ + background: 'transparent', + color: isHeroDisplay ? theme.colorPrimary : undefined, + root: { + minWidth: 275, + background: 'transparent', + backgroundImage: + 'linear-gradient(45deg, #ff9a9e 0%, #fad0c4 99%, #fad0c4 100%)', + backdropFilter: 'blur(40)px', + boxShadow: '10px 10px 10px rgba(30,30,30,.1)', + borderRadius: 10, + }, +}); + +// const CardContent = ({ theme, children }) => ( +//
{children}
+// ); -const CardTitle = ({ theme, children }) => ( +const CardTitle = ({ theme, children, isTableOrChart }) => (
( justifyContent: 'center', height: theme.lenXl2, padding: `0 ${theme.lenMd1}`, + // color: theme.colorLabel, color: theme.colorLabel, fontSize: theme.lenMd2, // overflow: 'hidden', }} > - + {children}
); +const iconStyles = { + // Adjust icon size to be larger based on the parent size + fontSize: '8rem', // Example size, adjust as needed + color: '#000000', // Example color, adjust as needed + maxWidth: '100%', + maxHeight: '100%', +}; +const String2Icon = (icon) => { + switch (icon) { + case 'AddIcon': + return ; + case 'SaveIcon': + return ; + case 'CollectionsIcon': + return ; + default: + return null; + } +}; const SimpleCard = ({ theme, hasTitle, isPrimary, isAccent, + isTableOrChart, noBottomMargin, children, cardTitle, data, + isFormHeader, + isSearchFormHeader, + isHeroDisplay, + heroText, + heroIcon, ...rest }) => { + const { theme: themeSettings } = useMode(); + const isMobileView = useMediaQuery(themeSettings.breakpoints.down('sm')); const cardStyle = { // display: 'flex', width: '100%', @@ -84,6 +141,10 @@ const SimpleCard = ({ borderRadius: theme.borderRadius, background: theme.colorCardBackground, color: theme.colorText, + ...(isHeroDisplay && getHeroDisplayStyles(theme, true)), + ...(isSearchFormHeader && getSearchFormHeaderStyle(theme, true)), + ...(isFormHeader && getFormHeaderStyle(theme, true)), + ...(isTableOrChart && getTableOrChartStyle(theme, true)), ...(isPrimary && getPrimaryStyle(theme, true)), ...(isAccent && getAccentStyle(theme, true)), }; @@ -92,61 +153,110 @@ const SimpleCard = ({
{cardTitle && ( <> - {cardTitle} - {children} + + {cardTitle} + +
+ {children} +
)} {!cardTitle && children} + {isHeroDisplay && ( + + + + + {String2Icon(heroIcon)} + + + + + + + + {/* No need for additional style */} + + + + {heroText} {/* Display heroText */} + + + + )}
); }; export default SimpleCard; - -// const CardContent = ({ theme, children }) => ( -//
{children}
-// ); - -// const CardTitle = ({ theme, children }) => ( -//
-// {children} -//
-// ); - -// export const SimpleCard = () => { -// theme, -// children, -// cardTitle, -// isPrimary, -// isAccent, -// noBottomMargin, -// }) => { -// return ( -// -// {cardTitle && ( -// <> -// {cardTitle} -// {children} -// -// )} -// {!cardTitle && children} -// -// ); -// }; diff --git a/src/layout/REUSABLE_COMPONENTS/unique/SimpleSectionHeader.jsx b/src/layout/REUSABLE_COMPONENTS/unique/SimpleSectionHeader.jsx index 4843798..bdc6154 100644 --- a/src/layout/REUSABLE_COMPONENTS/unique/SimpleSectionHeader.jsx +++ b/src/layout/REUSABLE_COMPONENTS/unique/SimpleSectionHeader.jsx @@ -2,8 +2,10 @@ import React from 'react'; import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import { useMode } from '../../../context'; +import MDTypography from '../MDTYPOGRAPHY/MDTypography'; +import { useMediaQuery, useTheme } from '@mui/material'; +import MDBox from '../MDBOX'; -// Updated SimpleSectionHeader component with additional parameters const SimpleSectionHeader = ({ sectionName, userName, @@ -11,39 +13,78 @@ const SimpleSectionHeader = ({ lastUpdated, }) => { const { theme } = useMode(); + const lgDown = useMediaQuery(theme.breakpoints.down('lg')); + const isMobileView = useMediaQuery(theme.breakpoints.down('sm')); + return ( - - {sectionName} - + + {sectionName} + + - {` ${userName}'s Portfolio`} + {`${userName}'s Portfolio`} + + + + {/* Section Description */} + + {sectionDescription} -
- - {sectionDescription} + + {/* Last Updated */} - {` ${lastUpdated}`} + {`${lastUpdated}`} - +
); }; diff --git a/src/layout/cart/CartContent.js b/src/layout/cart/CartContent.js index 8567037..9d18358 100644 --- a/src/layout/cart/CartContent.js +++ b/src/layout/cart/CartContent.js @@ -2,24 +2,31 @@ import React from 'react'; import { Typography, Skeleton, Box, Grid, Container } from '@mui/material'; import CartContainer from './CartContainer'; import { useCartStore } from '../../context/MAIN_CONTEXT/CartContext/CartContext'; -import { useMode } from '../../context'; +import { useMode, useUserContext } from '../../context'; import GenericCard from '../../components/cards/GenericCard'; const CartContent = () => { const { theme } = useMode(); - const { getProductGridContainerStyle } = theme.responsiveStyles; + // const { getProductGridContainerStyle } = theme.responsiveStyles; // const containerStyles = responsiveStyles.getProductGridContainerStyle; const { cartData, isLoading } = useCartStore(); + // const { user } = useUserContext(); - const renderCartItems = () => { - if (isLoading) { - return ; - } + // if (isLoading && (!cartData?.cart || cartData.cart.length === 0)) { + // return ( + // + // + // Your cart is empty. + // + // + // ); + // } - return ( + return ( + { {cartData?.cart?.map((card, index) => ( + {console.log(card)} { ))} - ); - }; - - if (!isLoading && (!cartData?.cart || cartData.cart.length === 0)) { - return ( - - - Your cart is empty. - - - ); - } - - return {renderCartItems()}; + + ); }; export default CartContent; diff --git a/src/layout/cart/CartSummary.js b/src/layout/cart/CartSummary.js index f235171..27d2b9e 100644 --- a/src/layout/cart/CartSummary.js +++ b/src/layout/cart/CartSummary.js @@ -1,29 +1,29 @@ -// import React from 'react'; -// import { Box, Typography } from '@mui/material'; +import React from 'react'; +import { Box, Typography } from '@mui/material'; -// const CartSummary = ({ quantity, totalCost }) => { -// return ( -// -// -// Items: {quantity} -// -// -// Grand Total: ${totalCost} -// -// -// ); -// }; +const CartSummary = ({ quantity, totalCost }) => { + return ( + + + Items: {quantity} + + + Grand Total: ${totalCost} + + + ); +}; -// export default CartSummary; +export default CartSummary; diff --git a/src/layout/collection/collectionGrids/ChartGridLayout.jsx b/src/layout/collection/collectionGrids/ChartGridLayout.jsx index 14cfa9f..961af6b 100644 --- a/src/layout/collection/collectionGrids/ChartGridLayout.jsx +++ b/src/layout/collection/collectionGrids/ChartGridLayout.jsx @@ -1,35 +1,141 @@ -import React from 'react'; -import { Grid, Card, useMediaQuery, Icon } from '@mui/material'; +import React, { Suspense, useEffect, useMemo } from 'react'; +import { + Grid, + Card, + useMediaQuery, + Icon, + Container, + Grow, + Box, +} from '@mui/material'; import MDBox from '../../REUSABLE_COMPONENTS/MDBOX'; -import CollectionPortfolioChartContainer from './cards-chart/CollectionPortfolioChartContainer'; import DataTable from './cards-datatable'; -import { useMode } from '../../../context'; +import { useMode, useStatisticsStore } from '../../../context'; import DashboardBox from '../../REUSABLE_COMPONENTS/DashboardBox'; import BoxHeader from '../../REUSABLE_COMPONENTS/BoxHeader'; import { UpdaterAndStatisticsRow } from './cards-chart/UpdaterAndStatisticsRow'; import SimpleCard from '../../REUSABLE_COMPONENTS/unique/SimpleCard'; import uniqueTheme from '../../REUSABLE_COMPONENTS/unique/uniqueTheme'; - -const ChartGridLayout = ({ selectedCards, removeCard, columns, data }) => { - const { theme } = useMode(); - const isXs = useMediaQuery(theme.breakpoints.down('sm')); - const isLg = useMediaQuery(theme.breakpoints.up('lg')); - const tableSize = isXs ? 'less' : isLg ? 'large' : 'small'; - console.log({ selectedCards, tableSize, columns, data }); // Debug log - // A function to render MDBox Card container for reusability - const renderCardContainer = (content) => ( +import { ChartArea } from '../../../pages/pageStyles/StyledComponents'; +import useSelectedCollection from '../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +import useTimeRange from '../../../components/forms/selectors/useTimeRange'; +import useSkeletonLoader from './cards-datatable/useSkeletonLoader'; +import ChartErrorBoundary from './cards-chart/ChartErrorBoundary'; +import { ChartConfiguration } from './cards-chart/ChartConfigs'; +import IconStatWrapper from '../../REUSABLE_COMPONENTS/unique/IconStatWrapper'; +import { TopCardsDisplayRow } from '../sub-components/TopCardsDisplayRow'; +import LoadingOverlay from '../../LoadingOverlay'; +import RCWrappedIcon from '../../REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIcon'; +import { + ResponsiveContainer, + CartesianGrid, + AreaChart, + BarChart, + Bar, + LineChart, + XAxis, + YAxis, + Legend, + Line, + Tooltip, + Area, +} from 'recharts'; +const renderCardContainer = (content) => { + return ( - {/* */} - {/* */} {content} - {/* */} ); +}; + +const ChartGridLayout = ({ selectedCards, removeCard, columns, data }) => { + const { theme } = useMode(); + const isXs = useMediaQuery(theme.breakpoints.down('sm')); + const isLg = useMediaQuery(theme.breakpoints.up('lg')); + const tableSize = isXs ? 'less' : isLg ? 'large' : 'small'; + const env = process.env.CHART_ENVIRONMENT; + const isSmall = useMediaQuery(theme.breakpoints.down('sm')); + const { selectedCollection, showCollections } = useSelectedCollection(); + const chartDataVariants = { + averaged: selectedCollection.averagedChartData, + raw: selectedCollection.nivoChartData, + new: selectedCollection.newNivoChartData, + test: selectedCollection.nivoTestData, + }; + const selectedData = chartDataVariants.averaged; + const { selectedTimeRange } = useTimeRange(); + const { stats, markers } = useStatisticsStore(); + const { SkeletonLoader } = useSkeletonLoader(); + const averagedData = selectedCollection.averagedChartData; + if (!averagedData || !averagedData[selectedTimeRange]) { + return ; + } + // if (!averagedData || !averagedData[selectedTimeRange]) { + // return ( + // + // + // + // + // + // + // + // + // + // + // + // + // {[...Array(5)].map((_, index) => ( + // + // + // + // + // + // ))} + // + // + // + // ); + // } + + const selectedChartData = useMemo(() => { + const chartData = averagedData[selectedTimeRange]; + return chartData || null; + }, [selectedCollection.averagedChartData, selectedTimeRange]); + useEffect(() => { + console.log('DEBUG LOG, ', { + selectedChartData, + markers, + selectedTimeRange, + }); + }, []); + + const updatedAt = selectedCollection?.updatedAt; + const formattedTime = updatedAt + ? new Date(updatedAt).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + }) + : 'Loading...'; return ( - + { > table_chart} + icon={ + + show_chart + + } sideText="+4%" /> {renderCardContainer( - + }> + + + + + + )} { > + + + { > table_chart} - sideText="+4%" + icon={ + + list + + } + // sideText="Updated recently" + sideText={`Last Updated: ${formattedTime}`} /> {renderCardContainer( diff --git a/src/layout/collection/collectionGrids/CollectionListStats.jsx b/src/layout/collection/collectionGrids/CollectionListStats.jsx deleted file mode 100644 index 72ef20e..0000000 --- a/src/layout/collection/collectionGrids/CollectionListStats.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Grid, Skeleton } from '@mui/material'; -import TotalValueOfCollectionsDisplay from '../sub-components/TotalValueOfCollectionsDisplay'; -import TopFiveExpensiveCards from '../sub-components/TopFiveExpensiveCards'; -import { useStatisticsStore } from '../../../context'; -import SimpleCard from '../../REUSABLE_COMPONENTS/unique/SimpleCard'; -import uniqueTheme from '../../REUSABLE_COMPONENTS/unique/uniqueTheme'; - -const CollectionListStats = () => { - const { topFiveCards, totalValue, chartData } = useStatisticsStore(); - return ( - - - - This is a primary styled card content. - - - - - - - - - - - - - ); -}; - -export default CollectionListStats; diff --git a/src/layout/collection/collectionGrids/StatisticsCardsGrid.jsx b/src/layout/collection/collectionGrids/StatisticsCardsGrid.jsx deleted file mode 100644 index 2953f8a..0000000 --- a/src/layout/collection/collectionGrids/StatisticsCardsGrid.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Grid } from '@mui/material'; -import MDBox from '../../REUSABLE_COMPONENTS/MDBOX'; -import ComplexStatisticsCard from '../sub-components/ComplexStatisticsCard'; -import LoadingIndicator from '../../../components/reusable/indicators/LoadingIndicator'; -import useSelectedCollection from '../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; - -const cardData = [ - { - icon: 'attach_money', - title: 'Cards Added', - countFunc: (collection) => - collection?.cards?.reduce((acc, card) => acc + card.quantity, 0), - percentage: { color: 'success', amount: '+55%', label: 'than last week' }, - }, - { - icon: 'leaderboard', - title: "Today's Total Value", - countFunc: (collection) => `$${collection.totalPrice}`, - percentage: { color: 'success', amount: '+3%', label: 'than last month' }, - }, - { - icon: 'store', - title: 'Most Valuable Card', - countFunc: (collection) => { - const mostValuableCard = collection?.cards?.reduce((acc, card) => - acc.price > card.price ? acc : card - ); - return `${mostValuableCard?.name} - $${mostValuableCard?.price}`; - }, - percentage: { color: 'success', amount: '+1%', label: 'than yesterday' }, - }, - { - icon: 'trending_up', - title: 'Weekly Performance', - countFunc: (collection) => - `+${collection?.collectionStatistics?.percentageChange}%`, - percentage: { color: 'success', amount: '', label: 'Just updated' }, - }, -]; - -const gridItemStyle = { - display: 'flex', - justifyContent: 'center', - width: '100%', - height: '100%', -}; - -const StatisticsCardGrid = () => { - const { allCollections, selectedCollection } = useSelectedCollection(); - - return ( - - - {cardData?.map((data, index) => ( - - - - ))} - - - ); -}; - -export default StatisticsCardGrid; diff --git a/src/layout/collection/collectionGrids/cards-chart/ChartConfigs.jsx b/src/layout/collection/collectionGrids/cards-chart/ChartConfigs.jsx index ab2b5cd..295371d 100644 --- a/src/layout/collection/collectionGrids/cards-chart/ChartConfigs.jsx +++ b/src/layout/collection/collectionGrids/cards-chart/ChartConfigs.jsx @@ -1,208 +1,64 @@ import { ResponsiveLine } from '@nivo/line'; import { useChartContext, useMode } from '../../../../context'; -import useSelectedCollection from '../../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; import { CustomTooltipLayer, useEventHandlers, } from '../../../../context/MAIN_CONTEXT/ChartContext/helpers'; -import { useEffect, useMemo, useState } from 'react'; -import useCollectionManager from '../../../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; -import NivoContainer from './NivoContainer'; -import { mockLineData as data } from '../../data/mockData'; +import { useEffect, useMemo } from 'react'; +import NivoContainer from '../../../REUSABLE_COMPONENTS/NivoContainer'; import PropTypes from 'prop-types'; -// import nivoTestData from './GenerateNivoTestData'; -// const { tickValues, xFormat } = useMemo(() => { -// let format, ticks; -// switch (timeRange) { -// case '24hr': -// format = '%H:%M'; -// ticks = 'every hour'; -// break; -// case '7d': -// format = '%b %d'; -// ticks = 'every day'; -// break; -// case '30d': -// format = '%b %d'; -// ticks = 'every day'; -// break; -// case '90d': -// format = '%b %d'; -// ticks = 'every 3 days'; -// break; -// case '180d': -// format = '%b %d'; -// ticks = 'every 6 days'; -// break; -// case '270d': -// format = '%b %d'; -// ticks = 'every 9 days'; -// break; -// case '365d': -// format = '%b %d'; -// ticks = 'every 12 days'; -// break; -// default: -// format = '%b %d'; -// ticks = 'every day'; -// } -// return { tickValues: ticks, xFormat: `time:${format}` }; -// }, [timeRange]); -// const normalizeData = (data, timeRange) => { -// // Determine the start and end dates based on the timeRange -// let endDate = new Date(); -// let startDate = new Date(); - -// startDate.setDate(endDate.getDate() - parseInt(timeRange, 10)); - -// let normalizedData = []; -// let currentDate = new Date(startDate); - -// while (currentDate <= endDate) { -// let dateString = currentDate.toISOString().split('T')[0]; - -// let dataPoint = data?.find((point) => point.x === dateString); -// normalizedData.push({ -// x: dateString, -// y: dataPoint ? dataPoint.y : null, // Use null for missing data -// }); - -// currentDate.setDate(currentDate.getDate() + 1); -// } - -// return [{ id: 'data', data: normalizedData }]; -// }; -// function getTickValuesAndFormat(timeRange) { -// // Your logic to determine tick values and format based on timeRange... -// return { -// tickValues: 'every day', -// xFormat: '%Y-%m-%d', -// }; -// } -// const normalizeData = (data, timeRange) => { -// let endDate = new Date(); -// let startDate = new Date(); -// startDate.setDate(endDate.getDate() - parseInt(timeRange, 10)); - -// return [ -// { -// id: 'data', -// data: data.map(({ x, y }) => ({ -// x: x || startDate.toISOString().split('T')[0], // Provide a fallback date if necessary -// y: y != null ? y : 0, // Ensure null or undefined values are replaced with 0 -// })), -// }, -// ]; -// }; +import ChartErrorBoundary from './ChartErrorBoundary'; +import { BasicTooltip } from '@nivo/tooltip'; +const formatDateBasedOnRange = (range) => { + const formatMap = { + '24hr': { format: '%H:%M', ticks: 'every hour' }, + '7d': { format: '%b %d', ticks: 'every day' }, + '30d': { format: '%b %d', ticks: 'every day' }, + '90d': { format: '%b %d', ticks: 'every 3 days' }, + '180d': { format: '%b %d', ticks: 'every 6 days' }, + '270d': { format: '%b %d', ticks: 'every 9 days' }, + '365d': { format: '%b %d', ticks: 'every 12 days' }, + default: { format: '%b %d', ticks: 'every day' }, + }; + + return formatMap[range] || formatMap.default; +}; +const TooltipLayer = ({ points }) => ( + <> + {points?.map((point) => ( + + ))} + +); -// function getTickValuesAndFormat(timeRange) { -// // Simplified logic for determining tick values and format based on timeRange -// const format = timeRange === '24hr' ? '%H:%M' : '%b %d'; -// const ticks = timeRange === '24hr' ? 'every hour' : 'every day'; -// return { tickValues: ticks, xFormat: `time:${format}` }; -// } export const ChartConfiguration = ({ markers, height, nivoChartData, range, - loadingId, + loadingID, }) => { const { theme } = useMode(); - const { selectedCollection, nivoTestData } = useSelectedCollection(); - // const { nivoChartData, newNivoChartData } = useCollectionManager(); + const colors = theme.palette.chartTheme; + const { greenAccent, redAccent, grey } = colors; const { handleMouseMove, handleMouseLeave } = useEventHandlers(); - // const chartData = useMemo(() => { - // // Access the averaged data based on the selected range - // return selectedCollection.averagedChartData.get(range) || []; - // }, [selectedCollection.averagedChartData, range]); - // const [timeRange, setTimeRange] = useState('24hr'); - // const [chartData, setChartData] = useState([]); - // useEffect(() => { - // // Assuming a function like `normalizeData` exists to format your data correctly based on the time range. - // // This should adjust the data structure to fit what Nivo expects. - // const normalizedData = normalizeData(nivoChartData, timeRange); - // setChartData(normalizedData); - // }, [nivoChartData, timeRange]); - // useEffect(() => { - // let isValidData = true; // Assume data is valid initially - - // // Checks if the data array has valid 'x' and 'y' values - // const validateChartData = (data) => { - // return data.every((series) => - // series.data.every( - // (point) => point.x && point.y !== null && point.y !== undefined - // ) - // ); - // }; - - // // Check if nivoChartData or newNivoChartData has valid structure and content - // if ( - // !nivoChartData || - // !newNivoChartData || - // !validateChartData(nivoChartData) - // ) { - // isValidData = false; // Data is considered invalid if checks fail - // } - - // // Set the chart data based on the validation above - // let activeChartData; - // if (isValidData) { - // // Normalize the actual data if it's valid - // activeChartData = normalizeData(nivoChartData, timeRange); - // } else { - // // Fallback to nivoTestData for the selected time range if data is invalid - // const testData = nivoTestData.find((data) => data.id === timeRange); - // activeChartData = testData ? normalizeData(testData.data, timeRange) : []; - // } - - // setChartData(activeChartData); - // }, [nivoChartData, newNivoChartData, timeRange, nivoTestData]); - - // Filtering valid markers only to avoid any runtime errors. const validMarkers = useMemo( - () => markers.filter((marker) => marker.value !== undefined), + () => markers?.filter((marker) => marker.value !== undefined), [markers] ); - const { tickValues, xFormat } = useMemo(() => { - let format, ticks; - switch (range) { - case '24hr': - format = '%H:%M'; - ticks = 'every hour'; - break; - case '7d': - case '30d': - format = '%b %d'; - ticks = 'every day'; - break; - case '90d': - format = '%b %d'; - ticks = 'every 3 days'; - break; - case '180d': - format = '%b %d'; - ticks = 'every 6 days'; - break; - case '270d': - format = '%b %d'; - ticks = 'every 9 days'; - break; - case '365d': - format = '%b %d'; - ticks = 'every 12 days'; - break; - default: - format = '%b %d'; - ticks = 'every day'; - } - return { tickValues: ticks, xFormat: `time:${format}` }; - }, [range]); - + const { tickValues, xFormat } = useMemo( + () => formatDateBasedOnRange(range), + [range] + ); const chartProps = useMemo( () => ({ - // data: [nivoChartData], - data: [nivoChartData], + data: nivoChartData, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, colors: { datum: 'color' }, // This is the only change from the original code @@ -211,8 +67,6 @@ export const ChartConfiguration = ({ pointBorderWidth: 2, pointBorderColor: { from: 'serieColor' }, yFormat: '$.2f', - - // colors: theme.palette.backgroundE.dark, axisBottom: { tickRotation: 0, legend: 'Time', @@ -239,28 +93,28 @@ export const ChartConfiguration = ({ margin: { top: 20, right: 40, bottom: 50, left: 55 }, padding: 0.5, animate: true, - // xFormat: 'time:%Y-%m-%d %H:%M:%S7Z', - // format: '%Y-%m-%dT%H:%M:%S.%LZ', - xScale: { type: 'time', format: '%Y-%m-%dT%H:%M:%S.%LZ', precision: 'millisecond', - // format: 'time:%Y-%m-%d', useUTC: false, - // precision: 'day', }, yScale: { type: 'linear', - min: 'auto', - max: 'auto', - stacked: true, + min: '0', // Explicitly setting minimum to 0, adjust as needed + max: 'auto', // Consider setting an explicit max if appropriate + stacked: false, // Changed to false unless stacking is needed reverse: false, }, - curve: 'monotoneX', - useMesh: true, - motionConfig: 'gentle', - + // yScale: { + // type: 'linear', + // min: 'auto', + // max: 'auto', + // stacked: true, + // reverse: false, + // }, + curve: 'catmullRom', // This curve type can create smoother, more wavy lines + motionConfig: 'wobbly', // A more dynamic motion configuration useMesh: true, stiffness: 90, damping: 15, enableSlices: 'x', @@ -274,50 +128,42 @@ export const ChartConfiguration = ({ 'points', 'axes', 'legends', - ({ points, xScale, yScale, markers: validMarkers }) => ( - - ), + TooltipLayer, ], - // theme: { - // axis: theme.nivo.axis, - // legends: theme.nivo.legends, - // tooltip: theme.nivo.tooltip, - // }, theme: { axis: { domain: { line: { - stroke: theme.palette.chartTheme.grey.lightest, + stroke: grey.lightest, }, }, legend: { text: { - fill: theme.palette.chartTheme.grey.lightest, + fill: greenAccent.darkest, + fontSize: 12, + outlineWidth: 0.1, + outlineColor: grey.darkest, }, }, ticks: { line: { - stroke: theme.palette.chartTheme.grey.lightest, + stroke: grey.lightest, strokeWidth: 1, }, text: { - fill: theme.palette.chartTheme.grey.lightest, + fill: grey.lightest, }, }, }, legends: { text: { - fill: theme.palette.chartTheme.grey.lightest, + fill: grey.lightest, }, }, tooltip: { container: { - color: theme.palette.chartTheme.primary.default, + color: grey.darkest, + borderColor: redAccent.darkest, }, }, }, @@ -326,51 +172,27 @@ export const ChartConfiguration = ({ ); return ( - - - + + + + + ); }; -// Define the structure of each data point -// const pointShape = PropTypes.shape({ -// x: PropTypes.oneOfType([ -// PropTypes.number, -// PropTypes.string, -// PropTypes.instanceOf(Date), -// ]).isRequired, -// y: PropTypes.number.isRequired, -// }); - -// // Define the structure for each data series -// const seriesShape = PropTypes.shape({ -// id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, -// color: PropTypes.string, -// data: PropTypes.arrayOf(pointShape).isRequired, -// }); - -// Now, apply these to ChartConfiguration propTypes ChartConfiguration.propTypes = { - // markers: PropTypes.arrayOf(PropTypes.object), - // height: PropTypes.number.isRequired, - // range: PropTypes.string.isRequired, - // loadingId: PropTypes.string, - nivoChartData: PropTypes.arrayOf( + markers: PropTypes.arrayOf( PropTypes.shape({ - id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - color: PropTypes.string, - data: PropTypes.arrayOf( - PropTypes.shape({ - x: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number, - PropTypes.instanceOf(Date), - ]).isRequired, - y: PropTypes.number.isRequired, - }) - ).isRequired, + axis: PropTypes.oneOf(['x', 'y']).isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) + .isRequired, + legend: PropTypes.string, + lineStyle: PropTypes.object, }) - ).isRequired, - // Validate chartData explicitly to match the expected structure - // nivoChartData: PropTypes.arrayOf(seriesShape).isRequired, + ), + height: PropTypes.number.isRequired, + nivoChartData: PropTypes.arrayOf(PropTypes.object).isRequired, + range: PropTypes.oneOf(['24hr', '7d', '30d', '90d', '180d', '270d', '365d']) + .isRequired, + loadingID: PropTypes.string.isRequired, }; diff --git a/src/layout/collection/collectionGrids/cards-chart/ChartErrorBoundary.jsx b/src/layout/collection/collectionGrids/cards-chart/ChartErrorBoundary.jsx index 7ec649a..761dd3d 100644 --- a/src/layout/collection/collectionGrids/cards-chart/ChartErrorBoundary.jsx +++ b/src/layout/collection/collectionGrids/cards-chart/ChartErrorBoundary.jsx @@ -1,30 +1,49 @@ -import { Typography } from '@mui/material'; -import React from 'react'; +import React, { useState, useEffect } from 'react'; +import { Box, Container, Icon, Typography, useMediaQuery } from '@mui/material'; +import useSkeletonLoader from '../cards-datatable/useSkeletonLoader'; +import BoxHeader from '../../../REUSABLE_COMPONENTS/BoxHeader'; +import { useMode } from '../../../../context'; -class ChartErrorBoundary extends React.Component { - constructor(props) { - super(props); - this.state = { hasError: false, error: null }; - } +function ChartErrorBoundary({ children }) { + const [hasError, setHasError] = useState(false); + const [error, setError] = useState(null); + const { SkeletonLoader } = useSkeletonLoader(); + const { theme } = useMode(); + const isSmall = useMediaQuery(theme.breakpoints.down('sm')); - static getDerivedStateFromError(error) { - // Update state so the next render will show the fallback UI. - return { hasError: true, error }; - } + useEffect(() => { + const errorListener = (event) => { + event.preventDefault(); + setError(event.error); + setHasError(true); + }; - componentDidCatch(error, errorInfo) { - // You can log the error to an error reporting service - console.error('Error in chart component:', error, errorInfo); - } + window.addEventListener('error', errorListener); - render() { - if (this.state.hasError) { - // You can render any custom fallback UI - return Unable to display chart; - } + return () => { + window.removeEventListener('error', errorListener); + }; + }, []); - return this.props.children; + if (hasError) { + return ( + + Unable to display chart + + table_chart} + sideText={new Date().toLocaleString()} + /> + + + + + ); } + + return children; } export default ChartErrorBoundary; diff --git a/src/layout/collection/collectionGrids/cards-chart/CollectionPortfolioChartContainer.jsx b/src/layout/collection/collectionGrids/cards-chart/CollectionPortfolioChartContainer.jsx deleted file mode 100644 index 261a730..0000000 --- a/src/layout/collection/collectionGrids/cards-chart/CollectionPortfolioChartContainer.jsx +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import { Box, useMediaQuery, Grid, Container, Icon } from '@mui/material'; -import { - useChartContext, - useStatisticsStore, - useMode, - useCollectionStore, -} from '../../../../context'; -import { ChartArea } from '../../../../pages/pageStyles/StyledComponents'; -import BoxHeader from '../../../REUSABLE_COMPONENTS/BoxHeader'; -import LinearChart from './LinearChart'; -import { Suspense } from 'react'; -import useSkeletonLoader from '../cards-datatable/useSkeletonLoader'; -import useSelectedCollection from '../../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -import useTimeRange from '../../../../components/forms/selectors/useTimeRange'; - -const CollectionPortfolioChartContainer = () => { - const { theme } = useMode(); - const env = process.env.CHART_ENVIRONMENT; - const isSmall = useMediaQuery(theme.breakpoints.down('sm')); - const { selectedCollection } = useSelectedCollection(); - const chartDataVariants = { - averaged: selectedCollection.averagedChartData, - raw: selectedCollection.nivoChartData, - new: selectedCollection.newNivoChartData, - test: selectedCollection.nivoTestData, - }; - const selectedData = chartDataVariants.averaged; - const { selectedTimeRange } = useTimeRange(); - const { stats, markers } = useStatisticsStore(); - const { SkeletonLoader } = useSkeletonLoader(); - // Directly use the averagedChartData map for selecting the data based on timeRange - const selectedChartData = useMemo(() => { - const averagedData = selectedCollection.averagedChartData; - - if (!averagedData || !averagedData[selectedTimeRange]) { - console.error( - 'No averaged chart data available for the selected time range.' - ); - return null; - } - - // Retrieve the specific dataset for the current time range directly - const chartData = averagedData[selectedTimeRange]; - return chartData || null; // Return null if the specific range data is not available - }, [selectedCollection.averagedChartData, selectedTimeRange]); - - if (!selectedChartData) { - return ( - - table_chart} - sideText={new Date().toLocaleString()} - /> - - - - - ); - } - - // const setSelectedChartData = (chartData) => { - // console.log('setSelectedChartData', chartData); - // setTimeRange(chartData); - // }; - // useEffect(() => { - // const timeRangeIdMap = nivoTestData?.reduce((acc, data) => { - // acc[data.id] = data; - // return acc; - // }, {}); - // console.log('TIME RANGE ID MAP:', timeRangeIdMap); - - // const matchedData = timeRangeIdMap[timeRange.id]; - // if (matchedData) { - // console.log('SET SELECTED DATA:', matchedData); - // setSelectedChartData(matchedData); - // // setSelectedChartData(matchedData); - // } - // }, [selectedData, timeRange]); - - // if (!selectedChartData) { - // return ( - // - // table_chart} - // sideText={new Date().toLocaleString()} - // /> - // - // - // - // - // ); - // } - return ( - }> - {/* */} - - - - {/* */} - - ); -}; - -export default CollectionPortfolioChartContainer; -// useEffect(() => { -// console.log('ADJUSTED TIME RANGE ID:', newNivoChartData[0].id); -// const oneDay = newNivoChartData[0].id; -// const sevenDays = newNivoChartData[1].id; -// const thirtyDays = newNivoChartData[2].id; -// const ninetyDays = newNivoChartData[3].id; -// const oneEightyDays = newNivoChartData[4].id; -// const twoSeventyDays = newNivoChartData[5].id; -// const threeSixtyFiveDays = newNivoChartData[6].id; - -// setTimeRangeIds([ -// oneDay, -// sevenDays, -// thirtyDays, -// ninetyDays, -// oneEightyDays, -// twoSeventyDays, -// threeSixtyFiveDays, -// ]); - -// // console.log('ALL RANES:', oneDay, sevenDays, thirtyDays, ninetyDays); -// if (timeRange.id === '24hr') { -// setSelectedChartData(oneDay); -// } -// if (timeRange.id === '7d') { -// console.log('SETTING SEVEN DAYS:', sevenDays); -// setSelectedChartData(sevenDays); -// } -// if (timeRange.id === '30d') { -// setSelectedChartData(thirtyDays); -// } -// if (timeRange.id === '90d') { -// setSelectedChartData(ninetyDays); -// } -// if (timeRange.id === '180d') { -// setSelectedChartData(oneEightyDays); -// } -// if (timeRange.id === '270d') { -// setSelectedChartData(twoSeventyDays); -// } -// if (timeRange.id === '365d') { -// setSelectedChartData(threeSixtyFiveDays); -// } -// }, [newNivoChartData, timeRange]); // Dependencies - -// if (!selectedChartData || !timeRange) { -// return ; -// } diff --git a/src/layout/collection/collectionGrids/cards-chart/LinearChart.js b/src/layout/collection/collectionGrids/cards-chart/LinearChart.js deleted file mode 100644 index edce7fe..0000000 --- a/src/layout/collection/collectionGrids/cards-chart/LinearChart.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import ChartWrapper from '../../../REUSABLE_COMPONENTS/unique/ChartWrapper'; -import FixedHeightCardWrapper from '../../../REUSABLE_COMPONENTS/unique/FixedHeightCardWrapper'; -import InfoStackWrapper from '../../../REUSABLE_COMPONENTS/unique/InfoStackWrapper'; -import uniqueTheme from '../../../REUSABLE_COMPONENTS/unique/uniqueTheme'; -import DynamicCollectionDestructuring from '../../data/statList'; -import { ChartConfiguration } from './ChartConfigs'; -import ChartErrorBoundary from './ChartErrorBoundary'; - -const LinearChart = ({ height, specialPoints, timeRange, nivoData }) => { - console.log('NIVODATA ', nivoData); - return ( - - - - ); -}; - -// Define prop types for LinearChart component -LinearChart.propTypes = { - height: PropTypes.number.isRequired, - specialPoints: PropTypes.arrayOf(PropTypes.object), - timeRange: PropTypes.string.isRequired, - nivoData: PropTypes.object.isRequired, -}; - -export default LinearChart; diff --git a/src/layout/collection/collectionGrids/cards-chart/UpdaterAndStatisticsRow.jsx b/src/layout/collection/collectionGrids/cards-chart/UpdaterAndStatisticsRow.jsx index d10860e..d11a7a0 100644 --- a/src/layout/collection/collectionGrids/cards-chart/UpdaterAndStatisticsRow.jsx +++ b/src/layout/collection/collectionGrids/cards-chart/UpdaterAndStatisticsRow.jsx @@ -14,33 +14,25 @@ import ThemeSelector from '../../../../components/forms/selectors/ThemeSelector' * @param {Object} props.timeRange Selected time range for statistics. * @param {Object} props.stats Collection statistics. */ -export const UpdaterAndStatisticsRow = ({ - isSmall, - socket, - timeRange, - stats, -}) => ( +export const UpdaterAndStatisticsRow = ({ isSmall }) => ( {/* Update Status Box */} - + {/* Time Range Selector */} - + {/* Collection Statistics Selector */} - + diff --git a/src/layout/collection/collectionGrids/cards-datatable/DataTableBodyComponent.jsx b/src/layout/collection/collectionGrids/cards-datatable/DataTableBodyComponent.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/layout/collection/collectionGrids/cards-datatable/DataTableHeadCell.jsx b/src/layout/collection/collectionGrids/cards-datatable/DataTableHeadCell.jsx index 7ee33a1..8b87dc4 100644 --- a/src/layout/collection/collectionGrids/cards-datatable/DataTableHeadCell.jsx +++ b/src/layout/collection/collectionGrids/cards-datatable/DataTableHeadCell.jsx @@ -1,110 +1,113 @@ -// prop-types is a library for typechecking of props +import { useMemo } from 'react'; import PropTypes from 'prop-types'; - -// @mui material components -import Icon from '@mui/material/Icon'; - -// Material Dashboard 2 React components - -// Material Dashboard 2 React contexts +import { TableRow, Checkbox, Icon } from '@mui/material'; import MDBox from '../../../REUSABLE_COMPONENTS/MDBOX'; -import { useMode } from '../../../../context'; import MDTypography from '../../../REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; +import { useMode } from '../../../../context'; +import FlexBetween from '../../../REUSABLE_COMPONENTS/FlexBetween'; -function DataTableHeadCell({ width, children, sorted, align, ...rest }) { +const DataTableHeadCell = ({ headerGroups, isSorted, setSortedValue }) => { const { theme } = useMode(); - return ( - + + const renderCellContent = (column, idx) => { + const sorted = setSortedValue(column, isSorted); + const shouldShowIcons = column.showIcons; + + return ( - - {children} - {' '} - {sorted && ( - + {column.render('Header')} + + {shouldShowIcons && sorted && ( - arrow_drop_up - - - arrow_drop_down + + arrow_drop_up + + + arrow_drop_down + - - )} + )} +
- - ); -} + ); + }; -// Setting default values for the props of DataTableHeadCell -DataTableHeadCell.defaultProps = { - width: 'auto', - sorted: 'none', - align: 'left', + return useMemo( + () => ( + <> + {headerGroups.map((headerGroup, key) => ( + + {headerGroup.headers.map(renderCellContent)} + + ))} + + ), + [ + headerGroups, + isSorted, + setSortedValue, + theme.palette.divider, + theme.typography.fontWeightMedium, + ] + ); }; -// Typechecking props for the DataTableHeadCell DataTableHeadCell.propTypes = { - width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - children: PropTypes.node.isRequired, - sorted: PropTypes.oneOf([false, 'none', 'asce', 'desc']), - align: PropTypes.oneOf(['left', 'right', 'center']), + headerGroups: PropTypes.array.isRequired, + isSorted: PropTypes.bool.isRequired, + setSortedValue: PropTypes.func.isRequired, }; export default DataTableHeadCell; diff --git a/src/layout/collection/collectionGrids/cards-datatable/DataTableHeadComponent.jsx b/src/layout/collection/collectionGrids/cards-datatable/DataTableHeadComponent.jsx deleted file mode 100644 index e382b4d..0000000 --- a/src/layout/collection/collectionGrids/cards-datatable/DataTableHeadComponent.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import { TableRow } from '@mui/material'; -import MDBox from '../../../REUSABLE_COMPONENTS/MDBOX'; -import DataTableHeadCell from './DataTableHeadCell'; - -export const DataTableHeadComponent = (props) => { - const { - onSelectAllClick, - order, - orderBy, - numSelected, - rowCount, - onRequestSort, - headerGroups, - isSorted, - setSortedValue, - } = props; - const createSortHandler = (property) => (event) => { - onRequestSort(event, property); - }; - - return ( - - {headerGroups?.map((headerGroup, key) => ( - - {/* - 0 && numSelected < rowCount} - checked={rowCount > 0 && numSelected === rowCount} - onChange={onSelectAllClick} - /> - */} - {headerGroup.headers.map((column, idx) => ( - - {column.render('Header')} - - ))} - - ))} - - ); -}; diff --git a/src/layout/collection/collectionGrids/cards-datatable/OptionsComponent.jsx b/src/layout/collection/collectionGrids/cards-datatable/OptionsComponent.jsx deleted file mode 100644 index c5fdad8..0000000 --- a/src/layout/collection/collectionGrids/cards-datatable/OptionsComponent.jsx +++ /dev/null @@ -1,53 +0,0 @@ -// OptionsComponent.jsx -import React from 'react'; -import MDBox from '../../../REUSABLE_COMPONENTS/MDBOX'; -import { Autocomplete, Grid, TextField } from '@mui/material'; - -const OptionsComponent = ({ - canSearch, - search, - handleSearchChange, - pageSize, - setPageSize, - pageOptions, -}) => ( - - - {canSearch && ( - - - - )} - - setPageSize(parseInt(newValue, 10))} - options={pageOptions?.map((option) => option?.toString())} - renderInput={(params) => ( - - )} - /> - - - -); - -export default OptionsComponent; diff --git a/src/layout/collection/collectionGrids/cards-datatable/index.jsx b/src/layout/collection/collectionGrids/cards-datatable/index.jsx index 64c80d3..8c371b9 100644 --- a/src/layout/collection/collectionGrids/cards-datatable/index.jsx +++ b/src/layout/collection/collectionGrids/cards-datatable/index.jsx @@ -13,25 +13,14 @@ import { import Table from '@mui/material/Table'; import TableContainer from '@mui/material/TableContainer'; import TableRow from '@mui/material/TableRow'; -import Icon from '@mui/material/Icon'; import DataTableBodyCell from './DataTableBodyCell'; -import useScreenWidth from '../../../../context/hooks/useScreenWidth'; -import { - Box, - Button, - Checkbox, - Grid, - Paper, - TableBody, - TextField, -} from '@mui/material'; +import { Box, Button, Checkbox, Grid, Paper, TableBody } from '@mui/material'; import PaginationComponent from './PaginationComponent'; -import OptionsComponent from './OptionsComponent'; -import { DataTableHeadComponent } from './DataTableHeadComponent'; -import BoxHeader from '../../../REUSABLE_COMPONENTS/BoxHeader'; +import OptionsComponent from '../../../../components/forms/OptionsComponent'; import GenericActionButtons from '../../../../components/buttons/actionButtons/GenericActionButtons'; -import { enqueueSnackbar } from 'notistack'; import { useMode } from '../../../../context'; +import DataTableHeadCell from './DataTableHeadCell'; +import FlexBetween from '../../../REUSABLE_COMPONENTS/FlexBetween'; const setSortedValue = (column, isSorted) => { let sortedValue; @@ -55,25 +44,15 @@ function DataTable({ noEndBorder, tableSize, }) { - // ** New Custom Breakpoints for Tracking Visible Table Data ** - // 800px - hide total price - // 650px = hide quantity - // 500px - hdie check box const { theme } = useMode(); const [showTotalPrice, setShowTotalPrice] = useState(window.innerWidth > 800); - const [showQuantity, setShowQuantity] = useState(window.innerWidth > 650); const [showSelection, setShowSelection] = useState(window.innerWidth > 500); - const [showPrice, setShowPrice] = useState(window.innerWidth > 445); useEffect(() => { const handleResize = () => { setShowTotalPrice(window.innerWidth > 800); - setShowQuantity(window.innerWidth > 650); setShowSelection(window.innerWidth > 500); - setShowPrice(window.innerWidth > 445); }; - window.addEventListener('resize', handleResize); - return () => { window.removeEventListener('resize', handleResize); }; @@ -82,47 +61,38 @@ function DataTable({ const data = useMemo(() => table.data, [table.data]); const columns = useMemo(() => { let baseColumns = [ - // Only include the selection column if showSelection is true showSelection && { id: 'selection', + showIcons: false, Header: ({ getToggleAllRowsSelectedProps }) => ( ), Cell: ({ row }) => , + // Apply a fixed width to the checkbox column + // width: 30, // Adjust the width as needed + // minWidth: 30, // Ensure it doesn't get smaller than the set width + // maxWidth: 30, // Ensure it doesn't get larger than the set width }, { Header: 'Name', accessor: 'name' }, - // { Header: 'Price', accessor: 'price' }, + { Header: 'Price', accessor: 'price' }, + + { Header: 'Quantity', accessor: 'quantity', showIcons: false }, { id: 'action', Header: 'Action', accessor: 'action', + showIcons: false, + Cell: ({ value }) => ( console.log('clicked')} - onSuccess={() => - enqueueSnackbar( - { - title: 'Action successful', - message: `Card added to ${value} successfully.`, - }, - 'success', - null - ) - } - onFailure={(error) => - enqueueSnackbar( - { - title: 'Action failed', - message: `Failed to add card to ${value}.`, - }, - 'error', - error - ) - } + onSuccess={() => console.log('success')} + onFailure={(error) => console.log(error)} page={'Collection'} cardSize={'small'} + variant="data-table" /> ), }, @@ -133,21 +103,9 @@ function DataTable({ accessor: 'tPrice', }); } - if (tableSize !== 'large' && showQuantity) { - baseColumns.push({ - Header: 'Quantity', - accessor: 'quantity', - }); - } - if (tableSize !== 'large' && showPrice) { - baseColumns.push({ - Header: 'Price', - accessor: 'price', - }); - } // Filter out any falsey values to remove the conditionally included columns when not shown return baseColumns.filter(Boolean); - }, [showTotalPrice, showQuantity, showSelection, tableSize, showPrice]); + }, [showTotalPrice, showSelection, tableSize]); const defaultPageSize = useMemo( () => entriesPerPage.defaultValue, @@ -228,6 +186,9 @@ function DataTable({ '& .MuiDataGrid-columnSeparator': { visibility: 'hidden', }, + '& .MuiTableRow-root': { + flexGrow: 1, + }, }} > @@ -241,56 +202,73 @@ function DataTable({ pageOptions={pageSizeOptions} /> {/* Table */} - - {}} - headerGroups={headerGroups} - isSorted={isSorted} - setSortedValue={setSortedValue} - /> - {/* Table Body */} - - {page.map((row, key) => { - prepareRow(row); - return ( - - {row.cells.map((cell, idx) => ( - - {cell.render('Cell')} - - ))} - - ); - })} - -
+ + + + {}} + headerGroups={headerGroups} + isSorted={isSorted} + setSortedValue={setSortedValue} + /> + + {/* Table Body */} + + {page.map((row, key) => { + prepareRow(row); + return ( + + {' '} + + {row.cells.map((cell, idx) => ( + + {cell.render('Cell')} + + ))} + + + ); + })} + +
+
{/* Pagination */} - + + +
); diff --git a/src/layout/collection/collectionGrids/cards-datatable/useSkeletonLoader.jsx b/src/layout/collection/collectionGrids/cards-datatable/useSkeletonLoader.jsx index c296376..3eedab4 100644 --- a/src/layout/collection/collectionGrids/cards-datatable/useSkeletonLoader.jsx +++ b/src/layout/collection/collectionGrids/cards-datatable/useSkeletonLoader.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import { Skeleton, Box, Stack, Grid } from '@mui/material'; +import { Box, Stack, Grid } from '@mui/material'; +import { Skeleton } from '@mui/joy'; // Enhanced skeleton configurations for a wider variety of types const skeletonVariants = { @@ -116,45 +117,6 @@ const useSkeletonLoader = () => { }, ...props }) => { - // if (type === 'grid') { - // const { variant, width, height } = skeletonVariants.grid; - // const typesKeyMap = contentProps.types.reduce( - // (acc, type) => ({ ...acc, [contentProps.numOfItems]: type }), - // {} - // ); - // console.log(typesKeyMap); - // const selectedVariant = typesKeyMap[count] || variant; - - // // Render skeletons within Grid container for 'grid' type - // return ( - // - // {Array.from({ length: count }, (_, index) => ( - // - // {/* Customize Grid item props as needed */} - // - // - // ))} - // - // ); - // } - // const skeletonVariants = { - // grid: { variant: 'rect', width: 210, height: 118 }, - // text: { variant: 'text', width: '100%', height: 20, marginBottom: 1 }, - // // Add other types as needed... - // }; - const generateVariantSequence = () => { return contentProps?.typeData?.flatMap((item) => Array.from({ length: item.num }, () => item.type) @@ -182,6 +144,25 @@ const useSkeletonLoader = () => { ); } + if (type === 'pieChart') { + return ( + + {' '} + {/* */} + + ); + } const { variant, width, height, marginBottom } = skeletonVariants[type] || skeletonVariants.text; diff --git a/src/layout/collection/collectionGrids/collections-list/CollectionListItem.jsx b/src/layout/collection/collectionGrids/collections-list/CollectionListItem.jsx index ad57aa1..9510cd2 100644 --- a/src/layout/collection/collectionGrids/collections-list/CollectionListItem.jsx +++ b/src/layout/collection/collectionGrids/collections-list/CollectionListItem.jsx @@ -1,175 +1,187 @@ -/* eslint-disable react/display-name */ -import React, { memo, useCallback, useRef, useState } from 'react'; -import { Card, CardActionArea, Grid, Tooltip } from '@mui/material'; -import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; -import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; -import PropTypes from 'prop-types'; +import React, { memo, useCallback } from 'react'; import { - useAuthContext, - useMode, - useVisibilityContext, -} from '../../../../context'; -import LongMenu from '../../../../layout/navigation/LongMenu'; -import useCollectionVisibility from '../../../../context/hooks/useCollectionVisibility'; + Box, + Card, + CardActionArea, + CardContent, + Grid, + Tooltip, +} from '@mui/material'; +import PropTypes from 'prop-types'; import MDBox from '../../../../layout/REUSABLE_COMPONENTS/MDBOX'; -import MDTypography from '../../../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; -import { - StyledCollectionListCard, - StyledCollectionListCardContent, -} from '../../../../pages/pageStyles/StyledComponents'; import useCollectionManager from '../../../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; -import CollectionDialog from '../../../../components/dialogs/CollectionDialog'; import useSelectedCollection from '../../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; import useDialogState from '../../../../context/hooks/useDialogState'; -import SimpleCard from '../../../REUSABLE_COMPONENTS/unique/SimpleCard'; -import uniqueTheme from '../../../REUSABLE_COMPONENTS/unique/uniqueTheme'; +import { useMode } from '../../../../context'; +import CollectionDialog from '../../../../components/dialogs/CollectionDialog'; +import LongMenu from '../../../../layout/navigation/LongMenu'; +import MDTypography from '../../../REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; +import RCChange from '../../../REUSABLE_COMPONENTS/RC/RCChange'; +import RCInfoItem from '../../../REUSABLE_COMPONENTS/RCInfoItem'; +import { useVisibility } from '../../../../context/hooks/useVisibility'; +import { roundToNearestTenth } from '../../../../context/Helpers'; -const CollectionInfoItem = ({ label, value, sx }) => ( - - - - {label}: {value} - - - {/* - {label}: {value} - */} - -); +// const CollectionInfoItem = ({ label, value, theme }) => ( +// +// +// +// {`${label}:`} +// +// +// {value} +// +// +// +// ); + +// CollectionInfoItem.propTypes = { +// label: PropTypes.string.isRequired, +// value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, +// theme: PropTypes.object.isRequired, +// }; const CollectionListItem = memo(({ collection }) => { const { theme } = useMode(); - const roundToNearestTenth = (number) => Math.ceil(number / 10) * 10; - const ref = useRef(null); const { deleteCollection } = useCollectionManager(); - const { - handleBackToCollections, - showCollections, - selectedCollection, - handleSelectCollection, - toggleShowCollections, - } = useSelectedCollection(); - const { - isCollectionVisible, - toggleCollectionVisibility, - // dialogStates, - // toggleDialog, - } = useVisibilityContext(); - + const { handleSelectCollection } = useSelectedCollection(); const { dialogState, openDialog, closeDialog } = useDialogState({ isEditCollectionDialogOpen: false, }); + const handleOpenDialog = useCallback((collection) => { - openDialog('editCollectionDialog', collection); - console.log(collection); - }, []); - const handleCloseDialog = useCallback(() => { - closeDialog('editCollectionDialog'); + openDialog('isEditCollectionDialogOpen', collection); }, []); const handleDelete = async () => { await deleteCollection(collection?._id); - // setShowOptions(false); // Close the options menu after deletion }; + const renderToolTip = () => (
- - handleOpenDialog(collection)} - onDelete={handleDelete} // Pass the async delete function - onSelect={() => handleSelectCollection(collection)} - onHide={() => toggleShowCollections(false)} - onStats={() => console.log('Stats:', collection)} - onView={() => console.log('View:', collection)} - // onClose={() => setShowOptions(false)} - collectionId={collection?._id} - ref={ref} - /> - + handleOpenDialog(collection)} + onDelete={handleDelete} + onSelect={() => handleSelectCollection(collection)} + // onHide={() => toggleShowCollections(false)} + collectionId={collection?._id} + collection={collection} + />
); - const renderPercentageChange = useCallback(() => { - const percentageChange = - collection?.collectionStatistics?.percentageChange || 0; - return ( - 0 ? 'success' : 'error'} - sx={{ display: 'flex', alignItems: 'center' }} - > - {percentageChange > 0 ? : } - {percentageChange}% - - ); - }, [collection]); + + const percentageChange = + collection?.collectionStatistics?.percentageChange || 0; + const handleSelection = useCallback(() => { handleSelectCollection(collection); - toggleCollectionVisibility(); + // toggleShowCollections(); }, [collection]); return ( - - - - - - - - - {renderPercentageChange()} - - + + + + + + + + 0} + change={percentageChange} + rangeLevel="24hr" // Default value; replace with actual data if available /> - + - {renderToolTip()} + + + {renderToolTip()} + + + {/* */} {dialogState.isEditCollectionDialogOpen && ( handleCloseDialog()} + onClose={() => closeDialog('isEditCollectionDialogOpen')} + // onClose={() => closeDialog('isAddDeckDialogOpen')} + collectionData={collection} isNew={false} - collectionMode="edit" - collectionData={{ - name: collection?.name, - description: collection?.description, - }} /> )} - + ); }); +CollectionListItem.displayName = 'CollectionListItem'; + CollectionListItem.propTypes = { collection: PropTypes.object.isRequired, - isSelected: PropTypes.bool, -}; - -CollectionListItem.defaultProps = { - isSelected: false, }; export default CollectionListItem; diff --git a/src/layout/collection/collectionGrids/collections-list/SelectCollectionHeader.jsx b/src/layout/collection/collectionGrids/collections-list/SelectCollectionHeader.jsx index 3ddb427..9002817 100644 --- a/src/layout/collection/collectionGrids/collections-list/SelectCollectionHeader.jsx +++ b/src/layout/collection/collectionGrids/collections-list/SelectCollectionHeader.jsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react'; -import { Grid, Button, Box } from '@mui/material'; +import { Grid, Button, Box, useMediaQuery } from '@mui/material'; import { useAuthContext, useFormContext, @@ -7,42 +7,13 @@ import { useUserContext, } from '../../../../context'; import { Card, Typography } from '@mui/joy'; -import MDButton from '../../../REUSABLE_COMPONENTS/MDBUTTON'; -import CustomButton from '../../../../components/buttons/other/CustomButton'; import useSkeletonLoader from '../cards-datatable/useSkeletonLoader'; -import IconStatWrapper from '../../../REUSABLE_COMPONENTS/unique/IconStatWrapper'; import uniqueTheme from '../../../REUSABLE_COMPONENTS/unique/uniqueTheme'; import SimpleButton from '../../../REUSABLE_COMPONENTS/unique/SimpleButton'; import styled from 'styled-components'; import SimpleCard from '../../../REUSABLE_COMPONENTS/unique/SimpleCard'; -import FlexBetween from '../../../REUSABLE_COMPONENTS/FlexBetween'; import SimpleSectionHeader from '../../../REUSABLE_COMPONENTS/unique/SimpleSectionHeader'; -import MDBox from '../../../REUSABLE_COMPONENTS/MDBOX'; -const StyledButtonWrapper = styled.div` - margin-bottom: 1rem; -`; -const SelectCollectionHeaderSkeleton = () => { - const { SkeletonLoader } = useSkeletonLoader(); - - return ( - - - - - - - - - - - - ); -}; +import { PageHeaderSkeleton } from '../../../REUSABLE_COMPONENTS/SkeletonVariants'; const FlexContainer = styled(Box)` display: flex; @@ -52,12 +23,12 @@ const FlexContainer = styled(Box)` padding: ${({ theme }) => theme.spacing(1, 2)}; `; -const HeaderContainer = styled.div` +const HeaderContainer = styled(Box)` flex: 1; - max-width: 25%; + max-width: 50%; `; -const ButtonContainer = styled.div` +const ButtonContainer = styled(Box)` flex: 1; display: flex; justify-content: flex-end; @@ -68,9 +39,8 @@ const SelectCollectionHeader = ({ openNewDialog }) => { const { theme } = useMode(); const { setCurrentForm } = useFormContext(); const { user } = useUserContext(); - if (!user) { - return ; + return ; } return ( diff --git a/src/layout/collection/collectionGrids/collections-list/SelectCollectionList.jsx b/src/layout/collection/collectionGrids/collections-list/SelectCollectionList.jsx index d4dc736..6e4300f 100644 --- a/src/layout/collection/collectionGrids/collections-list/SelectCollectionList.jsx +++ b/src/layout/collection/collectionGrids/collections-list/SelectCollectionList.jsx @@ -1,77 +1,4 @@ -// import React, { memo, useCallback, useEffect, useRef, useState } from 'react'; -// import { -// Box, -// Card, -// CardActionArea, -// Collapse, -// Grid, -// List, -// Skeleton, -// } from '@mui/material'; -// import PropTypes from 'prop-types'; -// import { TransitionGroup } from 'react-transition-group'; -// import CollectionListItem from './CollectionListItem'; -// import useSelectedCollection from '../../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -// import SimpleCard from '../../../REUSABLE_COMPONENTS/unique/SimpleCard'; -// import uniqueTheme from '../../../REUSABLE_COMPONENTS/unique/uniqueTheme'; -// import FlexBetween from '../../../REUSABLE_COMPONENTS/FlexBetween'; -// // -// // -// const CollectionListItemSkeleton = ({ count }) => { -// return ( -// -// -// -// -// -// -// -// -// -// -// -// ); -// }; - -// CollectionListItemSkeleton.propTypes = { -// count: PropTypes.number, -// }; - -// CollectionListItemSkeleton.displayName = 'CollectionListItemSkeleton'; - -// const SelectCollectionList = ({ openDialog }) => { -// const { allCollections } = useSelectedCollection(); -// const minListSize = 5; -// const collectionCount = allCollections?.length || 0; -// const skeletonsNeeded = Math.max(0, minListSize - collectionCount); - -// return ( -// -// -// -// {allCollections?.map((collection, index) => ( -// -// -// {skeletonsNeeded > 0 && ( -// -// )} -// -// ))} -// -// -// -// ); -// }; - -// SelectCollectionList.propTypes = { -// openDialog: PropTypes.func.isRequired, -// }; - -// export default SelectCollectionList; -import React, { memo, useCallback, useEffect, useRef, useState } from 'react'; +import React, { memo, useEffect, useRef, useState } from 'react'; import { Box, Card, @@ -88,64 +15,78 @@ import useSelectedCollection from '../../../../context/MAIN_CONTEXT/CollectionCo import styled from 'styled-components'; import SimpleCard from '../../../REUSABLE_COMPONENTS/unique/SimpleCard'; import uniqueTheme from '../../../REUSABLE_COMPONENTS/unique/uniqueTheme'; -const FlexContainer = styled(Box)` - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - padding: ${({ theme }) => theme.spacing(1, 2)}; -`; -const CollectionListItemSkeleton = ({ count }) => { - return [...Array(count).keys()].map((index) => ( - - - - - - - - - - - - - - )); -}; +import { CollectionListItemSkeleton } from '../../../REUSABLE_COMPONENTS/SkeletonVariants'; -CollectionListItemSkeleton.propTypes = { - count: PropTypes.number.isRequired, -}; +const SelectCollectionList = ({ + openDialog, + handleSelectAndShowCollection, +}) => { + const { + allCollections, + allIds, + handleSelectCollection, + toggleShowCollections, + } = useSelectedCollection(); + const [collectionList, setCollectionList] = useState([]); + const numCollections = allIds?.length || 0; + const nonSkeletonCount = useRef(0); + + useEffect(() => { + const minItems = 5; + const numRequired = + minItems - numCollections > 0 ? minItems - numCollections : 0; + nonSkeletonCount.current = numCollections; + + const allSkeletonCollections = [...Array(numRequired).keys()].map( + (index) => ( + + ) + ); + const combinedCollections = allCollections + .map((collection, index) => ( + + { + handleSelectAndShowCollection(collection); + }} + > + + + + )) + .concat(allSkeletonCollections); + + setCollectionList(combinedCollections); + }, [allIds?.length]); // Dependency on allIds.length instead of allIds and allCollections -const SelectCollectionList = ({ openDialog }) => { - const { allCollections } = useSelectedCollection(); - // const [skeletonCount, setSkeletonCount] = useState(0); - const listRef = useRef(); - const minItems = 5; - const numRequired = minItems - (allCollections?.length || 0); - const allSkeletonCollections = [...Array(numRequired).keys()].map((index) => ( - - )); - const combinedCollections = [...allCollections, ...allSkeletonCollections]; return ( - {combinedCollections?.map((collection, index) => ( - - - - ))} - {/* */} + {collectionList?.map((item, index) => + React.isValidElement(item) ? ( + item + ) : ( + + ) + )} diff --git a/src/layout/collection/collectionGrids/collections-list/StatBoard.jsx b/src/layout/collection/collectionGrids/collections-list/StatBoard.jsx index c30585a..2b2a27f 100644 --- a/src/layout/collection/collectionGrids/collections-list/StatBoard.jsx +++ b/src/layout/collection/collectionGrids/collections-list/StatBoard.jsx @@ -1,40 +1,87 @@ -import { Grid } from '@mui/material'; +/* eslint-disable react/jsx-key */ +import { Box, Grid, Typography, Skeleton } from '@mui/material'; // import PieChart from './statItems/PieChart'; -// import TotalPriceStatBox from './statItems/TotalPriceStatBox'; +import TotalPriceStatBox from './statItems/TotalPriceStatBox'; // import ValuDistributionCircle from './statItems/ValuDistributionCircle'; // import PricedCardList from './statItems/PricedCardList'; import SimpleCard from '../../../REUSABLE_COMPONENTS/unique/SimpleCard'; import uniqueTheme from '../../../REUSABLE_COMPONENTS/unique/uniqueTheme'; -import { ChartArea } from '../../../../pages/pageStyles/StyledComponents'; -import NivoContainer from '../cards-chart/NivoContainer'; -import { useMode } from '../../../../context'; +import { useAppContext, useMode } from '../../../../context'; import ValuDistributionCircle from './statItems/ValuDistributionCircle'; import PricedCardList from './statItems/PricedCardList'; -import ZenEnso from '../../../../assets/animations/ZenEnso'; +import PerformanceStatBox from './statItems/PerformanceStatBox'; import MDBox from '../../../REUSABLE_COMPONENTS/MDBOX'; -// import ValueDistPieChart from './statItems/ValueDistPieChart'; +import styled from 'styled-components'; +import TotalCardsCollectedStatBox from './statItems/TotalCardsCollectedStatBox'; +import FlexBetween from '../../../REUSABLE_COMPONENTS/FlexBetween'; -// const StatBoard = () => { -// return ( -// -// -// {item} -// -// -// {item} -// -// -// {item} -// -// -// ); -// }; +const SkeletonPieChart = ({ theme }) => ( + + + Collection Value Distribution + + + +); -const StatBoard = () => { +const StatBoxes = () => { + const { theme } = useMode(); + return ( + + + + + ); +}; +const DistCircle = () => { const { theme } = useMode(); + const colors = theme.palette.chartTheme; + const { allCollections } = useAppContext(); + if (!allCollections || allCollections.length === 0) { + return ( + + + + ); + } + return ( + + + + ); +}; + +const PriceList = () => { + const { theme } = useMode(); + const colors = theme.palette.chartTheme; + + return ( + + + + ); +}; +const StatBoard = () => { return ( { > - {/* - - - {' '} - */} - {/* */} - {/* */} - {/* - - */} - {/* */} - {/* - {/* - - */} - {/* - - */} - - - + {[, , ].map( + (component, index) => ( + + + {component} + + + ) + )} ); diff --git a/src/layout/collection/collectionGrids/collections-list/statItems/PerformanceStatBox.jsx b/src/layout/collection/collectionGrids/collections-list/statItems/PerformanceStatBox.jsx new file mode 100644 index 0000000..0cae726 --- /dev/null +++ b/src/layout/collection/collectionGrids/collections-list/statItems/PerformanceStatBox.jsx @@ -0,0 +1,52 @@ +import { Box, Typography } from '@mui/material'; +import StatBox from '../../../../REUSABLE_COMPONENTS/StatBox'; +// import useCollectionStats from '../../../../../context/MAIN_CONTEXT/CollectionContext/useCollectionStats'; +import { useAppContext, useMode } from '../../../../../context'; +import MonetizationOnIcon from '@mui/icons-material/MonetizationOn'; +import ProgressCircle from '../../../../REUSABLE_COMPONENTS/ProgressCircle'; +import MDBox from '../../../../REUSABLE_COMPONENTS/MDBOX'; +import StackedLineChartRoundedIcon from '@mui/icons-material/StackedLineChartRounded'; +const PerformanceStatBox = () => { + const { theme } = useMode(); + const colors = theme.palette.chartTheme; + const primary = colors.primary.dark; + const greenAccent = colors.greenAccent.light; + // const primary = colors.primary.default; + // const blue = colors.blueAccent.default; + const green = colors.greenAccent.default; + const greenliht = colors.greenAccent.light; + + const grey = colors.grey.default; + const { collectionMetaData } = useAppContext(); + + return ( + + + } + /> + + ); +}; + +export default PerformanceStatBox; diff --git a/src/layout/collection/collectionGrids/collections-list/statItems/PieChart.jsx b/src/layout/collection/collectionGrids/collections-list/statItems/PieChart.jsx deleted file mode 100644 index 0c36f5c..0000000 --- a/src/layout/collection/collectionGrids/collections-list/statItems/PieChart.jsx +++ /dev/null @@ -1,232 +0,0 @@ -import { mockPieData as data } from '../../../data/mockData'; -import { useMode } from '../../../../../context'; -import { ResponsivePie } from '@nivo/pie'; - -const PieChart = () => { - // const { theme } = useMode(); - // const colors = theme.palette.chartTheme; - // const lightGrey = colors.grey.lightest; - return ( - - ); -}; - -export default PieChart; diff --git a/src/layout/collection/collectionGrids/collections-list/statItems/PricedCardList.jsx b/src/layout/collection/collectionGrids/collections-list/statItems/PricedCardList.jsx index a255e6c..faaecad 100644 --- a/src/layout/collection/collectionGrids/collections-list/statItems/PricedCardList.jsx +++ b/src/layout/collection/collectionGrids/collections-list/statItems/PricedCardList.jsx @@ -1,108 +1,82 @@ import MDBox from '../../../../REUSABLE_COMPONENTS/MDBOX'; import BoxHeader from '../../../../REUSABLE_COMPONENTS/BoxHeader'; -import useSelectedCollection from '../../../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -import useCollectionStats from '../../../../../context/MAIN_CONTEXT/CollectionContext/useCollectionStats'; -import { Box } from '@mui/material'; -import { useMode } from '../../../../../context'; +import { Box, Card, CardContent } from '@mui/material'; +import { useAppContext, useMode } from '../../../../../context'; import { DataGrid } from '@mui/x-data-grid'; import prepareTableData from '../../../data/topCards'; +import styled from 'styled-components'; +import { useMemo } from 'react'; const PricedCardList = () => { - const { collectionStats, metaStats } = useCollectionStats(); - const { selectedCollection } = useSelectedCollection(); - const { data, columns } = prepareTableData(selectedCollection); const { theme } = useMode(); const colors = theme.palette.chartTheme; - const grey = theme.palette.chartTheme.grey.darkest; - const lightGrey = theme.palette.chartTheme.grey.lightest; - - const greenAccent = colors.greenAccent.default; - - // const renderCurrency = (params) => `$${params.value}`; - - // const productColumns = [ - // { - // field: '_id', - // headerName: 'ID', - // flex: 1, - // }, - // { - // field: 'expense', - // headerName: 'Expense', - // flex: 0.5, - // renderCell: renderCurrency, - // }, - // { - // field: 'price', - // headerName: 'Price', - // flex: 0.5, - // renderCell: renderCurrency, - // }, - // ]; - - // const countItems = (params) => params.value.length; - - // const transactionColumns = [ - // { - // field: '_id', - // headerName: 'ID', - // flex: 1, - // }, - // { - // field: 'buyer', - // headerName: 'Buyer', - // flex: 0.67, - // }, - // { - // field: 'amount', - // headerName: 'Amount', - // flex: 0.35, - // renderCell: renderCurrency, - // }, - // { - // field: 'productIds', - // headerName: 'Count', - // flex: 0.1, - // renderCell: countItems, - // }, - // ]; - - // Assuming productData is available in your context or props - // const productData = []; // Placeholder for actual data + const grey = colors.grey.darkest; + const lightGrey = colors.grey.lightest; + const primary = colors.primary.dark; + const greenAccent = colors.greenAccent.light; + const { cardsWithQuantities } = useAppContext(); + const topFiveCards = useMemo(() => { + return cardsWithQuantities?.sort((a, b) => b.price - a.price).slice(0, 5); + }, [cardsWithQuantities]); + const { data, columns } = useMemo( + () => prepareTableData(topFiveCards), + [topFiveCards] + ); return ( - - + - + + + acc + card.price, 0)}`} + colorVariant={greenAccent} + useSX={true} + titleVariant="h5" + paddingVariant={theme.spacing(2)} + sx={{ + color: greenAccent, + }} + /> + + + + + ); diff --git a/src/layout/collection/collectionGrids/collections-list/statItems/TotalCardsCollectedStatBox.jsx b/src/layout/collection/collectionGrids/collections-list/statItems/TotalCardsCollectedStatBox.jsx new file mode 100644 index 0000000..bdc48bd --- /dev/null +++ b/src/layout/collection/collectionGrids/collections-list/statItems/TotalCardsCollectedStatBox.jsx @@ -0,0 +1,42 @@ +import { Box } from '@mui/material'; +import StatBox from '../../../../REUSABLE_COMPONENTS/StatBox'; +// import useCollectionStats from '../../../../../context/MAIN_CONTEXT/CollectionContext/useCollectionStats'; +import { useAppContext, useMode } from '../../../../../context'; +import styled from 'styled-components'; +import FormatListNumberedRoundedIcon from '@mui/icons-material/FormatListNumberedRounded'; +const TotalCardsCollectedStatBox = () => { + const { theme } = useMode(); + const colors = theme.palette.chartTheme; + const primary = colors.primary.dark; + const greenAccent = colors.greenAccent.light; + const grey = colors.grey.dark; + const { collectionMetaData } = useAppContext(); + return ( + + + } + /> + + ); +}; + +export default TotalCardsCollectedStatBox; diff --git a/src/layout/collection/collectionGrids/collections-list/statItems/TotalPriceStatBox.jsx b/src/layout/collection/collectionGrids/collections-list/statItems/TotalPriceStatBox.jsx index 84508fb..d2ea418 100644 --- a/src/layout/collection/collectionGrids/collections-list/statItems/TotalPriceStatBox.jsx +++ b/src/layout/collection/collectionGrids/collections-list/statItems/TotalPriceStatBox.jsx @@ -1,15 +1,20 @@ -import MonetizationOnIcon from '@mui/icons-material/MonetizationOn'; -import useCollectionStats from '../../../../../context/MAIN_CONTEXT/CollectionContext/useCollectionStats'; -import { useMode } from '../../../../../context'; import { Box } from '@mui/material'; import StatBox from '../../../../REUSABLE_COMPONENTS/StatBox'; +// import useCollectionStats from '../../../../../context/MAIN_CONTEXT/CollectionContext/useCollectionStats'; +import { useAppContext, useMode } from '../../../../../context'; +import MonetizationOnIcon from '@mui/icons-material/MonetizationOn'; +import styled from 'styled-components'; const TotalPriceStatBox = () => { - const { collectionStats, metaStats } = useCollectionStats(); const { theme } = useMode(); const colors = theme.palette.chartTheme; const primary = colors.primary.dark; const greenAccent = colors.greenAccent.light; + const grey = colors.grey.dark; + + const { collectionMetaData } = useAppContext(); + const roundToNearestTenth = (num) => Math.round(num * 10) / 10; + return ( { display: 'flex', justifyContent: 'center', alignItems: 'center', + borderRadius: theme.spacing(4), height: '100%', - boxSizing: 'border-box', + minHeight: '135px', + // p: '5px', }} > - } + wrapIcon={false} + icon={} /> ); diff --git a/src/layout/collection/collectionGrids/collections-list/statItems/ValuDistributionCircle.jsx b/src/layout/collection/collectionGrids/collections-list/statItems/ValuDistributionCircle.jsx index cc1472b..b74ae5a 100644 --- a/src/layout/collection/collectionGrids/collections-list/statItems/ValuDistributionCircle.jsx +++ b/src/layout/collection/collectionGrids/collections-list/statItems/ValuDistributionCircle.jsx @@ -1,31 +1,73 @@ +/* eslint-disable max-len */ +import React from 'react'; import { Box, Typography } from '@mui/material'; +import MDBox from '../../../../REUSABLE_COMPONENTS/MDBOX'; import { useMode } from '../../../../../context'; import ProgressCircle from '../../../../REUSABLE_COMPONENTS/ProgressCircle'; -const ValuDistributionCircle = () => { +const ValuDistributionCircle = ({ collections }) => { const { theme } = useMode(); - const colors = theme.palette.chartTheme; - const primary = colors.primary.dark; - const greenAccent = colors.greenAccent.default; + const collectionMetaData = collections?.reduce( + (meta, collection) => { + meta.totalValue += collection?.totalPrice; + meta.tooltips.push( + `${collection?.name}: $${collection?.totalPrice.toFixed(2)}` + ); + return meta; + }, + { totalValue: 0, tooltips: [] } + ); + + let cumulativePercent = 0; + const gradientStops = collections + .map((collection) => { + const valuePercent = + (collection?.totalPrice / collectionMetaData?.totalValue) * 100; + const stop = `${theme.palette.chartTheme.blueAccent.default} ${cumulativePercent}%, ${theme.palette.chartTheme.blueAccent.default} ${cumulativePercent + valuePercent}%`; + cumulativePercent += valuePercent; + return stop; + }) + .join(', '); + + const tooltipContent = collectionMetaData.tooltips.join('\n'); return ( - - - Campaign - - - - - $48,352 revenue generated + + + + Collection Value Distribution + + + + Total Value: ${collectionMetaData.totalValue.toFixed(2)} + + + Includes extra misc expenditures and costs - Includes extra misc expenditures and costs - + ); }; diff --git a/src/layout/collection/collectionGrids/collections-list/statItems/ValueDistPieChart.jsx b/src/layout/collection/collectionGrids/collections-list/statItems/ValueDistPieChart.jsx deleted file mode 100644 index a2b3d6c..0000000 --- a/src/layout/collection/collectionGrids/collections-list/statItems/ValueDistPieChart.jsx +++ /dev/null @@ -1,27 +0,0 @@ -// import { Box } from '@mui/material'; -// import Header from './Header'; -// import NivoContainer from '../../cards-chart/NivoContainer'; -// import { useMode } from '../../../../../context'; -// import { ChartArea } from '../../../../../pages/pageStyles/StyledComponents'; -// import ChartErrorBoundary from '../../cards-chart/ChartErrorBoundary'; -// import PieChart from './PieChart'; - -// const ValueDistPieChart = () => { -// const { theme } = useMode(); -// return ( -// -//
-// {/* */} -// -// {/* */} -// -// -// -// {/* */} -// -// {/* */} -// -// ); -// }; - -// export default ValueDistPieChart; diff --git a/src/layout/collection/collectionGrids/cards-chart/GenerateNivoTestData.jsx b/src/layout/collection/data/GenerateNivoTestData.jsx similarity index 100% rename from src/layout/collection/collectionGrids/cards-chart/GenerateNivoTestData.jsx rename to src/layout/collection/data/GenerateNivoTestData.jsx diff --git a/src/layout/collection/data/collectionPortfolioData.jsx b/src/layout/collection/data/collectionPortfolioData.jsx index e77ddca..ae256ca 100644 --- a/src/layout/collection/data/collectionPortfolioData.jsx +++ b/src/layout/collection/data/collectionPortfolioData.jsx @@ -84,6 +84,7 @@ export default function prepareTableData(selectedCards) { Cell: ({ value }) => ( console.log('clicked')} onSuccess={() => diff --git a/src/layout/collection/data/topCards.jsx b/src/layout/collection/data/topCards.jsx index f9828d9..f388e56 100644 --- a/src/layout/collection/data/topCards.jsx +++ b/src/layout/collection/data/topCards.jsx @@ -1,95 +1,78 @@ -import Icon from '@mui/material/Icon'; -// Images -import MDTypography from '../../REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; import React from 'react'; -import GenericActionButtons from '../../../components/buttons/actionButtons/GenericActionButtons'; -import { useSnackbar } from 'notistack'; +import MDTypography from '../../REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; + const Name = ({ name }) => ( {name} ); + const Price = ({ price }) => ( {price} ); -const TPrice = ({ tPrice }) => ( - - {tPrice} - -); + const Quantity = ({ quantity }) => ( {quantity} ); -export default function prepareTableData(selectedCards) { - const roundToNearestTenth = (value) => Math.round(value * 10) / 10; - const columns = React.useMemo( - () => [ - { - Header: 'Name', - accessor: 'name', - id: 'name', - Cell: ({ value }) => , - }, - { - Header: 'Price', - accessor: 'price', - id: 'price', - Cell: ({ value }) => , - }, - { - Header: 'Quantity', - accessor: 'quantity', - id: 'quantity', - Cell: ({ value }) => , - }, - ], - [] - ); - const data = React.useMemo(() => { - if (!selectedCards || selectedCards.length === 0) { - return []; - } - // Sort by totalPrice in descending order and take the top 5 - const topFiveCards = selectedCards - ?.sort((a, b) => b.price - a.price) - .slice(0, 5) - .map((card) => ({ - ...card, - tPrice: roundToNearestTenth(card.totalPrice), - action: card, - })); +export default function prepareTableData(topCards) { + if (!topCards || topCards.length === 0) { + return { columns: [], data: [] }; + } + console.log('topFiveCards:', topCards); + + const columns = [ + { + field: 'name', + headerName: 'Name', + renderCell: (params) => , + width: 250, + flex: 1, + }, + { + field: 'price', + headerName: 'Price', + renderCell: (params) => , + maxWidth: 50, + flex: 1, + }, + { + field: 'quantity', + headerName: 'Quantity', + renderCell: (params) => , + maxWidth: 50, + flex: 1, + }, + ]; - return topFiveCards; - }, [selectedCards]); + const data = topCards?.map((card, index) => ({ + id: card.id, + name: card.name, + price: card.price, + quantity: card.quantity, + })); return { columns, data }; } diff --git a/src/layout/collection/index.jsx b/src/layout/collection/index.jsx index 8c12811..8d489a1 100644 --- a/src/layout/collection/index.jsx +++ b/src/layout/collection/index.jsx @@ -1,527 +1,261 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import propTypes from 'prop-types'; -import useCollectionVisibility from '../../context/hooks/useCollectionVisibility'; -import { - useCollectionStore, - useMode, - useVisibilityContext, -} from '../../context'; -import DashboardLayout from '../Containers/DashBoardLayout'; +import { useMode } from '../../context'; +import DashboardLayout from '../REUSABLE_COMPONENTS/DashBoardLayout'; import MDBox from '../REUSABLE_COMPONENTS/MDBOX'; -import PageLayout from '../Containers/PageLayout'; import { PortfolioBoxA } from '../../pages/pageStyles/StyledComponents'; -import { Grid, Card, Typography } from '@mui/material'; -import { useFormContext } from '../../context'; +import { Box, Card, Grid } from '@mui/material'; import CollectionDialog from '../../components/dialogs/CollectionDialog'; import collectionPortfolioData from './data/collectionPortfolioData'; import ChartGridLayout from './collectionGrids/ChartGridLayout'; -import StatisticsCardGrid from './collectionGrids/StatisticsCardsGrid'; import CollectionPortfolioHeader from './sub-components/CollectionPortfolioHeader'; -import CollectionListStats from './collectionGrids/CollectionListStats'; import SelectCollectionList from './collectionGrids/collections-list/SelectCollectionList'; import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -import useSnackBar from '../../context/hooks/useSnackBar'; -import { withDynamicSnackbar } from '../REUSABLE_COMPONENTS/HOC/DynamicSnackbar'; -import SelectionErrorDialog from '../../components/dialogs/SelectionErrorDialog'; -import useCollectionManager from '../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; -import useSkeletonLoader from './collectionGrids/cards-datatable/useSkeletonLoader'; -import { DEFAULT_COLLECTION } from '../../context/constants'; import useDialogState from '../../context/hooks/useDialogState'; import SelectCollectionHeader from './collectionGrids/collections-list/SelectCollectionHeader'; import DashboardBox from '../REUSABLE_COMPONENTS/DashboardBox'; import StatBoard from './collectionGrids/collections-list/StatBoard'; +import { Tab, Tabs } from '@mui/material'; +import RCHeader from '../REUSABLE_COMPONENTS/RCHeader'; -// function SelectCollectionView({ onNewDialog, onCloseDialog, dialogState }) { -// const { theme } = useMode(); -// const { allCollections, selectedCollection } = useSelectedCollection(); -// const { currentForm } = useFormContext(); -// return ( -// -// -// -// -// - -// -// -// -// -// -// {dialogState.isDialogOpen && ( -// -// )} -// {dialogState.isSelectionErrorDialogOpen && ( -// -// )} -// -// -// ); -// } +const CollectionsView = ({ openDialog, handleTabAndSelect }) => { + const { theme } = useMode(); + return ( + + + + openDialog('isAddCollectionDialogOpen')} + /> + + + + + + openDialog('isEditCollectionDialogOpen')} + /> + + + + ); +}; +const PortfolioView = ({ + selectedCollection, + columns, + data, + handleBackToCollections, + allCollections, +}) => ( + + + + + + + + + + +); const CollectionPortfolio = () => { const { theme } = useMode(); - const { fetchCollections, hasFetchedCollections } = useCollectionManager(); const { handleBackToCollections, - // showCollections, - selectedCollection, selectedCollectionId, + selectedCollection, allCollections, - customError, - toggleShowCollections, + handleSelectCollection, } = useSelectedCollection(); - // Initially fetch collections if needed - useEffect(() => { - fetchCollections(); - }, []); - const { currentForm } = useFormContext(); - const { - isCollectionVisible, - toggleCollectionVisibility, - // dialogStates, - // toggleDialog, - } = useVisibilityContext(); - - const { dialogState, openDialog, closeDialog } = useDialogState({ - isAddCollectionDialogOpen: false, - isSelectionErrorDialogOpen: false, - isConfirmationDialogOpen: false, // New dialog state - }); - const [viewState, setViewState] = useState({ - showListOfAllCollections: isCollectionVisible, - showSelectedCollection: !isCollectionVisible, - currentCollection: selectedCollectionId, - }); - const handleAddCollectionDialogToggle = useCallback(() => { - openDialog('isAddCollectionDialogOpen'); - }, [openDialog]); - - const handleCloseAddCollectionDialog = useCallback(() => { - closeDialog('isAddCollectionDialogOpen'); - }, [closeDialog]); - - const handleSelectionErrorDialogToggle = useCallback(() => { - openDialog('isSelectionErrorDialogOpen'); - }, [openDialog]); - - const handleCloseSelectionErrorDialog = useCallback(() => { - closeDialog('isSelectionErrorDialogOpen'); - }, [closeDialog]); - // const handleDialogToggle = () => - // setDialogState((prevState) => ({ - // ...prevState, - // isDialogOpen: !prevState.isDialogOpen, - // })); - // const handleCloseDialog = () => - // setDialogState((prevState) => ({ - // ...prevState, - // isDialogOpen: false, - // })); - // const handleErrorDialog = () => - // setDialogState((prevState) => ({ - // ...prevState, - // isSelectionErrorDialogOpen: true, - // })); - // const handleCloseErrorDialog = () => - // setDialogState((prevState) => ({ - // ...prevState, - // isSelectionErrorDialogOpen: false, - // })); - useEffect(() => { - if (customError === 'Selection Error') { - handleSelectionErrorDialogToggle(); - } else { - handleCloseSelectionErrorDialog(); + const { dialogState, openDialog, closeDialog } = useDialogState(); + const { columns, data } = collectionPortfolioData(selectedCollection?.cards); + const [activeTab, setActiveTab] = useState(0); + const tabs = []; + if (selectedCollectionId) { + tabs.push(); + } + const handleTabChange = (event, newValue) => { + if (newValue === 0 || (newValue === 1 && selectedCollectionId)) { + setActiveTab(newValue); } - }, [customError]); - // const handleSelectedCollectionView = useCallback(() => { - // console.log('SWITCHING TO SELECTED COLLECTION VIEW', selectedCollectionId); - // setViewState((prevState) => ({ - // ...prevState, - // showListOfAllCollections: !prevState.showListOfAllCollections, - // showSelectedCollection: !prevState.showSelectedCollection, - // currentCollection: selectedCollectionId, - // })); - // }, [selectedCollectionId]); - const handleDialogToggle = useCallback( - (dialogName) => { - dialogState[dialogName] - ? closeDialog(dialogName) - : openDialog(dialogName); + }; + const handleSelectAndShowCollection = useCallback( + (collection) => { + handleSelectCollection(collection._id); // Assume this function sets the selectedCollectionId + setActiveTab(1); // Switch to Portfolio View tab }, - [dialogState, openDialog, closeDialog] + [handleSelectCollection] ); - // const handleViewChange = useCallback(() => { - // toggleShowCollections(); - // }, [toggleShowCollections]); - useEffect(() => { - console.log('VIEW STATE CHANGED', viewState); - console.log('ALL COLLECTION', allCollections); - console.log('SELECTED COLLECTION', selectedCollection); - console.log('SELECTED COLLECTION ID', selectedCollectionId); - console.log('IS COLLECTION VISIBLE', isCollectionVisible); - }, [ - viewState, - allCollections, - isCollectionVisible, - selectedCollection, - selectedCollectionId, - ]); - const { columns, data } = collectionPortfolioData(selectedCollection?.cards); - - // if (!hasFetchedCollections) { - // return ( - // - // - // - // {/* Directly use the SkeletonLoader with 'grid' type and customize gridProps as needed */} - // - // - // - // - // ); - // } return ( - - - {!isCollectionVisible ? ( - - - - - - - - - - - + + + + + + {tabs} + + + + {activeTab === 0 && ( + + )} - {dialogState.isAddCollectionDialogOpen && ( - - )} - {dialogState.isSelectionErrorDialogOpen && ( - - )} - - - ) : ( - // Selected collection view - - - - - - - {/* */} - - - - - - - )} - + {activeTab === 1 && selectedCollectionId && ( + { + // handleBackToCollections(); + setActiveTab(0); // Switch back to collections tab when going back + }} + allCollections={allCollections} + /> + )} + {dialogState.isAddCollectionDialogOpen && ( + closeDialog('isAddCollectionDialogOpen')} + collectionData={null} + isNew={true} + /> + )} ); }; -export default withDynamicSnackbar(CollectionPortfolio); -// { -// viewState.showListOfAllCollections && ( -// -// -// -// -// +export default CollectionPortfolio; +// const CollectionPortfolio = () => { +// const { theme } = useMode(); +// const { Transitions } = theme; +// const { +// handleBackToCollections, +// selectedCollectionId, +// selectedCollection, +// allCollections, +// showCollections, +// } = useSelectedCollection(); +// const { dialogState, openDialog, closeDialog } = useDialogState({ +// isAddCollectionDialogOpen: false, +// isSelectionErrorDialogOpen: false, +// }); -// -// -// -// -// -// {dialogState.isDialogOpen && ( -// -// )} -// {dialogState.isSelectionErrorDialogOpen && ( -// -// )} -// -// -// ); -// } -// { -// viewState.showSelectedCollection && ( -// -// -// -// { -// handleBackToCollections(); -// handleSelectedCollectionView(); -// }} -// /> -// -// -// -// -// -// -// -// -// ); -// } -// const [isDialogOpen, setDialogOpen] = useState(false); -// const [isSelectionErrorDialogOpen, setSelectionErrorDialogOpen] = -// useState(false); -// const { currentForm } = useFormContext(); -// currentForm === 'addCollectionForm' -// const openNewDialog = useCallback((addOrEdit) => { -// setDialogOpen(true); // Correctly opens the dialog -// }, []); -// const handleDialogToggle = () => setDialogOpen(!isDialogOpen); -// const handleCloseDialog = () => setDialogOpen(false); -// const handleErrorDialog = () => setSelectionErrorDialogOpen(true); -// const handleCloseErrorDialog = () => setSelectionErrorDialogOpen(false); +// const { columns, data } = collectionPortfolioData(selectedCollection?.cards); -// if (!hasFetchedCollections) { // return ( -// -// -// Loading... -// -// -// ); -// } - -// return ( -// -// -// {showCollections ? ( -// -// -// {/* HEADER */} -// -// -// -// {/* STATISTICS FEATURE GRID */} -// -// {/* */} -// -// {/* CHARTS */} -// -// -// -// -// -// ) : ( -// -// -// {/* HEADER */} -// -// +// {!selectedCollectionId ? ( +// +// +// +// -// -// -// -// {/* COLLECTION LIST STATS DISPLAY */} -// -// openDialog('isAddCollectionDialogOpen')} +// /> +// +// -// -// -// -// {/* COLLECTION LIST */} -// -// +// +// // openDialog('isEditCollectionDialogOpen')} // /> -// -// +// -// {isDialogOpen && ( -// -// )} -// {isSelectionErrorDialogOpen && ( -// -// )} -// -// +// {dialogState.isAddCollectionDialogOpen && ( +// closeDialog('isAddCollectionDialogOpen')} +// collectionData={null} +// isNew={true} +// /> +// )} +// +// +// +// ) : ( +// +// +// +// +// +// +// +// {/* */} +// +// +// +// +// +// +// // )} -// -// -// ); +// +// ); +// }; -// export default withDynamicSnackbar(CollectionPortfolio); +// export default CollectionPortfolio; diff --git a/src/layout/collection/sub-components/CollectionGoalsTimeLine.jsx b/src/layout/collection/sub-components/CollectionGoalsTimeLine.jsx deleted file mode 100644 index 5e9893b..0000000 --- a/src/layout/collection/sub-components/CollectionGoalsTimeLine.jsx +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; -import Card from '@mui/material/Card'; -import Icon from '@mui/material/Icon'; -import MDBox from 'components/MDBox'; -import MDTypography from 'components/MDTypography'; -import TimelineItem from 'examples/Timeline/TimelineItem'; - -function CollectionGoalsTimeLine({ color, icon, title, dateTime, completed }) { - return ( - - ); -} - -function GoalsOverview() { - const goals = [ - { title: '$2,000 Goal', dateTime: '1 Jan', completed: true }, - { title: '$4,000 Goal', dateTime: '15 Feb', completed: true }, - { title: '$6,000 Goal', dateTime: '10 Mar', completed: true }, - { title: '$8,000 Goal', dateTime: '5 Apr', completed: false }, - { title: '$9,000 Goal', dateTime: '30 Apr', completed: false }, - ]; - - return ( - - - - Collection Goals Overview - - - - {goals.map((goal, index) => ( - - ))} - - - ); -} - -export default GoalsOverview; diff --git a/src/layout/collection/sub-components/CollectionPortfolioHeader.jsx b/src/layout/collection/sub-components/CollectionPortfolioHeader.jsx index b6705e1..45b1920 100644 --- a/src/layout/collection/sub-components/CollectionPortfolioHeader.jsx +++ b/src/layout/collection/sub-components/CollectionPortfolioHeader.jsx @@ -6,39 +6,14 @@ import AttachMoneyIcon from '@mui/icons-material/AttachMoney'; import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered'; import TrendingUpIcon from '@mui/icons-material/TrendingUp'; import MDBox from '../../REUSABLE_COMPONENTS/MDBOX'; -import MDTypography from '../../REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; -import MDAvatar from '../../REUSABLE_COMPONENTS/MDAVATAR'; import { useMode } from '../../../context'; -import useSelectedCollection from '../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -import { TransitionGroup } from 'react-transition-group'; -import useSkeletonLoader from '../collectionGrids/cards-datatable/useSkeletonLoader'; import { DEFAULT_COLLECTION } from '../../../context/constants'; import uniqueTheme from '../../REUSABLE_COMPONENTS/unique/uniqueTheme'; -import SimpleCard from '../../REUSABLE_COMPONENTS/unique/SimpleCard'; import IconStatWrapper from '../../REUSABLE_COMPONENTS/unique/IconStatWrapper'; import DashboardBox from '../../REUSABLE_COMPONENTS/DashboardBox'; -const SelectCollectionHeaderSkeleton = () => { - const { SkeletonLoader } = useSkeletonLoader(); +import { PageHeaderSkeleton } from '../../REUSABLE_COMPONENTS/SkeletonVariants'; +import RCWrappedIcon from '../../REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIcon'; - return ( - - - - - - - - - - - - ); -}; const HeaderItem = ({ icon, label, value, delay }) => { return ( @@ -72,12 +47,13 @@ const CollectionPortfolioHeader = ({ onBack, collection, allCollections }) => { useEffect(() => { console.log('collection', collection); }, [collection]); + if ( !collection || - collection === DEFAULT_COLLECTION || - allCollections.length === 0 + collection === DEFAULT_COLLECTION + // allCollections.length === 0 ) { - return onBack(); + return ; } const headerItems = [ { @@ -126,30 +102,19 @@ const CollectionPortfolioHeader = ({ onBack, collection, allCollections }) => { width: '100%', }} > - {/* */} - + - {/* */} - {headerItems.map((item, index) => ( + {headerItems?.map((item, index) => ( { ))} {/* */} - {/* */} ); }; diff --git a/src/layout/collection/sub-components/ComplexStatisticsCard.jsx b/src/layout/collection/sub-components/ComplexStatisticsCard.jsx deleted file mode 100644 index b8062a3..0000000 --- a/src/layout/collection/sub-components/ComplexStatisticsCard.jsx +++ /dev/null @@ -1,174 +0,0 @@ -import PropTypes from 'prop-types'; -import Card from '@mui/material/Card'; -import Divider from '@mui/material/Divider'; -import Icon from '@mui/material/Icon'; -import MDBox from '../../REUSABLE_COMPONENTS/MDBOX/index'; -import MDTypography from '../../REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; -import { useMode } from '../../../context'; - -function ComplexStatisticsCard({ color, title, count, percentage, icon, sx }) { - const { theme } = useMode(); - const textColor = color === 'light' ? 'dark' : 'white'; - - const gradientColor = theme.palette[color]?.main || theme.palette.info.main; - - const cardStyle = { - display: 'flex', - flexDirection: 'column', - height: '100%', - borderRadius: theme.shape.borderRadius, - ...sx, - }; - - const iconBoxStyle = { - variant: 'gradient', - bgColor: color, - color: textColor, - coloredShadow: color, - borderRadius: 'xl', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - width: '4rem', - height: '4rem', - transform: 'translateY(-50%)', - background: `linear-gradient(145deg, ${gradientColor}, ${theme.palette[color]?.dark || theme.palette.info.dark})`, - }; - - const statisticsBoxStyle = { - textAlign: 'right', - lineHeight: 1.25, - mt: -3, - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'flex-end', - width: '100%', - height: '6rem', - }; - - const percentageStyle = { - component: 'p', - variant: 'button', - color: 'text', - display: 'flex', - justifyContent: 'space-between', - mt: 1, - }; - return ( - - - - - {icon} - - - - - {title} - - {count} - - - - - - - {percentage.amount} - - {percentage.label} - - - - ); -} - -// Setting default values for the props of ComplexStatisticsCard -ComplexStatisticsCard.defaultProps = { - color: 'info', - percentage: { - color: 'success', - text: '', - label: '', - }, -}; - -// Typechecking props for the ComplexStatisticsCard -ComplexStatisticsCard.propTypes = { - color: PropTypes.oneOf([ - 'primary', - 'secondary', - 'info', - 'success', - 'warning', - 'error', - 'light', - 'dark', - ]), - title: PropTypes.string.isRequired, - count: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - percentage: PropTypes.shape({ - color: PropTypes.oneOf([ - 'primary', - 'secondary', - 'info', - 'success', - 'warning', - 'error', - 'dark', - 'white', - ]), - amount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - label: PropTypes.string, - }), - icon: PropTypes.node.isRequired, -}; - -export default ComplexStatisticsCard; diff --git a/src/layout/collection/sub-components/Notifications.jsx b/src/layout/collection/sub-components/Notifications.jsx deleted file mode 100644 index b987287..0000000 --- a/src/layout/collection/sub-components/Notifications.jsx +++ /dev/null @@ -1,244 +0,0 @@ -/** -========================================================= -* Material Dashboard 2 React - v2.2.0 -========================================================= - -* Product Page: https://www.creative-tim.com/product/material-dashboard-react -* Copyright 2023 Creative Tim (https://www.creative-tim.com) - -Coded by www.creative-tim.com - - ========================================================= - -* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -*/ -import PriceChangeIcon from '@mui/icons-material/PriceChange'; - -import { useState } from 'react'; - -// @mui material components -import Grid from '@mui/material/Grid'; -import Card from '@mui/material/Card'; - -// Material Dashboard 2 React components -import MDBox from 'components/MDBox'; -import MDTypography from 'components/MDTypography'; -import MDAlert from 'components/MDAlert'; -import MDButton from 'components/MDButton'; -import MDSnackbar from 'components/MDSnackbar'; - -// Material Dashboard 2 React example components -import DashboardLayout from 'examples/LayoutContainers/DashboardLayout'; -import DashboardNavbar from 'examples/Navbars/DashboardNavbar'; -import Footer from 'examples/Footer'; - -function Notifications() { - const [successSB, setSuccessSB] = useState(false); - const [infoSB, setInfoSB] = useState(false); - const [warningSB, setWarningSB] = useState(false); - const [errorSB, setErrorSB] = useState(false); - - const openSuccessSB = () => setSuccessSB(true); - const closeSuccessSB = () => setSuccessSB(false); - const openInfoSB = () => setInfoSB(true); - const closeInfoSB = () => setInfoSB(false); - const openWarningSB = () => setWarningSB(true); - const closeWarningSB = () => setWarningSB(false); - const openErrorSB = () => setErrorSB(true); - const closeErrorSB = () => setErrorSB(false); - - const alertContent = (name) => ( - - A simple {name} alert with - - an example link - - . Give it a click if you like. - - ); - - const renderSuccessSB = ( - - ); - - const renderInfoSB = ( - - ); - - const renderWarningSB = ( - - ); - - const renderErrorSB = ( - - ); - - return ( - - - - - - - - Alerts - - - - {alertContent('primary')} - - - {alertContent('secondary')} - - - {alertContent('success')} - - - {alertContent('error')} - - - {alertContent('warning')} - - - {alertContent('info')} - - - {alertContent('light')} - - - {alertContent('dark')} - - - {alertContent('blueGreen')} - - - - - - - - - Notifications - - Notifications on this page use Toasts from Bootstrap. Read - more details here. - - - - - - - success notification - - {renderSuccessSB} - - - - info notification - - {renderInfoSB} - - - - warning notification - - {renderWarningSB} - - - - error notification - - {renderErrorSB} - - {/* CUSTOMIZED API NOTIFICATIONS */} - - - Price Change Notification - - {renderSuccessSB} - - - - - - - -