From 34f84da1891c8214deaa0fd2c6fd409232e3d3c7 Mon Sep 17 00:00:00 2001 From: Reed Vogt Date: Thu, 28 Mar 2024 19:23:27 -0700 Subject: [PATCH 1/5] various small style edits to reusable components library --- src/components/cards/CardToolTip.jsx | 48 ++++++++++++------- src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx | 7 ++- .../REUSABLE_COMPONENTS/unique/SimpleCard.jsx | 18 +++++++ .../collectionGrids/ChartGridLayout.jsx | 37 +++++++++++--- src/layout/deck/DeckListItem.jsx | 19 +++++++- src/layout/deck/index.jsx | 13 ++++- src/pages/pageStyles/StyledComponents.jsx | 6 ++- 7 files changed, 117 insertions(+), 31 deletions(-) diff --git a/src/components/cards/CardToolTip.jsx b/src/components/cards/CardToolTip.jsx index a9617a8..ae676fc 100644 --- a/src/components/cards/CardToolTip.jsx +++ b/src/components/cards/CardToolTip.jsx @@ -4,6 +4,7 @@ import { useMode } from '../../context'; import { Box, Tooltip, Typography, Zoom } from '@mui/material'; import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; import styled from 'styled-components'; +import rgba from '../../assets/themes/functions/rgba'; export const StyledToolTipBox = styled(Box)(({ theme }) => ({ width: 'auto', border: `1px solid ${theme.palette.divider}`, @@ -78,30 +79,45 @@ const createTooltip = (card, theme) => { boxShadow: theme.shadows[3], borderRadius: theme.shape.borderRadius, border: `1px solid ${theme.palette.divider}`, - // w: 'auto', - // h: '100%', - bgColor: theme.palette.backgroundC.lightest, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + padding: theme.spacing(2), + // alignItems: 'center', + // height: '100%', + // maxWidth: 220, + // // w: 'auto', + h: '100%', + background: rgba( + theme.palette.chartTheme.grey.lightest || 'white', + 0.85 + ), // p: theme.spacing(2), color: theme.palette.text.primary, alignContent: 'flex-start', '&::before': { content: '""', display: 'block', - paddingTop: '100%', - }, - '& > img': { - position: 'absolute', - top: 0, - left: 0, - w: '100%', - h: '100%', - objectFit: 'cover', + // paddingTop: '100%', }, + // '& > img': { + // position: 'absolute', + // top: 0, + // left: 0, + // // w: '100%', + // // h: '100%', + // objectFit: 'cover', + // }, }} > {Object.entries(cardAttributes).map(([key, value]) => ( - {formatKey(key)}: {value} + + + {formatKey(key)}:{' '} + + {value} + ))} @@ -117,9 +133,9 @@ const CardToolTip = ({ card }) => { title="Card" placement="right-end" > - - {createTooltip(card, theme)} - + {/* */} + {createTooltip(card, theme)} + {/* */} ); }; diff --git a/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx b/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx index ab1d9ac..deba981 100644 --- a/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx +++ b/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx @@ -19,7 +19,7 @@ const BoxHeader = ({ return ( @@ -29,12 +29,15 @@ const BoxHeader = ({ {title} {subtitle !== 'none' && ( - {subtitle} + + {subtitle} + )} diff --git a/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx b/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx index 95e8a0a..fa29005 100644 --- a/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx +++ b/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx @@ -37,6 +37,22 @@ const getFormHeaderStyle = (theme, isFormHeader) => ({ width: '80%', }); +const getSelectorStyles = (themeSettings, isSelectorRow) => ({ + background: isSelectorRow ? themeSettings.palette.dark.state : undefined, + // color: isSelectorRow ? themeSettings.colorPrimary : undefined, + // maxWidth: 'lg', + padding: themeSettings.spacing(3), // 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 + mb: themeSettings.spacing(3), + mt: themeSettings.spacing(5), + pt: themeSettings.spacing(5), + mx: 'auto', + px: 'auto', + justifyContent: 'center', + // width: '80%', +}); + const getSearchFormHeaderStyle = (theme, isSearchFormHeader) => ({ background: isSearchFormHeader ? theme.colorCardBackground : undefined, color: isSearchFormHeader ? theme.colorPrimary : undefined, @@ -119,6 +135,7 @@ const SimpleCard = ({ hasTitle, isPrimary, isAccent, + isSelectorRow, isTableOrChart, noBottomMargin, children, @@ -143,6 +160,7 @@ const SimpleCard = ({ borderRadius: theme.borderRadius, background: theme.colorCardBackground, color: theme.colorText, + ...(isSelectorRow && getSelectorStyles(themeSettings, true)), ...(isHeroDisplay && getHeroDisplayStyles(theme, true)), ...(isSearchFormHeader && getSearchFormHeaderStyle(theme, true)), ...(isFormHeader && getFormHeaderStyle(theme, true)), diff --git a/src/layout/collection/collectionGrids/ChartGridLayout.jsx b/src/layout/collection/collectionGrids/ChartGridLayout.jsx index daf69d3..867007b 100644 --- a/src/layout/collection/collectionGrids/ChartGridLayout.jsx +++ b/src/layout/collection/collectionGrids/ChartGridLayout.jsx @@ -44,6 +44,8 @@ const renderCardContainer = (content) => { const ChartGridLayout = ({ selectedCards, removeCard }) => { const { theme } = useMode(); + const colors = theme.palette.chartTheme; + const greenAccent = colors.greenAccent.light; const isXs = useMediaQuery(theme.breakpoints.down('sm')); const { selectedCollection, showCollections, markers } = useSelectedCollection(); @@ -92,9 +94,17 @@ const ChartGridLayout = ({ selectedCards, removeCard }) => { title="Collection Card Chart" subtitle="List of all cards in the collection" icon={ - - show_chart - + + + show_chart + + } sideText="+4%" /> @@ -123,9 +133,11 @@ const ChartGridLayout = ({ selectedCards, removeCard }) => { @@ -155,14 +167,25 @@ const ChartGridLayout = ({ selectedCards, removeCard }) => { hasTitle={false} data="" isTableOrChart={true} + sx={{ + alignItems: 'flex-start', + }} > - list - + + + list + + } // sideText="Updated recently" sideText={`Last Updated: ${formattedTime}`} diff --git a/src/layout/deck/DeckListItem.jsx b/src/layout/deck/DeckListItem.jsx index b75ff75..d353479 100644 --- a/src/layout/deck/DeckListItem.jsx +++ b/src/layout/deck/DeckListItem.jsx @@ -18,6 +18,7 @@ import GenericCard from '../../components/cards/GenericCard'; import DeckForm from '../../components/forms/DeckForm'; import DeckOfCardsIcon from '../REUSABLE_COMPONENTS/icons/DeckOfCardsIcon'; import MoneyIcon from '../REUSABLE_COMPONENTS/icons/MoneyIcon'; +import rgba from '../../assets/themes/functions/rgba'; const AnimatedInfoItem = ({ label, value, theme, delay }) => { const [checked, setChecked] = useState(false); @@ -152,11 +153,25 @@ const DeckListItem = ({ {/* Adjust the spacing as needed */} - + {cards && cards.length > 0 && cards.map((card) => ( - + {/* Adjust breakpoints as needed for responsive design */} diff --git a/src/layout/deck/index.jsx b/src/layout/deck/index.jsx index 08369b8..551d799 100644 --- a/src/layout/deck/index.jsx +++ b/src/layout/deck/index.jsx @@ -10,6 +10,8 @@ import useDialogState from '../../context/hooks/useDialogState'; import useSelectedDeck from '../../context/MAIN_CONTEXT/DeckContext/useSelectedDeck'; import DeckListItem from './DeckListItem'; import DashboardBox from '../REUSABLE_COMPONENTS/DashboardBox'; +import SimpleCard from '../REUSABLE_COMPONENTS/unique/SimpleCard'; +import uniqueTheme from '../REUSABLE_COMPONENTS/unique/uniqueTheme'; const DeckBuilder = () => { const { theme } = useMode(); @@ -61,7 +63,14 @@ const DeckBuilder = () => { - + + \{/* */} {allDecks?.map((deck, index) => ( { /> ))} - + diff --git a/src/pages/pageStyles/StyledComponents.jsx b/src/pages/pageStyles/StyledComponents.jsx index 8e4d5cc..8a69a63 100644 --- a/src/pages/pageStyles/StyledComponents.jsx +++ b/src/pages/pageStyles/StyledComponents.jsx @@ -832,14 +832,16 @@ export const PortfolioTablePriceBox = styled(Box)(({ theme }) => ({ })); // ! CHART AND DATATABLES export const StyledChartBox = styled(Box)(({ theme }) => ({ - padding: '15px', + padding: '10px', + mx: 'auto', border: '2px solid #444', borderRadius: '8px', + borderColor: theme.palette.chartTheme.greenAccent.light, backgroundColor: '#222', color: '#fff', boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)', fontFamily: '"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', - maxWidth: '400px', + // maxWidth: '400px', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', From b310eb8530ede9d1104504237774323cde0782a4 Mon Sep 17 00:00:00 2001 From: Reed Vogt Date: Sun, 31 Mar 2024 13:02:16 -0700 Subject: [PATCH 2/5] Massive form logic changes --- src/App.js | 72 +-- src/Main.jsx | 121 ++-- src/assets/currentlyUnused/CardChart.jsx | 6 +- .../ChartContext/ChartContext.jsx | 40 ++ .../currentlyUnused}/ChartContext/helpers.jsx | 10 +- src/assets/currentlyUnused/FormTextField.jsx | 26 + .../currentlyUnused}/GenerateNivoTestData.jsx | 0 src/assets/currentlyUnused/RCForm.jsx | 19 + src/assets/currentlyUnused/RCFormInput.jsx | 23 + src/assets/currentlyUnused/RCFormInputs.jsx | 29 + .../StatisticsContext/StatisticsContext.jsx | 0 .../currentlyUnused/TopCardsDisplay.jsx | 6 +- src/assets/currentlyUnused/cardStyles.jsx | 10 +- src/assets/currentlyUnused/formFieldData.jsx | 263 +++++++++ src/assets/currentlyUnused/helpers2.jsx | 120 ++++ src/assets/currentlyUnused/menuItemsData.jsx | 2 +- src/assets/currentlyUnused/schemas.jsx | 71 +++ .../currentlyUnused}/statList.jsx | 2 +- src/assets/currentlyUnused/useAuthDialog.jsx | 26 + .../useSelectCollectionListStyles.jsx | 16 +- src/assets/themes/base/colors.jsx | 129 +---- .../themes/base/customColorPalettes.jsx | 378 ++++++++----- src/assets/themes/base/typography.jsx | 2 +- .../themes/components/buttons/contained.jsx | 26 +- src/assets/themes/components/buttons/holo.jsx | 82 +++ .../themes/components/buttons/index.jsx | 7 + .../themes/components/buttons/outlined.jsx | 6 +- src/assets/themes/components/card/index.jsx | 4 +- src/assets/useSnackbarManager.jsx | 59 ++ .../actionButtons/GenericActionButtons.jsx | 14 +- .../buttons/other/ReusableLoadingButton.jsx | 8 +- src/components/cards/CardToolTip.jsx | 85 +-- src/components/cards/GenericCard.jsx | 63 ++- src/components/componentHelpers.jsx | 30 - src/components/dialogs/CollectionDialog.jsx | 23 +- src/components/dialogs/DeckDialog.jsx | 2 +- src/components/dialogs/GenericCardDialog.jsx | 4 +- .../dialogs/SelectionErrorDialog.jsx | 22 +- src/components/forms/AuthForm.jsx | 1 - src/components/forms/CollectionForm.jsx | 20 +- src/components/forms/DeckForm.jsx | 32 +- .../forms/Factory/RCDynamicForm.jsx | 106 ++++ src/components/forms/Factory/RCFieldError.jsx | 16 + src/components/forms/Factory/RCInput.jsx | 277 ++++++++++ .../forms/Factory/useRCFormHook.jsx | 13 + src/components/forms/OptionsComponent.jsx | 46 +- src/components/forms/SearchForm.jsx | 9 +- .../customerCheckoutForm/CustomerForm.js | 13 +- src/components/forms/formsConfig.jsx | 515 ++++++++++++++++-- .../forms/hooks/useFormManagement.jsx | 20 + .../forms/hooks/useFormSubmission.jsx | 112 ++++ .../forms/hooks/useSubmitHandler.jsx | 35 +- src/components/forms/reusable/FormField.jsx | 4 +- .../forms/reusable/FormTextField.jsx | 26 - src/components/forms/reusable/RCSwitch.jsx | 3 - src/components/forms/reusable/RCZodForm.jsx | 18 +- .../forms/search/SearchComponent.jsx | 19 +- src/components/forms/search/SearchResults.jsx | 15 +- .../forms/search/SearchSettings.jsx | 2 +- .../CollectionStatisticsSelector.jsx | 3 +- .../forms/selectors/useTimeRange.jsx | 4 + src/config.json | 1 + src/context/Helpers.jsx | 152 ++---- .../MAIN_CONTEXT/AuthContext/authContext.js | 167 +----- .../MAIN_CONTEXT/AuthContext/helpers.jsx | 47 -- .../AuthContext/useAuthManager.jsx | 105 ++++ .../MAIN_CONTEXT/CardContext/CardContext.jsx | 2 +- .../CardContext}/useCardStore.jsx | 63 +-- .../MAIN_CONTEXT/CartContext/CartContext.js | 422 ++------------ .../MAIN_CONTEXT/CartContext/helpers.jsx | 11 - .../CartContext/useCartManager.jsx | 202 +++++++ .../ChartContext/ChartContext.jsx | 39 -- .../CollectionContext/CollectionContext.jsx | 80 +-- .../CollectionContext/helpers.jsx | 123 ----- .../useCollectionManager.jsx | 196 ++++--- .../useSelectedCollection.jsx | 230 +++++--- .../MAIN_CONTEXT/DeckContext/DeckContext.js | 398 +------------- .../DeckContext/useDeckManager.jsx | 33 +- .../DeckContext/useSelectedDeck.jsx | 403 +++----------- .../MAIN_CONTEXT/UserContext/UserContext.js | 96 +--- .../MAIN_CONTEXT/UserContext/useUserData.jsx | 137 +++++ .../AppContext/AppContextProvider.jsx | 182 +------ .../AppContext/useCompileCardData.jsx | 124 +++++ .../ColorModeContext/ColorModeProvider.jsx | 17 +- .../FormContext/FormContext.jsx | 244 +++------ .../UTILITIES_CONTEXT/FormContext/helpers.jsx | 227 -------- .../SideBarContext/SideBarProvider.jsx | 11 +- src/context/constants.jsx | 6 +- .../hooks/{ => generic}/useCounter.jsx | 2 +- .../hooks/{ => generic}/useDebounce.jsx | 0 .../hooks/{ => generic}/useIsFirstRender.jsx | 0 src/context/hooks/index.jsx | 193 ------- src/context/hooks/oldhooks/index.jsx | 193 +++++++ .../hooks/oldhooks/useApiResponseHandler.jsx | 22 + src/context/hooks/oldhooks/useCardActions.jsx | 113 ++++ .../hooks/oldhooks}/useCardCronJob.jsx | 0 .../hooks/{ => oldhooks}/useDialog.jsx | 0 src/context/hooks/useApiResponseHandler.jsx | 22 - src/context/hooks/useAuthDialog.jsx | 22 - src/context/hooks/useCardActions.jsx | 113 ---- src/context/hooks/useDialogState.jsx | 2 + src/context/hooks/useEventHandlers.jsx | 23 + src/context/hooks/useFetchWrapper.jsx | 48 +- src/context/hooks/useFormData.jsx | 18 + src/context/hooks/useGridItems.jsx | 6 +- src/context/hooks/useLoading.jsx | 36 +- src/context/hooks/useLocalStorage.jsx | 24 +- src/context/hooks/useManageCookies.jsx | 63 +++ src/context/hooks/useSelectedContext.jsx | 2 - src/context/hooks/useSnackbarManager.jsx | 59 -- .../{ => ygo-specific}/useGetRandomCard.jsx | 0 .../hooks/{ => ygo-specific}/useOverlay.jsx | 0 src/context/index.js | 16 +- src/context/simplified_constants.jsx | 2 + src/data/collectionPortfolioData.jsx | 5 +- src/data/collectionPortfolioHeaderItems.jsx | 34 ++ src/data/iconData.jsx | 8 +- src/data/route-config.jsx | 16 + src/data/searchData.jsx | 5 +- src/{context => data}/user.jsx | 0 src/index.js | 28 +- src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx | 8 +- .../Configurator/ConfiguratorRoot.jsx | 2 +- .../REUSABLE_COMPONENTS}/ErrorBoundary.jsx | 0 .../ErrorIndicator.js | 2 +- .../LoadingIndicator.js | 0 .../LoadingOverlay.jsx | 2 +- .../REUSABLE_COMPONENTS/MDBOX/MdBoxRoot.jsx | 9 - .../MDBUTTON/MDButtonRoot.jsx | 14 +- .../REUSABLE_COMPONENTS/MDBUTTON/index.jsx | 9 +- .../RCBADGE/RCBadgeRoot.jsx | 135 +++++ .../RCBADGE/examples/BadgesGradient.jsx | 28 + .../RCBADGE/examples/BadgesSimple.jsx | 68 +++ .../RCBADGE/examples/BadgesSimpleRounded.jsx | 76 +++ .../REUSABLE_COMPONENTS/RCBADGE/index.jsx | 72 +++ .../RCBUTTON/RCButtonRoot.jsx | 458 ++++++++++++++++ .../RCBUTTON/examples/ButtonGradient.jsx | 47 ++ .../RCBUTTON/examples/ButtonIconLeft.jsx | 33 ++ .../RCBUTTON/examples/ButtonIconRight.jsx | 32 ++ .../REUSABLE_COMPONENTS/RCBUTTON/index.jsx | 105 ++++ .../RCInput/RCInputRoot.jsx | 2 +- src/layout/REUSABLE_COMPONENTS/StatBox.jsx | 22 +- .../icons/DeckBuilderIcon.jsx | 9 +- .../unique/SimpleButton.jsx | 12 +- .../unique/SimpleSectionHeader.jsx | 2 +- .../ReusableStyledComponents.jsx | 73 +-- src/layout/cart/CartContent.js | 10 +- .../cart/cartPageContainers/Checkout.jsx | 4 +- src/layout/cart/cartPageContainers/Review.jsx | 31 +- .../collectionGrids/ChartGridLayout.jsx | 13 +- .../cards-chart/ChartConfigs.jsx | 7 +- .../cards-datatable/PricedDataTable.jsx | 3 - .../collections-list/CollectionListItem.jsx | 307 ++++++----- .../SelectCollectionHeader.jsx | 41 +- .../collections-list/SelectCollectionList.jsx | 61 ++- .../collections-list/StatBoard.jsx | 44 +- .../statItems/PricedCardList.jsx | 48 +- .../statItems/TotalCardsCollectedStatBox.jsx | 2 +- .../statItems/TotalPriceStatBox.jsx | 9 +- .../statItems/ValuDistributionCircle.jsx | 147 +++-- src/layout/collection/data/portfolioData.jsx | 51 +- .../CollectionPortfolioHeader.jsx | 40 +- .../sub-components/TopCardsDisplayRow.jsx | 29 +- src/layout/deck/DeckListItem.jsx | 20 +- src/layout/deck/DeckPageHeader.jsx | 50 +- src/layout/deck/index.jsx | 11 +- src/layout/navigation/Navigation.jsx | 35 +- src/layout/{ => navigation}/PrivateRoute.jsx | 4 +- .../NotificationList.jsx | 40 +- .../profile/NotificationSection.js/index.js | 8 +- src/layout/profile/ProfileSection.js | 25 +- src/layout/profile/UserStats.jsx | 14 +- src/layout/sections/AnimatedFeatureCard.jsx | 20 +- src/layout/sections/HeroSection.jsx | 23 +- src/layout/sections/HeroTextSection.jsx | 2 +- src/layout/sections/MainContentSection.jsx | 68 +-- src/pages/CartPage.js | 162 +++--- src/pages/CollectionPage.js | 4 - .../dialogs => pages}/LoginDialog.jsx | 101 ++-- src/pages/LoginPage.jsx | 36 +- src/pages/pageStyles/CarouselImage.jsx | 4 +- src/pages/pageStyles/HeroCenter.jsx | 4 +- src/pages/pageStyles/StyledComponents.jsx | 148 ++--- src/utils/FormFactory.jsx | 25 + 184 files changed, 6032 insertions(+), 4747 deletions(-) create mode 100644 src/assets/currentlyUnused/ChartContext/ChartContext.jsx rename src/{context/MAIN_CONTEXT => assets/currentlyUnused}/ChartContext/helpers.jsx (93%) create mode 100644 src/assets/currentlyUnused/FormTextField.jsx rename src/{layout/collection/data => assets/currentlyUnused}/GenerateNivoTestData.jsx (100%) create mode 100644 src/assets/currentlyUnused/RCForm.jsx create mode 100644 src/assets/currentlyUnused/RCFormInput.jsx create mode 100644 src/assets/currentlyUnused/RCFormInputs.jsx rename src/{context => assets/currentlyUnused}/SECONDARY_CONTEXT/StatisticsContext/StatisticsContext.jsx (100%) create mode 100644 src/assets/currentlyUnused/formFieldData.jsx create mode 100644 src/assets/currentlyUnused/helpers2.jsx create mode 100644 src/assets/currentlyUnused/schemas.jsx rename src/{layout/collection/data => assets/currentlyUnused}/statList.jsx (86%) create mode 100644 src/assets/currentlyUnused/useAuthDialog.jsx create mode 100644 src/assets/themes/components/buttons/holo.jsx create mode 100644 src/assets/useSnackbarManager.jsx delete mode 100644 src/components/componentHelpers.jsx create mode 100644 src/components/forms/Factory/RCDynamicForm.jsx create mode 100644 src/components/forms/Factory/RCFieldError.jsx create mode 100644 src/components/forms/Factory/RCInput.jsx create mode 100644 src/components/forms/Factory/useRCFormHook.jsx create mode 100644 src/components/forms/hooks/useFormManagement.jsx create mode 100644 src/components/forms/hooks/useFormSubmission.jsx delete mode 100644 src/components/forms/reusable/FormTextField.jsx delete mode 100644 src/context/MAIN_CONTEXT/AuthContext/helpers.jsx create mode 100644 src/context/MAIN_CONTEXT/AuthContext/useAuthManager.jsx rename src/context/{hooks => MAIN_CONTEXT/CardContext}/useCardStore.jsx (65%) delete mode 100644 src/context/MAIN_CONTEXT/CartContext/helpers.jsx create mode 100644 src/context/MAIN_CONTEXT/CartContext/useCartManager.jsx delete mode 100644 src/context/MAIN_CONTEXT/ChartContext/ChartContext.jsx delete mode 100644 src/context/MAIN_CONTEXT/CollectionContext/helpers.jsx create mode 100644 src/context/MAIN_CONTEXT/UserContext/useUserData.jsx create mode 100644 src/context/MISC_CONTEXT/AppContext/useCompileCardData.jsx delete mode 100644 src/context/UTILITIES_CONTEXT/FormContext/helpers.jsx rename src/context/hooks/{ => generic}/useCounter.jsx (97%) rename src/context/hooks/{ => generic}/useDebounce.jsx (100%) rename src/context/hooks/{ => generic}/useIsFirstRender.jsx (100%) delete mode 100644 src/context/hooks/index.jsx create mode 100644 src/context/hooks/oldhooks/index.jsx create mode 100644 src/context/hooks/oldhooks/useApiResponseHandler.jsx create mode 100644 src/context/hooks/oldhooks/useCardActions.jsx rename src/{layout => context/hooks/oldhooks}/useCardCronJob.jsx (100%) rename src/context/hooks/{ => oldhooks}/useDialog.jsx (100%) delete mode 100644 src/context/hooks/useApiResponseHandler.jsx delete mode 100644 src/context/hooks/useAuthDialog.jsx delete mode 100644 src/context/hooks/useCardActions.jsx create mode 100644 src/context/hooks/useEventHandlers.jsx create mode 100644 src/context/hooks/useFormData.jsx create mode 100644 src/context/hooks/useManageCookies.jsx delete mode 100644 src/context/hooks/useSnackbarManager.jsx rename src/context/hooks/{ => ygo-specific}/useGetRandomCard.jsx (100%) rename src/context/hooks/{ => ygo-specific}/useOverlay.jsx (100%) create mode 100644 src/data/collectionPortfolioHeaderItems.jsx create mode 100644 src/data/route-config.jsx rename src/{context => data}/user.jsx (100%) rename src/{context => layout/REUSABLE_COMPONENTS}/ErrorBoundary.jsx (100%) rename src/layout/{ => REUSABLE_COMPONENTS}/ErrorIndicator.js (97%) rename src/layout/{ => REUSABLE_COMPONENTS}/LoadingIndicator.js (100%) rename src/layout/{ => REUSABLE_COMPONENTS}/LoadingOverlay.jsx (96%) create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBADGE/RCBadgeRoot.jsx create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesGradient.jsx create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesSimple.jsx create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesSimpleRounded.jsx create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBADGE/index.jsx create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBUTTON/RCButtonRoot.jsx create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonGradient.jsx create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonIconLeft.jsx create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonIconRight.jsx create mode 100644 src/layout/REUSABLE_COMPONENTS/RCBUTTON/index.jsx rename src/layout/{ => navigation}/PrivateRoute.jsx (76%) rename src/{components/dialogs => pages}/LoginDialog.jsx (56%) create mode 100644 src/utils/FormFactory.jsx diff --git a/src/App.js b/src/App.js index fc4169f..53a2902 100644 --- a/src/App.js +++ b/src/App.js @@ -11,64 +11,46 @@ import { CardProvider, DeckProvider, CartProvider, - ChartProvider, SidebarProvider, AppContextProvider, - useAuthContext, - ErrorBoundary, ConfiguratorProvider, } from './context'; import { ThemeProvider } from 'styled-components'; -import { SnackbarProvider, useSnackbar } from 'notistack'; -import { useNavigate } from 'react-router-dom'; import { CssBaseline, GlobalStyles } from '@mui/material'; import { ParallaxProvider } from 'react-scroll-parallax'; -import { useLoading } from './context/hooks/useLoading'; // ==============================|| APP ||============================== // const App = () => { const { theme } = useMode(); - const navigate = useNavigate(); - const { resetLogoutTimer, logout, authUser, userId, isLoggedIn } = - useAuthContext(); - const { isLoading, isPageLoading, error } = useLoading(); - - if (isPageLoading || error) { - return
Loading...
; - } return ( - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - + + + + + + + + + + + + + + +
+ + + + + + + + + + + + ); }; diff --git a/src/Main.jsx b/src/Main.jsx index b4768d0..2a92a27 100644 --- a/src/Main.jsx +++ b/src/Main.jsx @@ -1,94 +1,65 @@ -import React, { Suspense, lazy } from 'react'; +import React, { Suspense, lazy, useEffect } from 'react'; import { Route, Routes } from 'react-router-dom'; -import { useMediaQuery } from '@mui/material'; -import PrivateRoute from './layout/PrivateRoute.jsx'; -import LoginDialog from './components/dialogs/LoginDialog'; -import { useAuthContext, useConfiguratorContext, useMode } from './context'; +import PrivateRoute from './layout/navigation/PrivateRoute.jsx'; +import LoginDialog from './pages/LoginDialog.jsx'; +import { useConfiguratorContext, useMode } from './context'; import PageLayout from './layout/REUSABLE_COMPONENTS/PageLayout.jsx'; import Navigation from './layout/navigation/Navigation.jsx'; -import LoadingIndicator from './layout/LoadingIndicator.js'; import Configurator from './layout/REUSABLE_COMPONENTS/Configurator/index.jsx'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; -import LoadingOverlay from './layout/LoadingOverlay.jsx'; +import LoadingOverlay from './layout/REUSABLE_COMPONENTS/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'; - -const ROUTE_CONFIG = [ - { path: '/', componentName: 'HomePage', isPrivate: false }, - { path: '/home', componentName: 'HomePage', isPrivate: false }, - { path: '/deckbuilder', componentName: 'DeckBuilderPage', isPrivate: false }, - { path: '/store', componentName: 'StorePage', isPrivate: false }, - { path: '/cart', componentName: 'CartPage', isPrivate: true }, - { path: '/collection', componentName: 'CollectionPage', isPrivate: true }, - { path: '/profile', componentName: 'ProfilePage', isPrivate: true }, - { path: '/login', componentName: 'LoginPage', isPrivate: false }, - { path: '/signup', componentName: 'SignupPage', isPrivate: false }, - { path: '/about', componentName: 'AboutPage', isPrivate: false }, - { path: '/contact', componentName: 'ContactPage', isPrivate: false }, - { path: '/terms', componentName: 'TermsPage', isPrivate: false }, - { path: '/privacy', componentName: 'PrivacyPage', isPrivate: false }, - { path: '*', componentName: 'NotFoundPage', isPrivate: false }, -]; +import useManageCookies from './context/hooks/useManageCookies.jsx'; +import { ROUTE_CONFIG } from './data/route-config.jsx'; const LazyRoute = ({ componentName, ...rest }) => { const Component = lazy(() => import(`./pages/${componentName}`)); return ; }; const Main = () => { - const { theme } = useMode(); - const isMobileView = useMediaQuery(theme.breakpoints.down('sm')); - const { isLoggedIn } = useAuthContext(); + const { addCookie, getCookie, deleteCookie } = useManageCookies(); + const { isLoggedIn, userId } = getCookie(['isLoggedIn', 'userId']); const { isConfiguratorOpen, toggleConfigurator } = useConfiguratorContext(); - const { dialogState, openDialog, closeDialog } = useDialogState({}); - const { selectedCollection } = useSelectedCollection(); + const { dialogState, openDialog, closeDialog } = useDialogState(); return ( - <> - {!isLoggedIn ? ( - - ) : ( - - - {isConfiguratorOpen && } - - - }> - - {ROUTE_CONFIG.map( - ({ path, componentName, isPrivate }, index) => ( - - - - ) : ( - - ) - } - /> + + + {isConfiguratorOpen && } + + + }> + + {ROUTE_CONFIG.map(({ path, componentName, isPrivate }, index) => ( + + + + ) : ( + ) - )} - - - - - {dialogState.isSelectionErrorDialogOpen && ( - closeDialog('isSelectionErrorDialogOpen')} - selectedValue={selectedCollection?.name} - /> - )} - - )} - + } + /> + ))} + + + + + + closeDialog('isSelectionErrorDialogOpen')} + // selectedValue={selectedCollection?.name} + /> + ); }; diff --git a/src/assets/currentlyUnused/CardChart.jsx b/src/assets/currentlyUnused/CardChart.jsx index 631be3c..b169920 100644 --- a/src/assets/currentlyUnused/CardChart.jsx +++ b/src/assets/currentlyUnused/CardChart.jsx @@ -133,7 +133,7 @@ // // { +// const { selectedCollection } = useSelectedCollection(); + +// const contextValue = useMemo( +// () => ({ +// nivoChartData: selectedCollection?.nivoChartData, +// newNivoChartData: selectedCollection?.newNivoChartData, +// muiChartData: selectedCollection?.muiChartData, +// nivoTestData: selectedCollection?.nivoTestData, +// }), +// [ +// selectedCollection?.nivoChartData, +// selectedCollection?.newNivoChartData, +// selectedCollection?.muiChartData, +// selectedCollection?.nivoTestData, +// ] +// ); +// return ( +// +// {children} +// +// ); +// }; + +// export const useChartContext = () => { +// const context = useContext(ChartContext); +// if (!context) { +// throw new Error('useChartContext must be used within a ChartProvider'); +// } +// return context; +// }; diff --git a/src/context/MAIN_CONTEXT/ChartContext/helpers.jsx b/src/assets/currentlyUnused/ChartContext/helpers.jsx similarity index 93% rename from src/context/MAIN_CONTEXT/ChartContext/helpers.jsx rename to src/assets/currentlyUnused/ChartContext/helpers.jsx index ccfd38e..64f629c 100644 --- a/src/context/MAIN_CONTEXT/ChartContext/helpers.jsx +++ b/src/assets/currentlyUnused/ChartContext/helpers.jsx @@ -1,16 +1,10 @@ -import { roundToNearestTenth } from '../../Helpers'; +import { roundToNearestTenth } from '../../../context/Helpers'; import { Tooltip, Typography } from '@mui/material'; import { useState, useCallback, useMemo, useEffect } from 'react'; import { debounce } from 'lodash'; import styled from 'styled-components'; import { Box } from '@mui/system'; -import useMode from '../../UTILITIES_CONTEXT/ColorModeContext/useMode'; -import { useChartContext } from './ChartContext'; -import { useAuthContext } from '../AuthContext/authContext'; -import useCollectionManager from '../CollectionContext/useCollectionManager'; -import { ResponsiveLine } from '@nivo/line'; -import { useLoading } from '../../hooks/useLoading'; -import useSelectedCollection from '../CollectionContext/useSelectedCollection'; +import useMode from '../../../context/UTILITIES_CONTEXT/ColorModeContext/useMode'; export const groupAndAverageData = (data, threshold = 600000, timeRange) => { if (!data || data.length === 0) return []; diff --git a/src/assets/currentlyUnused/FormTextField.jsx b/src/assets/currentlyUnused/FormTextField.jsx new file mode 100644 index 0000000..71f3cd2 --- /dev/null +++ b/src/assets/currentlyUnused/FormTextField.jsx @@ -0,0 +1,26 @@ +// import { TextField } from '@mui/material'; +// import React from 'react'; +// import { Controller, useFormContext } from 'react-hook-form'; + +// // Removed the TypeScript-specific type definitions and kept the structure of the component +// function FormTextField({ control: providedControl, name, rules, ...rest }) { +// const { control } = useFormContext(); +// return ( +// ( +// +// )} +// /> +// ); +// } + +// export default FormTextField; diff --git a/src/layout/collection/data/GenerateNivoTestData.jsx b/src/assets/currentlyUnused/GenerateNivoTestData.jsx similarity index 100% rename from src/layout/collection/data/GenerateNivoTestData.jsx rename to src/assets/currentlyUnused/GenerateNivoTestData.jsx diff --git a/src/assets/currentlyUnused/RCForm.jsx b/src/assets/currentlyUnused/RCForm.jsx new file mode 100644 index 0000000..75693c5 --- /dev/null +++ b/src/assets/currentlyUnused/RCForm.jsx @@ -0,0 +1,19 @@ +// import { FormProvider } from 'react-hook-form'; +// import Container from '@mui/material/Container'; + +// export const RCForm = ({ form, onSubmit, children, ...props }) => { +// return ( +// +// +//
+//
+// {children} +//
+//
+//
+//
+// ); +// }; diff --git a/src/assets/currentlyUnused/RCFormInput.jsx b/src/assets/currentlyUnused/RCFormInput.jsx new file mode 100644 index 0000000..d390cfc --- /dev/null +++ b/src/assets/currentlyUnused/RCFormInput.jsx @@ -0,0 +1,23 @@ +// import { forwardRef } from 'react'; +// import TextField from '@mui/material/TextField'; +// import { RCFieldError } from './RCFieldError'; + +// export const RCFormInput = forwardRef(function Input( +// { label, type = 'text', name, ...props }, +// ref +// ) { +// return ( +//
+// : null} +// /> +//
+// ); +// }); diff --git a/src/assets/currentlyUnused/RCFormInputs.jsx b/src/assets/currentlyUnused/RCFormInputs.jsx new file mode 100644 index 0000000..fec7b3b --- /dev/null +++ b/src/assets/currentlyUnused/RCFormInputs.jsx @@ -0,0 +1,29 @@ +// import React from 'react'; +// import { Controller } from 'react-hook-form'; +// import { TextField } from '@mui/material'; + +// const RCFormInputs = Object.keys(dynamicForm).map((key) => { +// const { rules, defaultValue, label } = dynamicForm[key]; + +// return ( +//
+// ( +// +// )} +// /> +//
+// ); +// }); diff --git a/src/context/SECONDARY_CONTEXT/StatisticsContext/StatisticsContext.jsx b/src/assets/currentlyUnused/SECONDARY_CONTEXT/StatisticsContext/StatisticsContext.jsx similarity index 100% rename from src/context/SECONDARY_CONTEXT/StatisticsContext/StatisticsContext.jsx rename to src/assets/currentlyUnused/SECONDARY_CONTEXT/StatisticsContext/StatisticsContext.jsx diff --git a/src/assets/currentlyUnused/TopCardsDisplay.jsx b/src/assets/currentlyUnused/TopCardsDisplay.jsx index 4fea8c6..07d511b 100644 --- a/src/assets/currentlyUnused/TopCardsDisplay.jsx +++ b/src/assets/currentlyUnused/TopCardsDisplay.jsx @@ -22,7 +22,7 @@ // background: theme.palette.backgroundB.dark, // border: `1px solid ${theme.palette.backgroundB.lighter}`, // borderRadius: theme.shape.borderRadiusLarge, -// color: theme.palette.backgroundA.light, +// color: theme.palette.greenAccent.lightest, // overflow: 'hidden', // padding: theme.spacing(1), // height: '100%', @@ -145,7 +145,7 @@ // onClick={handleNext} // disabled={activeStep === maxSteps - 1} // sx={{ -// color: theme.palette.backgroundA.light, +// color: theme.palette.greenAccent.lightest, // }} // > // {theme.direction === 'rtl' ? ( @@ -162,7 +162,7 @@ // onClick={handleBack} // disabled={activeStep === 0} // sx={{ -// color: theme.palette.backgroundA.light, +// color: theme.palette.greenAccent.lightest, // '&.Mui-disabled': { // // background: theme.palette.background.disabled, // color: theme.palette.background.disabled, diff --git a/src/assets/currentlyUnused/cardStyles.jsx b/src/assets/currentlyUnused/cardStyles.jsx index e080a8b..14cdb56 100644 --- a/src/assets/currentlyUnused/cardStyles.jsx +++ b/src/assets/currentlyUnused/cardStyles.jsx @@ -38,7 +38,7 @@ // // border: `1px solid ${theme.palette.divider}`, // // borderRadius: theme.shape.borderRadius, // // padding: theme.spacing(2), -// // backgroundColor: theme.palette.backgroundA.lightest, +// // backgroundColor: theme.palette.greenAccent.contrastText, // // color: theme.palette.text.primary, // // boxShadow: theme.shadows[3], // // alignContent: 'flex-start', @@ -65,7 +65,7 @@ // // // border: `1px solid ${theme.palette.divider}`, // // // borderRadius: theme.shape.borderRadius, // // // padding: theme.spacing(2), -// // // backgroundColor: theme.palette.backgroundA.lightest, +// // // backgroundColor: theme.palette.greenAccent.contrastText, // // // color: theme.palette.text.primary, // // // boxShadow: theme.shadows[3], // // // alignContent: 'flex-start', @@ -107,7 +107,7 @@ // padding: theme.spacing(2), // borderRadius: theme.shape.borderRadius, // boxShadow: `0 4px 8px 0 ${theme.palette.shadow}`, -// backgroundColor: theme.palette.backgroundA.lightest, +// backgroundColor: theme.palette.greenAccent.contrastText, // display: 'flex', // flexDirection: 'column', // gap: theme.spacing(1), @@ -196,7 +196,7 @@ // // border: `1px solid ${theme.palette.divider}`, // Add border // // borderRadius: theme.shape.borderRadius, // Use theme border radius // // padding: theme.spacing(2), // Add some padding -// // backgroundColor: theme.palette.backgroundA.lightest, // Use theme background +// // backgroundColor: theme.palette.greenAccent.contrastText, // Use theme background // // color: theme.palette.text.primary, // Use theme text color // // boxShadow: theme.shadows[3], // Use theme shadow // // alignContent: 'flex-start', @@ -263,7 +263,7 @@ // // padding: theme.spacing(2), // // borderRadius: theme.shape.borderRadius, // // boxShadow: `0 4px 8px 0 ${theme2.palette.shadow}`, -// // backgroundColor: theme.palette.backgroundA.lightest, +// // backgroundColor: theme.palette.greenAccent.contrastText, // // display: 'flex', // // flexDirection: 'column', // // gap: theme.spacing(1), diff --git a/src/assets/currentlyUnused/formFieldData.jsx b/src/assets/currentlyUnused/formFieldData.jsx new file mode 100644 index 0000000..a4f17be --- /dev/null +++ b/src/assets/currentlyUnused/formFieldData.jsx @@ -0,0 +1,263 @@ +// 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 loginFields = [ +// { +// label: 'Username', +// type: 'text', +// placeHolder: 'Enter username', +// defaultValue: '', +// rules: { +// required: true, +// }, +// icon: , +// field: 'username', +// name: 'username', +// }, +// { +// label: 'Password', +// type: 'text', +// placeHolder: 'Enter password', +// defaultValue: '', +// rules: { +// required: true, +// }, +// icon: , +// field: 'password', +// name: 'password', +// }, +// ]; +// const signupFields = [ +// { +// label: 'First Name', +// type: 'text', +// placeHolder: 'Enter first name', +// defaultValue: '', +// rules: { +// required: true, +// }, +// icon: , +// field: 'firstName', +// name: 'firstName', +// }, +// { +// label: 'Last Name', +// type: 'text', +// placeHolder: 'Enter last name', +// defaultValue: '', +// rules: { +// required: true, +// }, +// icon: , +// field: 'lastName', +// name: 'lastName', +// }, +// { +// label: 'Email', +// type: 'email', +// placeHolder: 'Enter email', +// defaultValue: '', +// rules: { +// required: true, +// }, +// icon: , +// field: 'email', +// name: 'email', +// }, +// ]; +// const addDeckFields = [ +// { +// label: 'Name', +// type: 'text', +// placeHolder: 'Enter deck name', +// defaultValue: '', +// rules: { +// required: true, +// }, +// icon: , +// field: 'name', +// value: '', +// required: true, +// name: 'name', +// }, +// { +// label: 'Description', +// type: 'text', +// placeHolder: 'Enter deck description', +// defaultValue: '', +// rules: { +// required: true, +// multiline: true, +// rows: 4, +// }, +// value: '', +// multiline: true, +// rows: 4, +// icon: , +// required: false, +// name: 'description', +// }, +// ]; +// const updateDeckFields = [ +// { +// label: 'Tags', +// type: 'chips', +// placeHolder: 'Enter tags', +// defaultValue: '', +// rules: { +// required: true, +// }, +// chipData: tags, +// icon: , +// onAddChip: handleAddTag, +// onDeleteChip: handleDeleteTag, +// required: false, +// name: 'tags', +// value: tags.join(', '), +// // chipData: tags, +// // icon: null, +// // onAddChip: handleAddTag, +// // onDeleteChip: handleDeleteTag, +// // required: false, +// // name: 'tags', +// }, +// { +// label: 'Color', +// type: 'select', +// placeHolder: 'Select color', +// defaultValue: '', +// rules: { +// required: true, +// }, +// 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' }, +// ], +// name: 'color', +// }, +// ]; +// const authFormFields = [...signupFields, ...loginFields]; +// const collectionFormFields = [ +// { +// label: 'Name', +// type: 'text', +// placeHolder: 'Enter collection name', +// defaultValue: '', +// rules: { +// required: true, +// }, +// icon: , +// value: '', +// required: true, +// name: 'name', +// }, +// { +// label: 'Description', +// type: 'text', +// placeHolder: 'Enter collection description', +// defaultValue: '', +// rules: { +// required: true, +// multiline: true, +// rows: 4, +// }, +// icon: , +// value: '', +// required: true, +// multiline: true, +// rows: 4, +// name: 'description', +// }, +// ]; +// const deckFormFields = [...addDeckFields, ...updateDeckFields]; +// const searchFormFields = [ +// { +// label: 'Search Cards', +// type: 'text', +// placeholder: 'Search for cards...', +// defaultValue: '', +// rules: { +// required: false, +// }, +// required: false, +// value: forms?.searchForm?.searchTerm || '', +// onChange: handleChange, +// onFocus: handleFocus, +// onBlur: handleBlur, +// name: 'searchTerm', +// }, +// ]; +// const statisticsFormFields = [ +// { value: 'highPoint', label: 'High Point' }, +// { value: 'lowPoint', label: 'Low Point' }, +// { value: 'twentyFourHourAverage', label: '24 Hour Average' }, +// { value: 'average', label: 'Average' }, +// { value: 'volume', label: 'Volume' }, +// { value: 'volatility', label: 'Volatility' }, +// ]; +// const themeFormFields = [ +// { value: 'light', label: 'Light Theme' }, +// { value: 'dark', label: 'Dark Theme' }, +// { value: 'system', label: 'System Theme' }, +// ]; +// const timeRangeFormFields = [ +// { value: '24hr', label: 'Today' }, +// { value: '7d', label: 'This Week' }, +// { value: '30d', label: 'This Month' }, +// { value: '90d', label: 'Last Three Months' }, +// { value: '180d', label: 'Last Six Months' }, +// { value: '270d', label: 'Last Nine Months' }, +// { value: '365d', label: 'All Time' }, +// ]; +// const formHandlers = { +// signupForm: async (formData) => signup(formData), +// loginForm: async (formData) => login(formData), +// updateUserDataForm: (formData) => console.log(formData), +// addCollectionForm: (formData, additionalData) => +// createNewCollection(formData, additionalData), +// updateCollectionForm: (formData, additionalData) => +// updateCollection(additionalData, 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, additionalData), +// searchSettingsSelector: (formData, additionalData) => +// 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); +// toggleActiveForm('loginForm', 'signupForm'); +// }, +// }; + +// return formHandlers; +// }; diff --git a/src/assets/currentlyUnused/helpers2.jsx b/src/assets/currentlyUnused/helpers2.jsx new file mode 100644 index 0000000..527c92d --- /dev/null +++ b/src/assets/currentlyUnused/helpers2.jsx @@ -0,0 +1,120 @@ +// export const calculatePriceChanges = (data) => { +// if (!Array.isArray(data) || data.length === 0) return []; + +// const sortedData = data.sort((a, b) => new Date(a.x) - new Date(b.x)); +// const latestDataPoint = sortedData[sortedData.length - 1]; +// const latestTime = new Date(latestDataPoint.x).getTime(); +// const twentyFourHoursAgo = latestTime - 24 * 60 * 60 * 1000; + +// let closestIndex = -1; +// let closestTimeDifference = Number.MAX_SAFE_INTEGER; + +// for (let i = 0; i < sortedData.length - 1; i++) { +// const time = new Date(sortedData[i].x).getTime(); +// const timeDifference = Math.abs(time - twentyFourHoursAgo); + +// if (timeDifference < closestTimeDifference) { +// closestTimeDifference = timeDifference; +// closestIndex = i; +// } +// } + +// if (closestIndex !== -1) { +// const pastPrice = sortedData[closestIndex].y; +// const priceChange = latestDataPoint.y - pastPrice; +// const percentageChange = ((priceChange / pastPrice) * 100).toFixed(2); +// return [ +// { +// startDate: sortedData[closestIndex].x, +// lowPoint: pastPrice.toFixed(2), +// highPoint: latestDataPoint?.y?.toFixed(2), +// endDate: latestDataPoint?.x, +// priceChange: priceChange.toFixed(2), +// percentageChange: `${percentageChange}%`, +// priceIncreased: priceChange > 0, +// }, +// ]; +// } + +// return []; +// }; + +// export const getTopCollection = (collections) => { +// return collections?.reduce( +// (max, collection) => +// max.totalPrice > collection.totalPrice ? max : collection, +// collections[0] +// ); +// }; +// export const getTopCard = (cards) => { +// return cards?.reduce( +// (max, card) => (max.price > card.price ? max : card), +// cards[0] +// ); +// }; +// const calculateTotalPriceOfAllCollections = (collections) => { +// return collections +// ?.reduce((total, collection) => total + collection.totalPrice, 0) +// .toFixed(2); +// }; + +// export const calculateStatistics = (data, timeRange, allCollections, cards) => { +// if (!data || !Array.isArray(data.data) || data.data.length === 0) return {}; + +// const filteredData = data?.data?.filter( +// (item) => new Date(item?.x).getTime() >= Date.now() - timeRange +// ); +// if (filteredData.length === 0) return {}; + +// let highPoint = Math.max(...filteredData.map((item) => item.y)); +// let lowPoint = Math.min(...filteredData.map((item) => item.y)); +// let sum = filteredData.reduce((acc, curr) => acc + curr.y, 0); +// let averageData = calculatePriceChanges(filteredData); +// let average = sum / filteredData.length || 0; +// let volume = filteredData.length; +// let mean = sum / volume; +// let squaredDiffs = filteredData.map((item) => { +// const diff = item.y - mean; +// return diff * diff; +// }); +// let volatility = Math.sqrt(squaredDiffs.reduce((a, b) => a + b, 0) / volume); + +// const topCollection = getTopCollection(allCollections); +// const topCard = getTopCard(cards); +// const totalPriceAllCollections = +// calculateTotalPriceOfAllCollections(allCollections); + +// return { +// highPoint: highPoint.toFixed(2), +// lowPoint: lowPoint.toFixed(2), +// twentyFourHourAverage: { +// startDate: averageData[0]?.startDate, +// endDate: averageData[0]?.endDate, +// lowPoint: averageData[0]?.lowPoint, +// highPoint: averageData[0]?.highPoint, +// priceChange: averageData[0]?.priceChange, +// percentageChange: averageData[0]?.percentageChange, +// priceIncreased: averageData[0]?.priceIncreased, +// }, +// average: average.toFixed(2), +// volume, +// volatility: volatility.toFixed(2), +// general: { +// totalPrice: totalPriceAllCollections, +// topCard: topCard?.name, // or any other identifier for the card +// topCollection: topCollection?.name, // or any other identifier for the collection +// }, +// }; +// }; +// export const calculateStatsForCollection = (collection, timeRange) => { +// try { +// const data = collection?.chartData?.allXYValues || []; +// return calculateStatistics({ data }, timeRange) || {}; +// } catch (error) { +// console.error( +// `Error calculating statistics for collection ${collection._id}:`, +// error +// ); +// return {}; // Default value in case of error +// } +// }; diff --git a/src/assets/currentlyUnused/menuItemsData.jsx b/src/assets/currentlyUnused/menuItemsData.jsx index fc5a409..9d8cfbf 100644 --- a/src/assets/currentlyUnused/menuItemsData.jsx +++ b/src/assets/currentlyUnused/menuItemsData.jsx @@ -36,7 +36,7 @@ // // 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'; +// // const iconColor = isMobileView ? theme.palette.success.secondary : 'white'; // try { // const baseMenuItems = [ diff --git a/src/assets/currentlyUnused/schemas.jsx b/src/assets/currentlyUnused/schemas.jsx new file mode 100644 index 0000000..1f079a9 --- /dev/null +++ b/src/assets/currentlyUnused/schemas.jsx @@ -0,0 +1,71 @@ +// import { z } from 'zod'; + +// const loginSchema = z.object({ +// username: z.string().min(1, 'Username is required'), +// password: z.string().min(1, 'Password is required'), +// }); +// const signupSchema = z.object({ +// firstName: z.string().min(1, 'First Name is required'), +// lastName: z.string().min(1, 'Last Name is required'), +// email: z.string().email('Invalid email format'), +// }); +// const authFormSchema = signupSchema.merge(loginSchema); +// const addDeckSchema = z.object({ +// name: z.string().min(1, 'Deck Name is required'), +// description: z.string().min(1, 'Description is required').optional(), +// }); +// const updateDeckSchema = z.object({ +// tags: z.array(z.string()).optional(), // Assuming tags can be optional +// color: z.enum([ +// 'red', +// 'blue', +// 'green', +// 'yellow', +// 'purple', +// 'pink', +// 'orange', +// 'teal', +// ]), +// }); +// const deckFormSchema = addDeckSchema.merge(updateDeckSchema); +// const collectionFormSchema = z.object({ +// name: z.string().min(1, 'Collection Name is required'), +// description: z.string().min(1, 'Collection Description is required'), +// }); +// const searchFormSchema = z.object({ +// searchTerm: z.string().optional(), +// }); +// const statisticsSchema = z.object({ +// selectedStatistic: z +// .enum([ +// 'highPoint', +// 'lowPoint', +// 'twentyFourHourAverage', +// 'average', +// 'volume', +// 'volatility', +// ]) +// .optional(), +// }); +// const themeFormSchema = z.object({ +// theme: z.enum(['light', 'dark', 'system']).optional(), +// }); +// const timeRangeFormSchema = z.object({ +// timeRange: z +// .enum(['24hr', '7d', '30d', '90d', '180d', '270d', '365d']) +// .optional(), +// }); + +// export { +// loginSchema, +// signupSchema, +// authFormSchema, +// addDeckSchema, +// updateDeckSchema, +// deckFormSchema, +// collectionFormSchema, +// searchFormSchema, +// statisticsSchema, +// themeFormSchema, +// timeRangeFormSchema, +// }; diff --git a/src/layout/collection/data/statList.jsx b/src/assets/currentlyUnused/statList.jsx similarity index 86% rename from src/layout/collection/data/statList.jsx rename to src/assets/currentlyUnused/statList.jsx index 978c285..401f5ad 100644 --- a/src/layout/collection/data/statList.jsx +++ b/src/assets/currentlyUnused/statList.jsx @@ -1,4 +1,4 @@ -import useSelectedCollection from '../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; const data = [ { diff --git a/src/assets/currentlyUnused/useAuthDialog.jsx b/src/assets/currentlyUnused/useAuthDialog.jsx new file mode 100644 index 0000000..c9f8805 --- /dev/null +++ b/src/assets/currentlyUnused/useAuthDialog.jsx @@ -0,0 +1,26 @@ +// import { useCallback, useState } from 'react'; +// import { useAuthContext } from '../MAIN_CONTEXT/AuthContext/authContext'; +// import useDialog from './useDialog'; +// import useManageCookies from './useManageCookies'; + +// const useAuthDialog = () => { +// const { logout } = useAuthContext(); +// const { addCookie, getCookie, deleteCookie } = useManageCookies(); +// const { isLoggedIn } = getCookie(['isLoggedIn']); +// console.log(isLoggedIn); +// const { openDialog, closeDialog } = useDialog(); + +// // Toggle login dialog based on authentication status +// const toggleLoginDialog = useCallback(() => { +// if (!isLoggedIn) { +// openDialog('Login'); +// } else { +// logout(); +// closeDialog('Login'); +// } +// }, [isLoggedIn, openDialog, closeDialog, logout]); + +// return { toggleLoginDialog, isLoggedIn, logout }; +// }; + +// export default useAuthDialog; diff --git a/src/assets/currentlyUnused/useSelectCollectionListStyles.jsx b/src/assets/currentlyUnused/useSelectCollectionListStyles.jsx index a0b96a4..5c6a0e6 100644 --- a/src/assets/currentlyUnused/useSelectCollectionListStyles.jsx +++ b/src/assets/currentlyUnused/useSelectCollectionListStyles.jsx @@ -16,15 +16,15 @@ // export const EditButton = styled(Button)(({ theme }) => ({ // marginLeft: theme.spacing(2), -// backgroundColor: theme.palette.backgroundA.dark, +// backgroundColor: theme.palette.greenAccent.light, // color: '#ffffff', // '&:hover': { -// backgroundColor: theme.palette.backgroundA.darker, +// backgroundColor: theme.palette.greenAccent.default, // }, // })); // export const CollectionListItem = styled('div')(({ theme }) => ({ -// backgroundColor: theme.palette.backgroundA.lightest, +// backgroundColor: theme.palette.greenAccent.contrastText, // transition: theme.transitions.create(['background-color', 'box-shadow']), // '&:hover': { // backgroundColor: theme.palette.grey[100], @@ -51,7 +51,7 @@ // width: '100%', // borderRadius: theme.shape.borderRadius, // // eslint-disable-next-line max-len -// background: `linear-gradient(to right, ${theme.palette.backgroundA.lightest} 40%, ${theme.palette.grey[300]} 70%, ${theme.palette.backgroundA.lightest} 100%)`, +// background: `linear-gradient(to right, ${theme.palette.greenAccent.contrastText} 40%, ${theme.palette.grey[300]} 70%, ${theme.palette.greenAccent.contrastText} 100%)`, // animation: '$shimmer 2s infinite', // })); @@ -126,14 +126,14 @@ // // }, // // editButton: { // // marginLeft: theme.spacing(2), -// // backgroundColor: theme.palette.backgroundA.dark, +// // backgroundColor: theme.palette.greenAccent.light, // // color: '#ffffff', // // '&:hover': { -// // backgroundColor: theme.palette.backgroundA.darker, +// // backgroundColor: theme.palette.greenAccent.default, // // }, // // }, // // collectionListItem: { -// // backgroundColor: theme.palette.backgroundA.lightest, +// // backgroundColor: theme.palette.greenAccent.contrastText, // // transition: theme.transitions.create(['background-color', 'box-shadow']), // // '&:hover': { // // backgroundColor: theme.palette.grey[100], @@ -150,7 +150,7 @@ // // borderRadius: theme.shape.borderRadius, // // // For shimmer effect // // // eslint-disable-next-line max-len -// // background: `linear-gradient(to right, ${theme.palette.backgroundA.lightest} 40%, ${theme.palette.grey[300]} 70%, ${theme.palette.backgroundA.lightest} 100%)`, +// // background: `linear-gradient(to right, ${theme.palette.greenAccent.contrastText} 40%, ${theme.palette.grey[300]} 70%, ${theme.palette.greenAccent.contrastText} 100%)`, // // animation: '$shimmer 2s infinite', // // }, // // '@keyframes shimmer': { diff --git a/src/assets/themes/base/colors.jsx b/src/assets/themes/base/colors.jsx index 6a83b9c..62d1afe 100644 --- a/src/assets/themes/base/colors.jsx +++ b/src/assets/themes/base/colors.jsx @@ -1,11 +1,6 @@ import hexToRgba from '../functions/hexToRgba'; import { - backgroundA, - backgroundB, - backgroundE, - backgroundF, - backgroundG, - backgroundGSecondary, + rarityOverlay, chartTheme, rarity, info, @@ -17,6 +12,11 @@ import { action, primary, secondary, + grey, + greenAccent, + redAccent, + blueAccent, + customDarkTheme, } from './customColorPalettes'; const colors = { chartTheme: chartTheme, @@ -24,55 +24,14 @@ const colors = { default: '#f0f2f5', paper: '#fff', }, - // PRIMARY COLORS - backgroundA, - backgroundD: { - darkest: hexToRgba(backgroundA.darkest, 0.9), - darker: hexToRgba(backgroundA.darker, 0.7), - dark: hexToRgba(backgroundA.dark, 0.6), - default: hexToRgba(backgroundA.default, 0.5), - light: hexToRgba(backgroundA.light, 0.4), - lighter: hexToRgba(backgroundA.lighter, 0.3), - lightest: hexToRgba(backgroundA.lightest, 0.2), - contrastText: '#FBFAF2', - }, - // SECONDARY COLORS - backgroundB, - backgroundC: { - darkest: hexToRgba(backgroundB.darkest, 0.9), - darker: hexToRgba(backgroundB.darker, 0.8), - dark: hexToRgba(backgroundB.dark, 0.7), - default: hexToRgba(backgroundB.default, 0.6), - light: hexToRgba(backgroundB.light, 0.5), - lighter: hexToRgba(backgroundB.lighter, 0.4), - lightest: hexToRgba(backgroundB.lightest, 0.3), - contrastText: '#FBFAF2', - }, - // TERTIARY COLORS - backgroundE, - backgroundF, - backgroundG, - backgroundGSecondary, - + greenAccent: greenAccent, + redAccent: redAccent, + blueAccent: blueAccent, + customDarkTheme: customDarkTheme, // COLORS FOR CARD RARITY OVERLAY rarity, // CARD RARITY OVERLAYS - rarityOverlay: { - common: hexToRgba(rarity.common, 0.5), - uncommon: hexToRgba(rarity.uncommon, 0.5), - rare: hexToRgba(rarity.rare, 0.5), - super: hexToRgba(rarity.super, 0.5), - ultra: hexToRgba(rarity.ultra, 0.5), - secret: hexToRgba(rarity.secret, 0.5), - ghost: hexToRgba(rarity.ghost, 0.5), - starlight: hexToRgba(rarity.starlight, 0.5), - prismatic: hexToRgba(rarity.prismatic, 0.5), - collector: hexToRgba(rarity.collector, 0.5), - shortPrint: hexToRgba(rarity.shortPrint, 0.5), - parallel: hexToRgba(rarity.parallel, 0.5), - qcr: hexToRgba(rarity.qcr, 0.5), - // Add more rarities as needed - }, + rarityOverlay, // TEXT COLORS text, // DIVIDER COLORS @@ -87,7 +46,7 @@ const colors = { warning, // ERROR COLORS error, - + grey, // OTHER COLORS // text: { // main: '#7b809a', @@ -113,38 +72,16 @@ const colors = { main: '#5CDB95', focus: '#379683', }, - - // COLOR TO COMPLEMENT GREEN - greenComplement: { - main: '#05386B', - focus: '#05386B', - }, - primary, - secondary, light: { main: '#f0f2f5', focus: '#f0f2f5', }, - dark: { main: '#344767', focus: '#2c3c58', }, - - grey: { - 100: '#f8f9fa', - 200: '#f0f2f5', - 300: '#dee2e6', - 400: '#ced4da', - 500: '#adb5bd', - 600: '#6c757d', - 700: '#495057', - 800: '#343a40', - 900: '#212529', - }, - gradients: { primary: { main: '#EC407A', @@ -188,21 +125,7 @@ const colors = { main: '#42424a', state: '#191919', }, - - backgroundA: { - main: '#f8f9fa', - state: '#f8f9fa', - }, - backgroundB: { - main: '#f0f2f5', - state: '#f0f2f5', - }, - backgroundF: { - main: '#f8f9fa', - state: '#f8f9fa', - }, }, - socialMediaColors: { facebook: { main: '#3b5998', @@ -264,7 +187,6 @@ const colors = { dark: '#2a3749', }, }, - badgeColors: { primary: { background: '#f8b3ca', @@ -306,7 +228,6 @@ const colors = { text: '#1e2e4a', }, }, - coloredShadows: { primary: '#e91e62', secondary: '#110e0e', @@ -316,34 +237,14 @@ const colors = { error: '#f44336', light: '#adb5bd', dark: '#404040', - backgroundA: '#f8f9fa', - backgroundF: '#f8f9fa', + greenAccent: '#4caf4f', + redAccent: '#f44336', + blueAccent: '#2196f3', }, - inputBorderColor: '#d2d6da', - tabs: { indicator: { boxShadow: '#ddd' }, }, - // info: { - // main: '#1A73E8', - // focus: '#1662C4', - // }, - - // success: { - // main: '#4CAF50', - // focus: '#67bb6a', - // }, - - // warning: { - // main: '#fb8c00', - // focus: '#fc9d26', - // }, - - // error: { - // main: '#F44335', - // focus: '#f65f53', - // }, }; export default colors; diff --git a/src/assets/themes/base/customColorPalettes.jsx b/src/assets/themes/base/customColorPalettes.jsx index 5925fd6..0ffac93 100644 --- a/src/assets/themes/base/customColorPalettes.jsx +++ b/src/assets/themes/base/customColorPalettes.jsx @@ -1,31 +1,70 @@ import hexToRgba from '../functions/hexToRgba'; +import rgba from '../functions/rgba'; +const colorTextForDark = rgba('white', 0.96); +const rarity = { + common: '#C0C0C0', // Silver + uncommon: '#B8860B', // DarkGoldenRod + rare: '#FFD700', // Gold + super: '#00BFFF', // DeepSkyBlue + ultra: '#FF6347', // Tomato + secret: '#800080', // Purple + ghost: '#F8F8FF', // GhostWhite + starlight: '#F0E68C', // Khaki + prismatic: '#E6E6FA', // Lavender + collector: '#DAA520', // GoldenRod + shortPrint: '#778899', // LightSlateGrey + parallel: '#BA55D3', // MediumOrchid + qcr: '#FF4500', // OrangeRed + // Add more rarities as needed +}; +const rarityOverlay = { + common: hexToRgba(rarity.common, 0.5), + uncommon: hexToRgba(rarity.uncommon, 0.5), + rare: hexToRgba(rarity.rare, 0.5), + super: hexToRgba(rarity.super, 0.5), + ultra: hexToRgba(rarity.ultra, 0.5), + secret: hexToRgba(rarity.secret, 0.5), + ghost: hexToRgba(rarity.ghost, 0.5), + starlight: hexToRgba(rarity.starlight, 0.5), + prismatic: hexToRgba(rarity.prismatic, 0.5), + collector: hexToRgba(rarity.collector, 0.5), + shortPrint: hexToRgba(rarity.shortPrint, 0.5), + parallel: hexToRgba(rarity.parallel, 0.5), + qcr: hexToRgba(rarity.qcr, 0.5), + // Add more rarities as needed +}; + const error = { light: '#e57373', main: '#f44336', - focus: '#f44336', + secondary: '#f44336', + focus: rgba('#f44336' || 'white', 0.15), dark: '#d32f2f', - darkest: '#7f2e2eff', // --persian-plum-- + darkest: '#7f2e2eff', contrastText: '#fff', - hoverContrastText: '#111', + hoverContrastText: colorTextForDark, }; const warning = { + light: '#ffb74d', main: '#ff9800', focus: '#ff9800', dark: '#f57c00', - light: '#ffb74d', + secondary: '#ff5722', }; const success = { - // main: '#4caf50', - // dark: '#388e3c', - dark: '#3da58a', - main: '#4cceac', - focus: '#4cceac', light: '#70d8bd', // '#4cceac', + main: '#18b984', + secondary: '#5CDB95', + focus: rgba('#18b984' || 'white', 0.15), + secondaryFocus: rgba('#5CDB95' || 'white', 0.15), + dark: '#3da58a', + darkest: '#2e7c67', // '#70d8bd', contrastText: '#fff', - hoverContrastText: '#111', + hoverContrastText: colorTextForDark, }; const info = { main: '#3781F1', + secondary: '#3781F1', // main: '#2196f3', dark: '#1976d2', light: '#64b5f6', @@ -33,7 +72,8 @@ const info = { contrastText: '#fff', }; const text = { - main: '#212121', + // main: '#212121', + main: '#3d3d3d', // '#424242', focus: '#212121', dark: '#424242', primary: '#212121', @@ -45,132 +85,69 @@ const action = { hover: '#424242', disabled: '#eeeeee', }; -const backgroundA = { - darkest: '#2e7c67', // '#70d8bd', - darker: '#3da58a', - dark: '#4cceac', - default: '#70d8bd', // '#4cceac', - light: '#94e2cd', - lighter: '#b7ebde', - lightest: '#dbf5ee', - contrastTextA: '#FBFAF2', - contrastTextB: '#333', - contrastTextC: '#555', - contrastTextD: '#000', - hover: '#4cceac', -}; -const backgroundB = { - darkest: '#111', - darker: '#222', - dark: '#333', - default: '#444', - light: '#555', - lighter: '#666', - lightest: '#777', - contrastText: '#FBFAF2', -}; -const backgroundC = { - darkest: hexToRgba(backgroundB.darkest, 0.9), - darker: hexToRgba(backgroundB.darker, 0.8), - dark: hexToRgba(backgroundB.dark, 0.7), - default: hexToRgba(backgroundB.default, 0.6), - light: hexToRgba(backgroundB.light, 0.5), - lighter: hexToRgba(backgroundB.lighter, 0.4), - lightest: hexToRgba(backgroundB.lightest, 0.3), -}; -const backgroundD = { - darkest: hexToRgba(backgroundA.darkest, 0.9), - darker: hexToRgba(backgroundA.darker, 0.7), - dark: hexToRgba(backgroundA.dark, 0.6), - default: hexToRgba(backgroundA.default, 0.5), - light: hexToRgba(backgroundA.light, 0.4), - lighter: hexToRgba(backgroundA.lighter, 0.3), - lightest: hexToRgba(backgroundA.lightest, 0.2), -}; -const backgroundE = { - darkest: '#0d5d96', - darker: '#206d9b', - dark: '#4e93a6', - default: '#7cb8b1', - main: '#7cb8b1', - focus: '#7cb8b1', - light: '#8ec7b6', - lightBlue: '#57909F', - lighter: '#b7ebde', - lighterBlue: '#7EACB9', - lightest: '#dbf5ee', - lightestBlue: '#A5C8D2', - contrastText: '#FBFAF2', -}; -const backgroundF = { - darkest: hexToRgba(backgroundE.darkest, 0.9), - darker: hexToRgba(backgroundE.darker, 0.8), - dark: hexToRgba(backgroundE.dark, 0.7), - default: hexToRgba(backgroundE.default, 0.6), - main: hexToRgba(backgroundE.main, 0.5), - focus: hexToRgba(backgroundE.focus, 0.4), - light: hexToRgba(backgroundE.light, 0.5), - lighter: hexToRgba(backgroundE.lighter, 0.4), - lightest: hexToRgba(backgroundE.lightest, 0.3), -}; -const backgroundG = { - darkest: '#073b4cff', // --midnight-green-- - darker: '#0c637fff', // --cerulean-- - dark: '#118ab2ff', // --blue-ncs-- - default: '#0cb0a9ff', // --light-sea-green-- - light: '#06d6a0ff', // --emerald-- - lighter: '#91dacbff', // --tiffany-blue-- - lightest: '#c8ede5ff', // --mint-green-- - contrastText: '#fff', -}; -const backgroundGSecondary = { - darkest: '#7f2e2eff', // --persian-plum-- - darker: '#a63c3cff', // --pomegranate-- - dark: '#cc4a4aff', // --flamingo-- - default: '#e55e5eff', // --sunset-orange-- - light: '#f4755fff', // --bittersweet-- - lighter: '#f89a7dff', // --rajah-- - lightest: '#facbb0ff', // --navajo-white-- - contrastText: '#fff', -}; const grey = { + lighterSimpleGrey: '#777', + simpleGrey: '#333', + clearGrey: hexToRgba('#444', 0.6), + black: '#040509', + white: '#f2f0f0', + blueGrey: '#141b2d', + lightBlueGrey: '#1F2A40', darkest: '#141414', - darkert: '#292929', + darker: '#292929', dark: '#3d3d3d', default: '#525252', light: '#666666', lighter: '#858585', + lighter2: '#8c8c8c', lightest: '#a3a3a3', evenLighter: '#c2c2c2', contrastText: '#e0e0e0', + main: '#e0e0e0', }; const primary = { darkest: '#040509', darker: '#040509', dark: '#040509', default: '#f2f0f0', - main: '#5CDB95', - focus: '#379683', light: '#141b2d', lighter: '#1F2A40', lightest: '#727681', evenLighter: '#8c8c8c', contrastText: '#e0e0e0', + secondary: '#f2f0f0', + main: '#141b2d', + focus: '#141b2d', + hoverContrastText: colorTextForDark, }; const secondary = { main: '#8c8c8c', focus: '#8f93a9', + hoverContrastText: colorTextForDark, + contrastText: '#e0e0e0', + secondary: '#8c8c8c', }; const greenAccent = { + crystalGreen: hexToRgba('#4cceac', 0.6), + darkCerulean: hexToRgba('#0d5d96', 0.9), + greyGreen: '#A5C8D2', + greenBlue: hexToRgba('#4e93a6', 0.8), + pureGreenBlue: '#4e93a6', + emerald: '#06d6a0ff', + lighterSeaGreen: '#8ec7b6', + lightSeaGreen: '#0cb0a9ff', darkest: '#0f2922', // Assuming this is the darkest darker: '#1e5245', // Next darker shade dark: '#2e7c67', // Next dark shade default: '#3da58a', // Default considered here as the mid-point + main: '#18b984', + focus: rgba('#18b984' || 'white', 0.15), light: '#4cceac', // Light shade lighter: '#70d8bd', // Lighter shade lightest: '#94e2cd', // Lightest shade evenLighter: '#b7ebde', // Even lighter than the lightest contrastText: '#dbf5ee', // Most contrasting or lightest, could be adjusted + secondary: '#5CDB95', }; const redAccent = { darkest: '#2c100f', @@ -182,6 +159,10 @@ const redAccent = { lightest: '#e99592', evenLighter: '#f1b9b7', contrastText: '#f8dcdb', + secondary: '#f1b9b7', + main: '#db4f4a', + focus: rgba('#db4f4a' || 'white', 0.15), + hoverContrastText: colorTextForDark, }; const blueAccent = { darkest: '#151632', @@ -193,6 +174,10 @@ const blueAccent = { lightest: '#a4a9fc', evenLighter: '#c3c6fd', contrastText: '#e1e2fe', + secondary: '#c3c6fd', + main: '#6870fa', + focus: rgba('#6870fa' || 'white', 0.15), + hoverContrastText: colorTextForDark, }; const chartTheme = { primary, @@ -201,32 +186,76 @@ const chartTheme = { redAccent, blueAccent, }; -const rarity = { - common: '#C0C0C0', // Silver - uncommon: '#B8860B', // DarkGoldenRod - rare: '#FFD700', // Gold - super: '#00BFFF', // DeepSkyBlue - ultra: '#FF6347', // Tomato - secret: '#800080', // Purple - ghost: '#F8F8FF', // GhostWhite - starlight: '#F0E68C', // Khaki - prismatic: '#E6E6FA', // Lavender - collector: '#DAA520', // GoldenRod - shortPrint: '#778899', // LightSlateGrey - parallel: '#BA55D3', // MediumOrchid - qcr: '#FF4500', // OrangeRed - // Add more rarities as needed +const customDarkTheme = { + text: { + light: '#e0e0e0', + dark: '#141414', + default: '#3d3d3d', + contrastText: '#fff', + color: '#70d8bd', + }, + background: { + grey: { + darkest: '#141414', + darker: '#292929', + dark: '#3d3d3d', + default: '#525252', + light: '#666666', + lighter: '#858585', + lightest: '#a3a3a3', + evenLighter: '#c2c2c2', + contrastText: '#e0e0e0', + }, + primary: { + darkest: '#040509', + darker: '#040509', + dark: '#040509', + default: '#f2f0f0', + light: '#141b2d', + lighter: '#1F2A40', + lightest: '#727681', + evenLighter: '#8c8c8c', + contrastText: '#e0e0e0', + }, + secondary: {}, + greenAccent: { + darkest: '#0f2922', // Assuming this is the darkest + darker: '#1e5245', // Next darker shade + dark: '#2e7c67', // Next dark shade + default: '#3da58a', // Default considered here as the mid-point + light: '#4cceac', // Light shade + lighter: '#70d8bd', // Lighter shade + lightest: '#94e2cd', // Lightest shade + evenLighter: '#b7ebde', // Even lighter than the lightest + contrastText: '#dbf5ee', // Most contrasting or lightest, could be adjusted + }, + redAccent: { + darkest: '#2c100f', + darker: '#58201e', + dark: '#832f2c', + default: '#af3f3b', + light: '#db4f4a', + lighter: '#e2726e', + lightest: '#e99592', + evenLighter: '#f1b9b7', + contrastText: '#f8dcdb', + }, + blueAccent: { + darkest: '#151632', + darker: '#2a2d64', + dark: '#3e4396', + default: '#535ac8', + light: '#6870fa', + lighter: '#868dfb', + lightest: '#a4a9fc', + evenLighter: '#c3c6fd', + contrastText: '#e1e2fe', + }, + }, }; export { chartTheme, - backgroundA, - backgroundB, - backgroundC, - backgroundD, - backgroundE, - backgroundF, - backgroundG, - backgroundGSecondary, + rarityOverlay, rarity, error, warning, @@ -237,7 +266,32 @@ export { action, primary, secondary, + customDarkTheme, + grey, + greenAccent, + redAccent, + blueAccent, }; +// const backgroundA = { +// darkest: '#2e7c67', // '#70d8bd', +// darker: '#3da58a', +// dark: '#4cceac', +// default: '#70d8bd', // '#4cceac', +// light: '#94e2cd', +// lighter: '#b7ebde', +// lightest: '#dbf5ee', +// contrastTextA: '#FBFAF2', +// contrastTextB: '#333', +// contrastTextC: '#555', +// contrastTextD: '#000', +// hover: '#4cceac', +// }; +// main: '#4caf50', +// dark: '#388e3c', +// main: '#4cceac', +// focus: '#4cceac', +// contrastText: colorTextForDark, +// hoverContrastText: '#111', // error: { // main: colorsA.redAccent[500], // dark: colorsA.redAccent[700], @@ -275,3 +329,77 @@ export { // hover: colorsA.grey[800], // disabled: colorsA.grey[200], // }, +// const backgroundB = { +// darkest: '#111', +// darker: '#222', +// dark: '#333', +// default: '#444', +// light: '#555', +// lighter: '#666', +// lightest: '#777', +// contrastText: '#FBFAF2', +// }; +// const backgroundG = { +// darkest: '#073b4cff', // --midnight-green-- +// darker: '#0c637fff', // --cerulean-- +// dark: '#118ab2ff', // --blue-ncs-- +// default: '#0cb0a9ff', // --light-sea-green-- +// light: '#06d6a0ff', // --emerald-- +// lighter: '#91dacbff', // --tiffany-blue-- +// lightest: '#c8ede5ff', // --mint-green-- +// contrastText: '#fff', +// }; +// const backgroundGSecondary = { +// darkest: '#7f2e2eff', // --persian-plum-- +// darker: '#a63c3cff', // --pomegranate-- +// dark: '#cc4a4aff', // --flamingo-- +// default: '#e55e5eff', // --sunset-orange-- +// light: '#f4755fff', // --bittersweet-- +// lighter: '#f89a7dff', // --rajah-- +// lightest: '#facbb0ff', // --navajo-white-- +// contrastText: '#fff', +// }; +// const backgroundC = { +// darkest: hexToRgba(backgroundB.darkest, 0.9), +// darker: hexToRgba(backgroundB.darker, 0.8), +// dark: hexToRgba(backgroundB.dark, 0.7), +// default: hexToRgba(backgroundB.default, 0.6), +// light: hexToRgba(backgroundB.light, 0.5), +// lighter: hexToRgba(backgroundB.lighter, 0.4), +// lightest: hexToRgba(backgroundB.lightest, 0.3), +// }; +// const backgroundD = { +// darkest: hexToRgba(greenAccent.dark, 0.9), +// darker: hexToRgba(greenAccent.default, 0.7), +// dark: hexToRgba('#4cceac', 0.6), +// default: hexToRgba(greenAccent.lighter, 0.5), +// light: hexToRgba(greenAccent.lightest, 0.4), +// lighter: hexToRgba(backgroundA.lighter, 0.3), +// lightest: hexToRgba(greenAccent.contrastText, 0.2), +// }; +// const backgroundF = { +// darkest: hexToRgba(backgroundE.darkest, 0.9), +// darker: hexToRgba(backgroundE.darker, 0.8), +// dark: hexToRgba(backgroundE.dark, 0.7), +// default: hexToRgba(backgroundE.default, 0.6), +// main: hexToRgba(backgroundE.main, 0.5), +// focus: hexToRgba(backgroundE.focus, 0.4), +// light: hexToRgba(backgroundE.light, 0.5), +// lighter: hexToRgba(backgroundE.lighter, 0.4), +// lightest: hexToRgba(backgroundE.lightest, 0.3), +// }; +// const backgroundE = { +// darkest: '#0d5d96', // - colorname: 'dark-cerulean' - hex: '#0d5d96' - rgb: 'rgb(13, 93, 150)' - hsl: 'hsl(209, 84%, 32%)' +// darker: '#206d9b', // - colorname: 'dark-blue' - hex: '#206d9b +// dark: '#4e93a6', // - colorname: 'blue' - hex: '#4e93a6' - rgb: 'rgb(78, 147, 166)' - hsl: 'hsl(194, 35%, 47%)' +// default: '#7cb8b1', +// main: '#7cb8b1', +// focus: '#7cb8b1', +// light: '#8ec7b6', +// lightBlue: '#57909F', +// lighter: '#b7ebde', +// lighterBlue: '#7EACB9', +// lightest: '#dbf5ee', +// lightestBlue: '#A5C8D2', +// contrastText: '#FBFAF2', +// }; diff --git a/src/assets/themes/base/typography.jsx b/src/assets/themes/base/typography.jsx index 7bd293d..d506fa3 100644 --- a/src/assets/themes/base/typography.jsx +++ b/src/assets/themes/base/typography.jsx @@ -204,7 +204,7 @@ const typography = { }, size: { - xxs: baseProperties.fontSizeXXS, + // xxs: baseProperties.fontSizeXXS, xs: baseProperties.fontSizeXS, sm: baseProperties.fontSizeSM, md: baseProperties.fontSizeMD, diff --git a/src/assets/themes/components/buttons/contained.jsx b/src/assets/themes/components/buttons/contained.jsx index 4078513..e1b6981 100644 --- a/src/assets/themes/components/buttons/contained.jsx +++ b/src/assets/themes/components/buttons/contained.jsx @@ -1,23 +1,13 @@ -// Material Dashboard 2 React Helper Functions import colors from '../../base/colors'; import typography from '../../base/typography'; import pxToRem from '../../functions/pxToRem'; -const { - white, - text, - info, - secondary, - success, - green, - backgroundE, - backgroundG, -} = colors; +const { white, text, info, secondary, success, green, grey } = colors; const { size } = typography; export default { base: { - backgroundColor: backgroundG.default, + backgroundColor: grey.default, minHeight: pxToRem(40), color: text.main, padding: `${pxToRem(10)} ${pxToRem(24)}`, @@ -58,26 +48,26 @@ export default { }, primary: { - backgroundColor: backgroundG.default, + backgroundColor: grey.default, '&:hover': { - backgroundColor: backgroundG.dark, + backgroundColor: grey.dark, }, '&:focus:not(:hover)': { - backgroundColor: backgroundG.light, + backgroundColor: grey.light, }, }, secondary: { - backgroundColor: backgroundE.light, + backgroundColor: grey.light, '&:hover': { - backgroundColor: backgroundE.light, + backgroundColor: grey.light, }, '&:focus:not(:hover)': { - backgroundColor: backgroundE.lightest, + backgroundColor: grey.lightest, }, }, }; diff --git a/src/assets/themes/components/buttons/holo.jsx b/src/assets/themes/components/buttons/holo.jsx new file mode 100644 index 0000000..4978fce --- /dev/null +++ b/src/assets/themes/components/buttons/holo.jsx @@ -0,0 +1,82 @@ +import colors from '../../base/colors'; +import typography from '../../base/typography'; +import pxToRem from '../../functions/pxToRem'; + +const { white, text, info, secondary, success, green, grey } = colors; +const { size } = typography; + +export default { + base: { + backgroundColor: grey.default, + minHeight: pxToRem(40), + color: text.main, + padding: `${pxToRem(10)} ${pxToRem(24)}`, + boxShadow: + '0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12)', + borderRadius: pxToRem(4), + border: 'none', + cursor: 'pointer', + transition: 'background-color 0.3s, color 0.3s', + '&:hover': { + backgroundColor: green.focus, + }, + '&:active, &:active:focus, &:active:hover': { + opacity: 0.85, + }, + '& .material-icon, .material-icons-round, svg': { + fontSize: `${pxToRem(16)} !important`, + }, + }, + + small: { + minHeight: pxToRem(32), + padding: `${pxToRem(6)} ${pxToRem(16)} `, + fontSize: size.xs, + '& .material-icon, .material-icons-round, svg': { + fontSize: `${pxToRem(12)} !important`, + }, + }, + + medium: { + minHeight: pxToRem(40), + padding: `${pxToRem(10)} ${pxToRem(22)}`, + fontSize: size.sm, + '& .material-icon, .material-icons-round, svg': { + fontSize: `${pxToRem(22)} !important`, + }, + }, + + large: { + minHeight: pxToRem(47), + padding: `${pxToRem(16)} ${pxToRem(36)}`, + fontSize: size.md, + + '& .material-icon, .material-icons-round, svg': { + fontSize: `${pxToRem(26)} !important`, + }, + }, + + primary: { + backgroundColor: grey.default, + + '&:hover': { + backgroundColor: grey.dark, + }, + + '&:focus:not(:hover)': { + backgroundColor: grey.light, + }, + }, + + secondary: { + backgroundColor: grey.light, + + '&:hover': { + backgroundColor: grey.light, + }, + + '&:focus:not(:hover)': { + backgroundColor: grey.lightest, + }, + }, +}; diff --git a/src/assets/themes/components/buttons/index.jsx b/src/assets/themes/components/buttons/index.jsx index 1e927d9..e0270ed 100644 --- a/src/assets/themes/components/buttons/index.jsx +++ b/src/assets/themes/components/buttons/index.jsx @@ -2,6 +2,7 @@ import root from './root'; import contained from './contained'; import outlined from './outlined'; import buttonText from './text'; +import holo from './holo'; export default { defaultProps: { @@ -19,6 +20,12 @@ export default { outlinedSizeLarge: { ...outlined.large }, outlinedPrimary: { ...outlined.primary }, outlinedSecondary: { ...outlined.secondary }, + holo: { ...holo.base }, + holoSizeSmall: { ...holo.small }, + holoSizeMedium: { ...holo.medium }, + holoSizeLarge: { ...holo.large }, + holoPrimary: { ...holo.primary }, + holoSecondary: { ...holo.secondary }, text: { ...buttonText.base }, textSizeSmall: { ...buttonText.small }, textSizeLarge: { ...buttonText.large }, diff --git a/src/assets/themes/components/buttons/outlined.jsx b/src/assets/themes/components/buttons/outlined.jsx index 9009fe2..955498e 100644 --- a/src/assets/themes/components/buttons/outlined.jsx +++ b/src/assets/themes/components/buttons/outlined.jsx @@ -2,7 +2,7 @@ import colors from '../../base/colors'; import typography from '../../base/typography'; import pxToRem from '../../functions/pxToRem'; -const { transparent, light, info, secondary, backgroundE } = colors; +const { transparent, light, info, secondary, greenAccent } = colors; const { size } = typography; export default { @@ -52,11 +52,11 @@ export default { }, secondary: { - backgroundColor: backgroundE.light, + backgroundColor: greenAccent.lighterSeaGreen, borderColor: secondary.main, '&:hover': { - backgroundColor: backgroundE.light, + backgroundColor: greenAccent.lighterSeaGreen, }, }, }; diff --git a/src/assets/themes/components/card/index.jsx b/src/assets/themes/components/card/index.jsx index 97ce860..5354e20 100644 --- a/src/assets/themes/components/card/index.jsx +++ b/src/assets/themes/components/card/index.jsx @@ -3,7 +3,7 @@ import boxShadows from '../../base/boxShadows'; import colors from '../../base/colors'; import rgba from '../../functions/rgba'; -const { black, backgroundA } = colors; +const { black, greenAccent } = colors; const { borderWidth, borderRadius } = borders; const { md } = boxShadows; @@ -16,7 +16,7 @@ export default { minWidth: 0, wordWrap: 'break-word', backgroundImage: 'none', - backgroundColor: backgroundA.default, + backgroundColor: greenAccent.lighter, backgroundClip: 'border-box', border: `${borderWidth[0]} solid ${rgba(black.main, 0.125)}`, borderRadius: borderRadius.xl, diff --git a/src/assets/useSnackbarManager.jsx b/src/assets/useSnackbarManager.jsx new file mode 100644 index 0000000..66aeb19 --- /dev/null +++ b/src/assets/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/components/buttons/actionButtons/GenericActionButtons.jsx b/src/components/buttons/actionButtons/GenericActionButtons.jsx index 48e87ce..74dfbfa 100644 --- a/src/components/buttons/actionButtons/GenericActionButtons.jsx +++ b/src/components/buttons/actionButtons/GenericActionButtons.jsx @@ -2,15 +2,14 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { Box } from '@mui/material'; import MDTypography from '../../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; 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 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 '../../../layout/REUSABLE_COMPONENTS/icons/GlassyIcon'; import MDBox from '../../../layout/REUSABLE_COMPONENTS/MDBOX'; import useDeckManager from '../../../context/MAIN_CONTEXT/DeckContext/useDeckManager'; +import { useCartManager } from '../../../context/MAIN_CONTEXT/CartContext/useCartManager'; +import LoadingOverlay from '../../../layout/REUSABLE_COMPONENTS/LoadingOverlay'; const buttonSizeMap = { xs: 'extraSmall', @@ -30,10 +29,13 @@ const GenericActionButtons = ({ datatable = false, }) => { const { enqueueSnackbar } = useSnackbar(); // Add this line to use Notistack - const { addOneToCollection, removeOneFromCollection } = - useCollectionManager(); + const memoizedReturnValues = useCollectionManager(); // Add this line to use useCollectionManager + if (!memoizedReturnValues) return ; // Add this line to use useCollectionManager + // const { addOneToCollection, removeOneFromCollection } = + // useCollectionManager(); + const { addOneToCollection, removeOneFromCollection } = memoizedReturnValues; // Modify this line to use useCollectionManager const { addOneToDeck, removeOneFromDeck } = useDeckManager(); - const { addOneToCart, removeOneFromCart, cartData } = useCartStore(); + const { addOneToCart, removeOneFromCart } = useCartManager(); const [buttonSize, setButtonSize] = useState( buttonSizeMap[cardSize] || 'medium' ); diff --git a/src/components/buttons/other/ReusableLoadingButton.jsx b/src/components/buttons/other/ReusableLoadingButton.jsx index e2eac8e..c136a20 100644 --- a/src/components/buttons/other/ReusableLoadingButton.jsx +++ b/src/components/buttons/other/ReusableLoadingButton.jsx @@ -34,11 +34,11 @@ const ReusableLoadingButton = ({ }; } return { - background: theme.palette.backgroundG.light, - borderColor: theme.palette.backgroundG.light, + background: theme.palette.greenAccent.emerald, + borderColor: theme.palette.greenAccent.emerald, borderWidth: 2, - '&:hover': { background: theme.palette.backgroundG.default }, - '&:focus': { background: theme.palette.backgroundG.default }, + '&:hover': { background: theme.palette.greenAccent.lightSeaGreen }, + '&:focus': { background: theme.palette.greenAccent.lightSeaGreen }, }; }; diff --git a/src/components/cards/CardToolTip.jsx b/src/components/cards/CardToolTip.jsx index ae676fc..8066bfa 100644 --- a/src/components/cards/CardToolTip.jsx +++ b/src/components/cards/CardToolTip.jsx @@ -10,7 +10,7 @@ export const StyledToolTipBox = styled(Box)(({ theme }) => ({ border: `1px solid ${theme.palette.divider}`, borderRadius: theme.shape.borderRadius, padding: theme.spacing(2), - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, color: theme.palette.text.primary, boxShadow: theme.shadows[3], alignContent: 'flex-start', @@ -112,8 +112,8 @@ const createTooltip = (card, theme) => { > {Object.entries(cardAttributes).map(([key, value]) => ( - - + + {formatKey(key)}:{' '} {value} @@ -140,85 +140,6 @@ const CardToolTip = ({ card }) => { ); }; -// const SingleCardToolTip = ({ card }) => { -// // if (Array.isArray(card)) { -// // return card.map((card) => ); -// // } -// const { theme } = useMode(); -// const { -// name, -// desc, -// attributes, -// set_code, -// rarity, -// set_price, -// id, -// tcgplayer_price, -// } = card; - -// const price = set_price?.value || 'N/A'; -// const tcgPrice = tcgplayer_price?.value || 'N/A'; -// return ( -// -// -// img': { -// position: 'absolute', -// top: 0, -// left: 0, -// w: '100%', -// h: '100%', -// objectFit: 'cover', -// }, -// }} -// > -// {attributes && -// Object.entries(attributes).map(([key, value]) => ( -// -// {formatKey(key)}: {value} -// -// ))} -// {desc && ( -// -// Description: {desc} -// -// )} -// -// -// -// ); -// }; -// const CardToolTip = ({ card }) => { -// if (Array.isArray(card)) { -// return ( -//
-// {card.map((singleCard, index) => ( -// -// ))} -//
-// ); -// } -// return ; -// }; CardToolTip.propTypes = { card: PropTypes.oneOfType([ PropTypes.arrayOf( diff --git a/src/components/cards/GenericCard.jsx b/src/components/cards/GenericCard.jsx index d1bfed6..de359d6 100644 --- a/src/components/cards/GenericCard.jsx +++ b/src/components/cards/GenericCard.jsx @@ -10,30 +10,55 @@ import CardMediaSection from './CardMediaSection'; import GenericActionButtons from '../buttons/actionButtons/GenericActionButtons'; import placeholder from '../../assets/images/placeholder.jpeg'; import { useModalContext } from '../../context/UTILITIES_CONTEXT/ModalContext/ModalContext'; -import { PopoverContext } from '../../assets/currentlyUnused/PopoverContext/PopoverContext'; -import { Box } from '@mui/system'; -import { useCartStore } from '../../context/MAIN_CONTEXT/CartContext/CartContext'; -import { useCollectionStore } from '../../context/MAIN_CONTEXT/CollectionContext/CollectionContext'; -import { useDeckStore } from '../../context/MAIN_CONTEXT/DeckContext/DeckContext'; -import { useTheme } from 'styled-components'; -import { getQuantity } from '../componentHelpers'; import { useMode } from '../../context'; -import { useAppContext } from '../../context'; import MDTypography from '../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; -import { enqueueSnackbar } from 'notistack'; +import { useSnackbar } from 'notistack'; import useSelectedContext from '../../context/hooks/useSelectedContext'; -import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; import { AspectRatioBox, StyledCard, StyledCardContent, } from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; import { usePopover } from '../../context/hooks/usePopover'; +import { useCartManager } from '../../context/MAIN_CONTEXT/CartContext/useCartManager'; +import LoadingOverlay from '../../layout/REUSABLE_COMPONENTS/LoadingOverlay'; +import { useCompileCardData } from '../../context/MISC_CONTEXT/AppContext/useCompileCardData'; +import useSelectedDeck from '../../context/MAIN_CONTEXT/DeckContext/useSelectedDeck'; +const getQuantity = ({ + card, + cart, + selectedCollection, + allCollections, + selectedDeck, + allDecks, +}) => { + const findCardQuantity = (collectionsOrDecks, cardId) => + collectionsOrDecks?.reduce( + (acc, item) => + acc + (item?.cards?.find((c) => c.id === cardId)?.quantity ?? 0), + 0 + ) ?? 0; + + const cartQuantity = + cart?.items?.find((c) => c.id === card.id)?.quantity ?? 0; + const collectionQuantity = selectedCollection + ? selectedCollection?.cards?.find((c) => c.id === card.id)?.quantity ?? 0 + : findCardQuantity(allCollections, card.id); + const deckQuantity = selectedDeck + ? selectedDeck?.cards?.find((c) => c.id === card.id)?.quantity ?? 0 + : findCardQuantity(allDecks, card.id); + + return { + cartQuantity, + collectionQuantity, + deckQuantity, + }; +}; + 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' @@ -53,16 +78,18 @@ const GenericCard = React.forwardRef((props, ref) => { window.removeEventListener('resize', measureCard); }; }, []); - const { cartData } = useCartStore(); - const { selectedCollection, allCollections } = useSelectedCollection(); - const { selectedDeck, allDecks } = useDeckStore(); + const { cart } = useCartManager(); + const collectiondata = useSelectedContext(); + if (!collectiondata) return ; + const { selectedCollection, allCollections } = collectiondata; + const { selectedDeck, allDecks } = useSelectedDeck(); const { setContext, setIsContextSelected } = useSelectedContext(); - const { isCardInContext } = useAppContext(); + const { isCardInContext } = useCompileCardData(); const { openModalWithCard, setModalOpen, setClickedCard, isModalOpen } = useModalContext(); - // const { setHoveredCard, setIsPopoverOpen, hoveredCard } = - // useContext(PopoverContext); const { setHoveredCard, setIsPopoverOpen, hoveredCard } = usePopover(); + const { enqueueSnackbar } = useSnackbar(); // Assuming useOverlay has enqueueSnackbar method + const handleClick = useCallback(() => { openModalWithCard(card); setModalOpen(true); @@ -96,7 +123,7 @@ const GenericCard = React.forwardRef((props, ref) => { }`; const { cartQuantity, collectionQuantity, deckQuantity } = getQuantity({ card: card, - cartData: cartData, + cart: cart, selectedCollection: selectedCollection, allCollections: allCollections, selectedDeck: selectedDeck, diff --git a/src/components/componentHelpers.jsx b/src/components/componentHelpers.jsx deleted file mode 100644 index cf5f519..0000000 --- a/src/components/componentHelpers.jsx +++ /dev/null @@ -1,30 +0,0 @@ -export const getQuantity = ({ - card, - cartData, - selectedCollection, - allCollections, - selectedDeck, - allDecks, -}) => { - const findCardQuantity = (collectionsOrDecks, cardId) => - collectionsOrDecks?.reduce( - (acc, item) => - acc + (item?.cards?.find((c) => c.id === cardId)?.quantity ?? 0), - 0 - ) ?? 0; - - const cartQuantity = - cartData?.cart?.find((c) => c.id === card.id)?.quantity ?? 0; - const collectionQuantity = selectedCollection - ? selectedCollection?.cards?.find((c) => c.id === card.id)?.quantity ?? 0 - : findCardQuantity(allCollections, card.id); - const deckQuantity = selectedDeck - ? selectedDeck?.cards?.find((c) => c.id === card.id)?.quantity ?? 0 - : findCardQuantity(allDecks, card.id); - - return { - cartQuantity, - collectionQuantity, - deckQuantity, - }; -}; diff --git a/src/components/dialogs/CollectionDialog.jsx b/src/components/dialogs/CollectionDialog.jsx index a506a4b..d838d72 100644 --- a/src/components/dialogs/CollectionDialog.jsx +++ b/src/components/dialogs/CollectionDialog.jsx @@ -2,7 +2,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Avatar, 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 LockOutlinedIcon from '@mui/icons-material/LockOutlined'; import MDTypography from '../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; @@ -11,11 +10,16 @@ import { StyledDialog, StyledDialogContent, } from '../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; -// import MDAvatar from '../../layout/REUSABLE_COMPONENTS/MDAVATAR'; +import { formFields } from '../forms/formsConfig'; +import { useFormManagement } from '../forms/hooks/useFormManagement'; +import RCDynamicForm from '../forms/Factory/RCDynamicForm'; const CollectionDialog = ({ open, onClose, isNew, collectionData }) => { const { theme } = useMode(); const actionType = isNew ? 'add' : 'update'; + const { currentSchemaKey, setActiveFormSchema } = useFormManagement( + actionType === 'add' ? 'addCollectionForm' : 'updateCollectionForm' + ); return ( { border: 'none', }} > - + @@ -63,10 +67,19 @@ const CollectionDialog = ({ open, onClose, isNew, collectionData }) => { - + {/* + /> */} ); diff --git a/src/components/dialogs/DeckDialog.jsx b/src/components/dialogs/DeckDialog.jsx index 20308f0..6e65d74 100644 --- a/src/components/dialogs/DeckDialog.jsx +++ b/src/components/dialogs/DeckDialog.jsx @@ -51,7 +51,7 @@ const DeckDialog = ({ open, onClose, isNew, deckData }) => { border: 'none', }} > - + diff --git a/src/components/dialogs/GenericCardDialog.jsx b/src/components/dialogs/GenericCardDialog.jsx index f81ed4d..c3f110d 100644 --- a/src/components/dialogs/GenericCardDialog.jsx +++ b/src/components/dialogs/GenericCardDialog.jsx @@ -37,8 +37,6 @@ const GenericCardDialog = ({ const { closeModal } = useModalContext(); const { enqueueSnackbar } = useSnackbar(); // Assuming useOverlay has enqueueSnackbar method - const [imageUrl, setImageUrl] = useState(card?.card_images[0]?.image_url); - const handleAction = useCallback( (message, variant) => { enqueueSnackbar(message, { variant }); @@ -102,7 +100,7 @@ const GenericCardDialog = ({ - + ; @@ -24,8 +22,16 @@ const Transition = React.forwardRef(function Transition(props, ref) { function SelectionErrorDialog(props) { const { onClose, selectedValue, open } = props; const { allCollections } = useSelectedCollection(); - const { showSuccess, showError, showInfo } = useSnackbarManager(); // Using custom snackbar hook - + const { enqueueSnackbar } = useSnackbar(); + const showNotification = (message, variant) => { + enqueueSnackbar(message, { + variant: variant, + anchorOrigin: { + vertical: 'top', + horizontal: 'right', + }, + }); + }; const handleClose = () => { onClose(selectedValue); }; @@ -33,13 +39,13 @@ function SelectionErrorDialog(props) { const handleListItemClick = React.useCallback( (collection) => { if (collection._id) { - showInfo('Not implemented yet'); // Show an informational snackbar + showNotification('Not implemented yet', 'info'); } else { - showSuccess(`${collection} selected as backup account`); // Show a success snackbar + showNotification(`${collection} selected as backup account`, 'success'); } onClose(collection); }, - [onClose, showInfo, showSuccess] + [onClose, showNotification] ); return ( diff --git a/src/components/forms/AuthForm.jsx b/src/components/forms/AuthForm.jsx index b089af4..482fedf 100644 --- a/src/components/forms/AuthForm.jsx +++ b/src/components/forms/AuthForm.jsx @@ -47,7 +47,6 @@ const signupFields = [ ]; const AuthForm = ({ formType }) => { - // Combine forms to make signup forms const combinedFields = [...signupFields, ...loginFields]; const isSignup = formType === 'signupForm'; const fields = isSignup ? combinedFields : loginFields; diff --git a/src/components/forms/CollectionForm.jsx b/src/components/forms/CollectionForm.jsx index bd371cf..6581da6 100644 --- a/src/components/forms/CollectionForm.jsx +++ b/src/components/forms/CollectionForm.jsx @@ -1,26 +1,38 @@ import React, { useEffect } from 'react'; import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; import { useFormContext, useMode } from '../../context'; -import useSnackbarManager from '../../context/hooks/useSnackbarManager'; +import useSnackbarManager from '../../assets/useSnackbarManager'; import RCZodForm from './reusable/RCZodForm'; // Common fields structure used in both add and update forms const collectionFields = [ { - name: 'name', label: 'Name', type: 'text', + placeHolder: 'Enter collection name', + defaultValue: '', + rules: { + required: true, + }, value: '', required: true, + name: 'name', }, { - name: 'description', label: 'Description', type: 'text', + placeHolder: 'Enter collection description', + defaultValue: '', + rules: { + required: true, + multiline: true, + rows: 4, + }, value: '', required: true, multiline: true, rows: 4, + name: 'description', }, ]; @@ -33,7 +45,7 @@ const CollectionForm = ({ collectionData, actionType }) => { actionType === 'add' ? 'Create Collection' : 'Update Collection'; const startIcon = actionType === 'add' ? : ; - console.log('COLLECTION DATA', collectionData); + // console.log('COLLECTION DATA', collectionData); return ( { 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]); @@ -100,19 +88,17 @@ const DeckForm = ({ actionType, deckData }) => { React.useEffect(() => { if (!isUpdateMode) { - setFormSchema(formId); + setFormSchema('addDeckForm'); } - }, [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); + if (deckData) { + console.log('deckData:', deckData); + formMethods.reset({ + ...deckData, + tags: deckData?.tags?.join(', '), + color: deckData?.color || 'red', + }); } - }; + }, [setFormSchema, formId, isUpdateMode]); const handleDelete = () => { if (isUpdateMode) { diff --git a/src/components/forms/Factory/RCDynamicForm.jsx b/src/components/forms/Factory/RCDynamicForm.jsx new file mode 100644 index 0000000..5502560 --- /dev/null +++ b/src/components/forms/Factory/RCDynamicForm.jsx @@ -0,0 +1,106 @@ +import { FormLabel, Box, InputAdornment } from '@mui/material'; +import { useMediaQuery } from '@mui/material'; +import { RCFieldError } from './RCFieldError'; +import RCInput from './RCInput'; +import { Controller } from 'react-hook-form'; +import useRCFormHook from './useRCFormHook'; +import ReusableLoadingButton from '../../buttons/other/ReusableLoadingButton'; +import { + FormBox, + FormFieldBox, +} from '../../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; +import { useMode } from '../../../context'; +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; +import { zodSchemas, getFormFieldHandlers } from '../formsConfig'; +import { useFormSubmission } from '../hooks/useFormSubmission'; + +const RCDynamicForm = ({ formKey, inputs, options, initialData }) => { + const { theme } = useMode(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + const { + control, + handleSubmit, + formState: { errors, isSubmitting }, + } = useRCFormHook(formKey, zodSchemas, initialData); + const { onSubmit } = useFormSubmission(getFormFieldHandlers(), formKey); + return ( + + {Object.entries(inputs).map(([fieldName, fieldProps]) => ( + + {/*
*/} + {/* {fieldProps.label} */} + ( + + {fieldProps.icon} + + ), + } + : null + } + /> + )} + /> + {errors[fieldName] && ( + {errors[fieldName].message} + )} + {/*
*/} +
+ ))} + + {} + + } + fullWidth + sx={{ + ...(isMobile && { + fontSize: '0.75rem', // Adjust button font size for mobile + }), + }} + /> + + {options && + options?.map((button, index) => ( + + ))} +
+ ); +}; + +export default RCDynamicForm; diff --git a/src/components/forms/Factory/RCFieldError.jsx b/src/components/forms/Factory/RCFieldError.jsx new file mode 100644 index 0000000..349b5ee --- /dev/null +++ b/src/components/forms/Factory/RCFieldError.jsx @@ -0,0 +1,16 @@ +import { useFormContext } from 'react-hook-form'; +import Typography from '@mui/material/Typography'; + +export function RCFieldError({ name }) { + const { + formState: { errors }, + } = useFormContext(); + + if (!name) return null; + + const error = errors[name]; + + if (!error) return null; + + return {error.message}; +} diff --git a/src/components/forms/Factory/RCInput.jsx b/src/components/forms/Factory/RCInput.jsx new file mode 100644 index 0000000..6623d55 --- /dev/null +++ b/src/components/forms/Factory/RCInput.jsx @@ -0,0 +1,277 @@ +import React from 'react'; +import { AttachFile as AttachFileIcon } from '@mui/icons-material'; +import { + TextField, + Radio, + RadioGroup, + FormControlLabel, + FormControl, + FormLabel, + Checkbox, + Select, + MenuItem, + InputLabel, + FormGroup, + Slider, + Switch, + Input, + Autocomplete, + InputAdornment, + Chip, + Box, + OutlinedInput, + useMediaQuery, +} from '@mui/material'; +import { + FormFieldBox, + StyledTextField, +} from '../../../layout/REUSABLE_STYLED_COMPONENTS/ReusableStyledComponents'; +import { useMode } from '../../../context'; +import RCSwitch from '../reusable/RCSwitch'; + +const RCInput = ({ value, onChange, initialValue, type, options, ...rest }) => { + const { theme } = useMode(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + const handleDeleteChip = (chipToDelete) => { + const newValue = value.filter((chip) => chip !== chipToDelete); + onChange(newValue); // Notify react-hook-form of the change + }; + switch (type) { + case 'text': + case 'number': + case 'email': + case 'date': + case 'time': + case 'search': + case 'password': + return ( + onChange(e.target.value)} + value={value} + InputLabelProps={{ + shrink: !initialValue ? undefined : true, + }} + fontSize={isMobile ? '1rem' : '1.25rem'} + // error={!!errors[name]} + // helperText={errors[name]?.message} + {...rest} + /> + ); + + case 'multiline': + return ( + onChange(e.target.value)} + {...rest} + /> + ); + case 'select': + return ( + + + {rest.label} + + + + ); + case 'chips': + return ( + + + + ( + handleDeleteChip(chip)} + /> + )), + onChange: (e) => onChange(e.target.value), + // onBlur, + // onChange: event => { + // handleInputChange(event); + // onChange(event); + // }, + // onFocus + }} + variant="outlined" + margin="normal" + fullWidth + placeholder={rest?.placeholder} + value={value} + onChange={(e) => onChange(e.target.value)} + InputLabelProps={{ + shrink: !initialValue ? undefined : true, + }} + // error={!!errors[name]} + // helperText={errors[name]?.message} + {...rest} + /> + + + + ); + case 'checkbox': + return ( + + onChange(e.target.checked)} + {...rest} + /> + } + label={rest?.checkboxLabel} + /> + + ); + + case 'file': + return ( + + ); + case 'radio': + return ( + + {rest?.label} + onChange(e.target.value)} + row={rest?.row} + > + {rest?.options.map((option) => ( + } + label={option} + /> + ))} + + + ); + case 'dropdown': + return ( + + {rest?.label} + + + ); + case 'slider': + return ( + onChange(newValue)} + {...rest} + /> + ); + // case 'switch': + // return ( + // onChange(e.target.checked)} + // {...rest} + // /> + // } + // label={rest?.label} + // /> + // ); + case 'switch': + return ( + onChange(e.target.checked)} + labelLeft={rest.labelLeft} + labelRight={rest.labelRight} + formTitle={rest.formTitle} + iconLeft={rest.iconLeft} + iconRight={rest.iconRight} + size={rest.size} + {...rest} + /> + ); + case 'autocomplete': + return ( + { + onChange(newValue); + }} + options={options} + getOptionLabel={(option) => option.label || option} + renderInput={(params) => ( + + )} + {...rest} + /> + ); + default: + return null; + } +}; + +export default RCInput; diff --git a/src/components/forms/Factory/useRCFormHook.jsx b/src/components/forms/Factory/useRCFormHook.jsx new file mode 100644 index 0000000..4881b91 --- /dev/null +++ b/src/components/forms/Factory/useRCFormHook.jsx @@ -0,0 +1,13 @@ +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; + +const useRCFormHook = (schemaKey, schemas, defaultValues = {}) => { + const schema = schemas[schemaKey]; + const methods = useForm({ + resolver: schema ? zodResolver(schema) : undefined, + defaultValues, // Use provided default values to initialize the form + }); + + return methods; +}; +export default useRCFormHook; diff --git a/src/components/forms/OptionsComponent.jsx b/src/components/forms/OptionsComponent.jsx index 662faef..857949b 100644 --- a/src/components/forms/OptionsComponent.jsx +++ b/src/components/forms/OptionsComponent.jsx @@ -23,15 +23,33 @@ const OptionsComponent = ({ const schemaName = 'collectionSearchForm'; const buttonLabel = 'Search'; const startIcon = ; - const collectionSearchFields = [ - { - name: 'searchTerm', - label: 'Search', - type: 'text', + const collectionSearchFields = { + label: 'Search', + type: 'text', + placeholder: 'Search for cards...', + defaultValue: '', + rules: { required: true, - value: search, }, - ]; + startIcon: , + required: true, + value: search, + buttonLabel: 'Search', + }; + // const collectionSearchFields = { + // searchTerm: { + // label: 'Search', + // type: 'text', + // placeholder: 'Search for cards...', + // defaultValue: '', + // rules: { + // required: true, + // }, + // icon: , + // required: true, + // value: search, + // }, + // }; const { formMethods, onSubmit, @@ -62,22 +80,22 @@ const OptionsComponent = ({ color: theme.palette.text.primary, '& .MuiTextField-root': { '& fieldset': { - borderColor: theme.palette.primary.main, // subtle accent on text field borders + borderColor: theme.palette.success.secondary, // subtle accent on text field borders }, '&:hover fieldset': { - borderColor: theme.palette.primary.dark, // darker on hover for emphasis + borderColor: theme.palette.grey.black, // darker on hover for emphasis }, '& .Mui-focused fieldset': { - borderColor: theme.palette.primary.main, // maintain accent on focus + borderColor: theme.palette.success.secondary, // maintain accent on focus }, }, '& .MuiAutocomplete-root': { '& .Mui-focused .MuiOutlinedInput-notchedOutline': { - borderColor: theme.palette.primary.main, // accent on autocomplete focus + borderColor: theme.palette.success.secondary, // accent on autocomplete focus }, }, '& .MuiSvgIcon-root': { - color: theme.palette.primary.main, // icon color + color: theme.palette.success.secondary, // icon color }, }} > @@ -106,7 +124,7 @@ const OptionsComponent = ({ color: theme.palette.text.primary, // input text color }, '.MuiInputLabel-root': { - color: theme.palette.primary.light, // label color for a subtle accent + color: theme.palette.grey.blueGrey, // label color for a subtle accent }, }} /> @@ -126,7 +144,7 @@ const OptionsComponent = ({ label="Rows per page" variant="outlined" InputLabelProps={{ - style: { color: theme.palette.primary.light }, // subtle accent on the label + style: { color: theme.palette.grey.blueGrey }, // subtle accent on the label }} /> )} diff --git a/src/components/forms/SearchForm.jsx b/src/components/forms/SearchForm.jsx index 445eecb..1b983a5 100644 --- a/src/components/forms/SearchForm.jsx +++ b/src/components/forms/SearchForm.jsx @@ -10,14 +10,19 @@ const SearchForm = () => { const searchFields = [ { - name: 'searchTerm', - label: 'Search for cards', + label: 'Search Cards', type: 'text', + placeholder: 'Search for cards...', + defaultValue: '', + rules: { + required: false, + }, required: false, value: forms?.searchForm?.searchTerm || '', onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, + name: 'searchTerm', }, ]; return ( diff --git a/src/components/forms/customerCheckoutForm/CustomerForm.js b/src/components/forms/customerCheckoutForm/CustomerForm.js index 1a75031..c064d1b 100644 --- a/src/components/forms/customerCheckoutForm/CustomerForm.js +++ b/src/components/forms/customerCheckoutForm/CustomerForm.js @@ -1,16 +1,15 @@ import React, { useCallback, useContext } from 'react'; import { Box, Container, Typography, Grid, Button } from '@mui/material'; -import { useCartStore } from '../../../context/CartContext/CartContext'; import CustomerInfoFields from './CustomerInfoFields'; import StripeCheckoutModal from '../../dialogs/stripeModal/StripeCheckoutModal'; import { ModalContext } from '../../../context/ModalContext/ModalContext'; import CartSummary from '../../other/dataDisplay/CartSummary'; -import OrderSubmitButton from '../../buttons/other/OrderSubmitButton'; import { useMode } from '../../../context'; +import { useCartManager } from '../../../context/MAIN_CONTEXT/CartContext/useCartManager'; const CustomerForm = () => { const { isModalOpen, setModalOpen } = useContext(ModalContext); - const { cartData, cartCardQuantity, totalCost } = useCartStore(); + const { cart, cartCardQuantity, totalCost } = useCartManager(); const { theme } = useMode(); const handleModalToggle = useCallback( @@ -64,10 +63,10 @@ const CustomerForm = () => { sx={{ mt: 1, mb: 1, - border: `1px solid ${theme.palette.backgroundA.darker}`, - backgroundColor: theme.palette.backgroundA.light, + border: `1px solid ${theme.palette.greenAccent.default}`, + backgroundColor: theme.palette.greenAccent.lightest, '&:hover': { - backgroundColor: theme.palette.backgroundA.darker, + backgroundColor: theme.palette.greenAccent.default, }, boxShadow: '0px 5px 15px rgba(0, 0, 0, 0.1)', }} @@ -82,7 +81,7 @@ const CustomerForm = () => { diff --git a/src/components/forms/formsConfig.jsx b/src/components/forms/formsConfig.jsx index 0038a3d..6dee257 100644 --- a/src/components/forms/formsConfig.jsx +++ b/src/components/forms/formsConfig.jsx @@ -1,71 +1,508 @@ 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 +import SaveRoundedIcon from '@mui/icons-material/SaveRounded'; +import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded'; +import EditRoundedIcon from '@mui/icons-material/EditRounded'; +import AddRoundedIcon from '@mui/icons-material/AddRounded'; +import RemoveRoundedIcon from '@mui/icons-material/RemoveRounded'; +import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; +import ArrowForwardRoundedIcon from '@mui/icons-material/ArrowForwardRounded'; +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; +import SearchRoundedIcon from '@mui/icons-material/SearchRounded'; +// For additionButtons (which use non-rounded versions): +import DeleteIcon from '@mui/icons-material/Delete'; +import SaveIcon from '@mui/icons-material/Save'; +import EditIcon from '@mui/icons-material/Edit'; +import AddIcon from '@mui/icons-material/Add'; -const auth = [ - { - name: 'firstName', +import { z } from 'zod'; +import useAuthManager from '../../context/MAIN_CONTEXT/AuthContext/useAuthManager'; +import useCollectionManager from '../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; +import useDeckManager from '../../context/MAIN_CONTEXT/DeckContext/useDeckManager'; +import { useCardStoreHook } from '../../context/MAIN_CONTEXT/CardContext/useCardStore'; +import LoadingOverlay from '../../layout/REUSABLE_COMPONENTS/LoadingOverlay'; +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ---------------------------- FORM FIELD HANDLERS ---------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +const getFormFieldHandlers = () => { + const { signup, login } = useAuthManager(); + const { handleRequest, setSearchSettings, searchSettings } = + useCardStoreHook(); + const collectionmanagedata = useCollectionManager(); + if (!collectionmanagedata) { + return ; + } + const { createNewCollection, updateCollection } = collectionmanagedata; + const { updateDeckDetails, deleteDeck, createNewDeck } = useDeckManager(); + // const { setTimeRange } = useTimeRange(); + + const formHandlers = { + loginForm: (formData) => { + console.log('Login Form Data:', formData); + login(formData); + }, + signupForm: (formData) => { + console.log('Signup Form Data:', formData); + signup(formData); + }, + updateUserDataForm: (formData) => { + console.log('Update User Data Form Data:', formData); + }, + addCollectionForm: (formData, additionalData) => { + console.log('Add Collection Form Data:', formData, additionalData); + createNewCollection(formData, additionalData); + }, + updateCollectionForm: (formData, additionalData) => { + console.log('Update Collection Form Data:', formData, additionalData); + updateCollection(additionalData, formData); + }, + updateDeckForm: (formData, additionalData) => { + console.log('Update Deck Form Data:', formData, additionalData); + updateDeckDetails(formData, additionalData); + }, + addDeckForm: (formData, additionalData) => { + console.log('Add Deck Form Data:', formData, additionalData); + createNewDeck(formData, additionalData); + }, + deleteDeckForm: (formData, additionalData) => { + console.log('Delete Deck Form Data:', formData, additionalData); + deleteDeck(formData, additionalData); + }, + searchForm: (formData, additionalData) => { + console.log('Search Form Data:', formData, additionalData); + handleRequest(formData, additionalData); + }, + collectionSearchForm: (formData, additionalData) => { + console.log('Collection Search Form Data:', formData, additionalData); + }, + // timeRangeSelector: (formData, additionalData) => { + // console.log('Time Range Selector Form Data:', formData, additionalData); + // setTimeRange(formData, additionalData); + // }, + searchSettingsSelector: (formData, additionalData) => { + console.log( + 'Search Settings Selector Form Data:', + formData, + additionalData + ); + 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); + // toggleActiveForm('loginForm', 'signupForm'); + }, + }; + return formHandlers; +}; +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ---------------------------- FORM FIELD VALUES ------------------------------ +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +const loginFormFields = { + username: { + label: 'Username', + type: 'text', + placeHolder: 'Enter username', + defaultValue: '', + rules: { + required: true, + }, + icon: , + field: 'username', + }, + password: { + label: 'Password', + type: 'text', + placeHolder: 'Enter password', + defaultValue: '', + rules: { + required: true, + }, + icon: , + field: 'password', + }, +}; +const signupFormFields = { + firstName: { label: 'First Name', type: 'text', + placeHolder: 'Enter first name', + defaultValue: '', + rules: { + required: true, + }, icon: , + field: 'firstName', }, - { - name: 'lastName', + lastName: { label: 'Last Name', type: 'text', + placeHolder: 'Enter last name', + defaultValue: '', + rules: { + required: true, + }, icon: , + field: 'lastName', }, - { name: 'email', label: 'Email', type: 'email', icon: }, - { - name: 'username', - label: 'Username', + email: { + label: 'Email', + type: 'email', + placeHolder: 'Enter email', + defaultValue: '', + rules: { + required: true, + }, + icon: , + field: 'email', + }, + ...loginFormFields, +}; +const authFormFields = signupFormFields; +const addDeckFormFields = { + name: { + label: 'Name', type: 'text', - icon: , + placeHolder: 'Enter deck name', + defaultValue: '', + rules: { + required: true, + }, + icon: , + field: 'name', + required: true, }, - { - name: 'password', - label: 'Password', - type: 'password', - icon: , + description: { + label: 'Description', + type: 'text', + placeHolder: 'Enter deck description', + defaultValue: '', + rules: { + required: true, + multiline: true, + rows: 4, + }, + multiline: true, + rows: 4, + icon: , + required: false, + }, +}; +const updateDeckFormFields = { + tags: { + label: 'Tags', + type: 'chips', + placeHolder: 'Enter tags', + defaultValue: [], + values: [], + rules: { + required: true, + }, + // chipData: tags, // Assuming `tags` is defined elsewhere + icon: , + // onAddChip: handleAddTag, // Assuming `handleAddTag` is defined elsewhere + // onDeleteChip: handleDeleteTag, // Assuming `handleDeleteTag` is defined elsewhere + required: false, + // value: tags.join(', '), // Assuming `tags` is defined elsewhere + }, + color: { + label: 'Color', + type: 'select', + placeHolder: 'Select color', + defaultValue: 'green', + rules: { + required: true, + }, + 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 collection = [ - { - name: 'name', + ...addDeckFormFields, +}; +const deckFormFields = updateDeckFormFields; +const collectionFormFields = { + name: { label: 'Name', type: 'text', + placeHolder: 'Enter collection name', + defaultValue: '', + rules: { + required: true, + }, icon: , required: true, - multiline: false, }, - { - name: 'description', + description: { label: 'Description', type: 'text', + placeHolder: 'Enter collection description', + defaultValue: '', + rules: { + required: true, + multiline: true, + rows: 4, + }, 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, }; +const searchFormFields = { + searchTerm: { + label: 'Search Cards', + type: 'text', + placeholder: 'Search for cards...', + defaultValue: '', + rules: { + required: false, + }, + required: false, + value: '', + icon: , + // Assuming forms, handleChange, handleFocus, and handleBlur are defined elsewhere + // value: forms?.searchForm?.searchTerm || '', + // onChange: handleChange, + // onFocus: handleFocus, + // onBlur: handleBlur, + }, +}; +const collectionSearchFormFields = { + searchTerm: { + label: 'Search', + type: 'text', + placeholder: 'Search for cards...', + defaultValue: '', + rules: { + required: false, + }, + icon: , + required: false, + value: '', + }, +}; +const statRangeFormFields = { + highPoint: { label: 'High Point' }, + lowPoint: { label: 'Low Point' }, + twentyFourHourAverage: { label: '24 Hour Average' }, + average: { label: 'Average' }, + volume: { label: 'Volume' }, + volatility: { label: 'Volatility' }, +}; +const themeRangeFormFields = { + light: { label: 'Light Theme' }, + dark: { label: 'Dark Theme' }, + system: { label: 'System Theme' }, +}; +const timeRangeFormFields = { + '24hr': { label: 'Today' }, + '7d': { label: 'This Week' }, + '30d': { label: 'This Month' }, + '90d': { label: 'Last Three Months' }, + '180d': { label: 'Last Six Months' }, + '270d': { label: 'Last Nine Months' }, + '365d': { label: 'All Time' }, +}; +const authSwitchFormFields = { + authSwitch: { + label: 'Auth Switch', + type: 'switch', + defaultValue: false, + rules: { + required: true, + }, + required: true, + }, +}; +const formFields = { + loginForm: loginFormFields, + signupForm: signupFormFields, + authForm: authFormFields, + addDeckForm: addDeckFormFields, + updateDeckForm: updateDeckFormFields, + deckForm: deckFormFields, + addCollectionForm: collectionFormFields, + updateCollectionForm: collectionFormFields, + collectionForm: collectionFormFields, + searchForm: searchFormFields, + statRangeForm: statRangeFormFields, + themeRangeForm: themeRangeFormFields, + timeRangeForm: timeRangeFormFields, + authSwitchForm: authSwitchFormFields, + collectionSearchForm: collectionSearchFormFields, + // searchSettingsForm: searchSettingsFormFields, +}; +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// -------------------------- ZOD VALIDATION SCHEMAS --------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- -export default formData; +const loginSchema = z.object({ + username: z.string().min(1, 'Username is required'), + password: z.string().min(1, 'Password is required'), +}); +const signupSchema = z.object({ + firstName: z.string().min(1, 'First Name is required'), + lastName: z.string().min(1, 'Last Name is required'), + email: z.string().email('Invalid email format'), +}); +const authFormSchema = signupSchema.merge(loginSchema); +const addDeckSchema = z.object({ + name: z.string().min(1, 'Deck Name is required'), + description: z.string().min(1, 'Description is required').optional(), +}); +const updateDeckSchema = z.object({ + tags: z.array(z.string()).optional(), // Assuming tags can be optional + color: z.enum([ + 'red', + 'blue', + 'green', + 'yellow', + 'purple', + 'pink', + 'orange', + 'teal', + ]), +}); +const deckFormSchema = addDeckSchema.merge(updateDeckSchema); +const collectionFormSchema = z.object({ + name: z.string().min(1, 'Collection Name is required'), + description: z.string().min(1, 'Collection Description is required'), +}); +const searchFormSchema = z.object({ + searchTerm: z.string().optional(), +}); +const statisticsSchema = z.object({ + selectedStatistic: z + .enum([ + 'highPoint', + 'lowPoint', + 'twentyFourHourAverage', + 'average', + 'volume', + 'volatility', + ]) + .optional(), +}); +const themeFormSchema = z.object({ + theme: z.enum(['light', 'dark', 'system']).optional(), +}); +const timeRangeFormSchema = z.object({ + timeRange: z + .enum(['24hr', '7d', '30d', '90d', '180d', '270d', '365d']) + .optional(), +}); +const zodSchemas = { + loginSchema, + signupSchema, + authFormSchema, + addDeckSchema, + updateDeckSchema, + deckFormSchema, + collectionFormSchema, + searchFormSchema, + statisticsSchema, + themeFormSchema, + timeRangeFormSchema, +}; +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// -------------------------- ZOD VALIDATION FUNCTIONS ------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +const validationFunctions = { + login: (data) => loginSchema.safeParse(data), + signup: (data) => signupSchema.safeParse(data), + authForm: (data) => authFormSchema.safeParse(data), + addDeck: (data) => addDeckSchema.safeParse(data), + updateDeck: (data) => updateDeckSchema.safeParse(data), + deckForm: (data) => deckFormSchema.safeParse(data), + collectionForm: (data) => collectionFormSchema.safeParse(data), + searchForm: (data) => searchFormSchema.safeParse(data), + statistics: (data) => statisticsSchema.safeParse(data), + themeForm: (data) => themeFormSchema.safeParse(data), + timeRangeForm: (data) => timeRangeFormSchema.safeParse(data), +}; +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------- ADDITIONAL CONFIG OPTIONS --------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +const configOptions = { + startIcons: [ + { save: }, + { delete: }, + { edit: }, + { add: }, + { remove: }, + { back: }, + { next: }, + ], + labelOptions: [ + { label: 'Submit', value: 'submit' }, + { label: 'Delete', value: 'delete' }, + { label: 'Edit', value: 'edit' }, + { label: 'Add', value: 'add' }, + { label: 'Remove', value: 'remove' }, + { label: 'Back', value: 'back' }, + { label: 'Next', value: 'next' }, + ], + additionButtons: [ + { + label: 'Delete Deck', + onClick: console.log('Delete Deck'), + startIcon: , + color: 'error', + variant: 'holo', + }, + { + label: 'Save Deck', + onClick: console.log('Save Deck'), + startIcon: , + color: 'primary', + variant: 'holo', + }, + { + label: 'Edit', + onClick: console.log('Edit'), + startIcon: , + color: 'primary', + variant: 'holo', + }, + { + label: 'Add', + onClick: console.log('Add'), + startIcon: , + color: 'primary', + variant: 'holo', + }, + ], +}; +export { + formFields, + zodSchemas, + getFormFieldHandlers, + configOptions, + validationFunctions, +}; diff --git a/src/components/forms/hooks/useFormManagement.jsx b/src/components/forms/hooks/useFormManagement.jsx new file mode 100644 index 0000000..c3cfb7e --- /dev/null +++ b/src/components/forms/hooks/useFormManagement.jsx @@ -0,0 +1,20 @@ +import { useState, useCallback } from 'react'; + +export const useFormManagement = (initialFormKey) => { + const [activeForm, setActiveForm] = useState(initialFormKey); + + const setActiveFormSchema = useCallback( + (formKey) => setActiveForm(formKey), + [] + ); + + const toggleActiveForm = useCallback((formA, formB) => { + setActiveForm((prevForm) => (prevForm === formA ? formB : formA)); + }, []); + + return { + setActiveFormSchema, + toggleActiveForm, + currentSchemaKey: activeForm, + }; +}; diff --git a/src/components/forms/hooks/useFormSubmission.jsx b/src/components/forms/hooks/useFormSubmission.jsx new file mode 100644 index 0000000..560b0c7 --- /dev/null +++ b/src/components/forms/hooks/useFormSubmission.jsx @@ -0,0 +1,112 @@ +import { useCallback, useState } from 'react'; + +export const useFormSubmission = (formHandlers, activeForm) => { + const [isSubmitting, setIsSubmitting] = useState(false); + + const onSubmit = useCallback( + async (formData) => { + setIsSubmitting(true); + try { + const handler = formHandlers[activeForm]; + if (!handler) { + console.error(`No handler for form type: ${activeForm}`); + return; + } + await handler(formData); + } catch (error) { + console.error(`Error submitting form: ${activeForm}`, error); + } finally { + setIsSubmitting(false); + } + }, + [formHandlers, activeForm] + ); + + return { onSubmit, isSubmitting }; +}; + +// import { useCallback, useState, useEffect } from 'react'; +// import { useForm } from 'react-hook-form'; +// import { zodResolver } from '@hookform/resolvers/zod'; +// import { formSchemas, getDefaultValuesFromSchema } from './schemas'; // Adjust path as necessary +// import { useFormManagement } from './useFormManagement'; +// import { useRCFormHook } from '../Factory/useRCFormHook'; +// import useAuthManager from '../../../context/MAIN_CONTEXT/AuthContext/useAuthManager'; +// import useCollectionManager from '../../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; +// import useDeckManager from '../../../context/MAIN_CONTEXT/DeckContext/useDeckManager'; +// import { useCardStoreHook } from '../../../context/MAIN_CONTEXT/CardContext/useCardStore'; + +// export const useFormSubmission = () => { +// const { control, handleSubmit, reset } = useRCFormHook(); // Assuming this hooks properly sets up the form +// const { +// setActiveFormSchema, +// getActiveFormSchema, +// toggleActiveForm, +// getActiveFormDefaultValues, +// } = useFormManagement(); +// const { signup, login } = useAuthManager(); +// const { createNewCollection, updateCollection } = useCollectionManager(); +// const { updateDeckDetails, deleteDeck, createNewDeck } = useDeckManager(); +// const { setSearchSettings } = useCardStoreHook(); +// const [isSubmitting, setIsSubmitting] = useState(false); +// const formHandlers = { +// signupForm: async (formData) => signup(formData), +// loginForm: async (formData) => login(formData), +// updateUserDataForm: (formData) => console.log(formData), +// addCollectionForm: (formData, additionalData) => +// createNewCollection(formData, additionalData), +// updateCollectionForm: (formData, additionalData) => +// updateCollection(additionalData, 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, additionalData), +// searchSettingsSelector: (formData, additionalData) => +// 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); +// toggleActiveForm('loginForm', 'signupForm'); +// }, +// }; + +// const onSubmit = useCallback( +// async (formData) => { +// setIsSubmitting(true); +// const currentSchemaKey = getActiveFormSchema(); // Get the current active form's schema key +// try { +// const handler = formHandlers[currentSchemaKey]; +// if (!handler) { +// console.error(`No handler for form type: ${currentSchemaKey}`); +// return; +// } +// await handler(formData); +// } catch (error) { +// console.error(`Error submitting form: ${currentSchemaKey}`, error); +// } finally { +// setIsSubmitting(false); +// } +// }, +// // Ensure to include all dependencies that the callback uses +// [formHandlers, getActiveFormSchema] +// ); + +// useEffect(() => { +// const currentSchemaKey = getActiveFormSchema(); +// const defaultValues = getActiveFormDefaultValues(currentSchemaKey); // Assuming this function exists and fetches default values correctly +// reset(defaultValues); +// }, [getActiveFormSchema, reset]); + +// return { control, handleSubmit: handleSubmit(onSubmit), isSubmitting }; +// }; diff --git a/src/components/forms/hooks/useSubmitHandler.jsx b/src/components/forms/hooks/useSubmitHandler.jsx index 5b3287c..a68f72a 100644 --- a/src/components/forms/hooks/useSubmitHandler.jsx +++ b/src/components/forms/hooks/useSubmitHandler.jsx @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import useSnackbarManager from '../../../context/hooks/useSnackbarManager'; +import { useSnackbar } from 'notistack'; function useSubmitHandler( onSubmit, @@ -7,32 +7,45 @@ function useSubmitHandler( successDescription, errorDescription ) { - const { showSuccess, showError } = useSnackbarManager(); + const { enqueueSnackbar } = useSnackbar(); return useCallback( (data, formType) => { onSubmit(data, formType) .then(() => { - // Using showSuccess for positive feedback - showSuccess( - `${successTitle}: ${successDescription.replace('{timeRange}', data?.timeRange)}` + // Correctly formatted enqueueSnackbar call for success + enqueueSnackbar( + `${successTitle}: ${successDescription.replace('{timeRange}', data?.timeRange)}`, + { + variant: 'success', + anchorOrigin: { + vertical: 'top', + horizontal: 'right', + }, + } ); }) .catch((error) => { - // Using showError for negative feedback - showError( - `${errorDescription.replace('{timeRange}', data?.timeRange)}: ${error}` + // Correctly formatted enqueueSnackbar call for error + enqueueSnackbar( + `${errorDescription.replace('{timeRange}', data?.timeRange)}: ${error}`, + { + variant: 'error', + anchorOrigin: { + vertical: 'top', + horizontal: 'right', + }, + } ); }); }, [ onSubmit, - showSuccess, - showError, + enqueueSnackbar, successTitle, successDescription, errorDescription, - ] + ] // Removed showSuccess and showError from dependencies ); } diff --git a/src/components/forms/reusable/FormField.jsx b/src/components/forms/reusable/FormField.jsx index edce8e4..76980ff 100644 --- a/src/components/forms/reusable/FormField.jsx +++ b/src/components/forms/reusable/FormField.jsx @@ -12,7 +12,6 @@ const FormField = ({ ...props }) => { const { theme } = useMode(); - const showLabel = !initialValue; return ( ); }; diff --git a/src/components/forms/reusable/FormTextField.jsx b/src/components/forms/reusable/FormTextField.jsx deleted file mode 100644 index f3d2c2d..0000000 --- a/src/components/forms/reusable/FormTextField.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import { TextField } from '@mui/material'; -import React from 'react'; -import { Controller, useFormContext } from 'react-hook-form'; - -// Removed the TypeScript-specific type definitions and kept the structure of the component -function FormTextField({ control: providedControl, name, rules, ...rest }) { - const { control } = useFormContext(); - return ( - ( - - )} - /> - ); -} - -export default FormTextField; diff --git a/src/components/forms/reusable/RCSwitch.jsx b/src/components/forms/reusable/RCSwitch.jsx index 07e6624..2849ce9 100644 --- a/src/components/forms/reusable/RCSwitch.jsx +++ b/src/components/forms/reusable/RCSwitch.jsx @@ -1,9 +1,6 @@ import React, { useState } 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'; const RCSwitch = ({ checked, diff --git a/src/components/forms/reusable/RCZodForm.jsx b/src/components/forms/reusable/RCZodForm.jsx index bdefb7c..84e48a6 100644 --- a/src/components/forms/reusable/RCZodForm.jsx +++ b/src/components/forms/reusable/RCZodForm.jsx @@ -28,7 +28,6 @@ const RCZodForm = ({ }) => { const { theme } = useMode(); const isMobile = useMediaQuery(theme.breakpoints.down('sm')); - const { formMethods, onSubmit, @@ -39,16 +38,16 @@ const RCZodForm = ({ formState: { errors, isSubmitting }, } = useFormContext(); - const onFormSubmit = (data) => { - onSubmit(data, additionalData); - }; + // const onFormSubmit = (data) => { + // onSubmit(data, additionalData); + // }; useEffect(() => { if (initialValues) { Object.keys(initialValues).forEach((key) => { formMethods.setValue(key, initialValues[key]); }); } - }, [initialValues, formMethods.setValue]); + }, [initialValues]); const renderField = (field) => { const isSearchForm = schemaName === 'searchForm' && field.name === 'searchTerm'; @@ -64,11 +63,6 @@ const RCZodForm = ({ {...formMethods.register(field.name)} label={field.label} value={formMethods.getValues(field.name) || ''} // Manage value explicitly - // defaultValues={initialValues ? initialValues[field.name] : ''} - // value={initialValues ? initialValues[field.name] : ''} - // onChange={onChange} - // options={field.options} - // defaultValue={initialValues ? initialValues[field.name] : ''} sx={{ width: '100%', marginBottom: 2, @@ -154,7 +148,7 @@ const RCZodForm = ({ return ( {startIcon} @@ -179,6 +174,7 @@ const RCZodForm = ({ }), }} /> + {additionalButtons && additionalButtons?.map((button, index) => ( { const { theme } = useMode(); const isMobile = useMediaQuery(theme.breakpoints.down('sm')); @@ -80,7 +80,7 @@ const SearchComponent = (pageContext) => { align="left" sx={{ fontWeight: 'bold', - color: theme.palette.backgroundB.dark, + color: theme.palette.grey.simpleGrey, textTransform: 'uppercase', marginBottom: isMobile ? theme.spacing(2) : 0, // Add bottom margin on mobile }} @@ -113,14 +113,21 @@ const SearchComponent = (pageContext) => {
- + ; return ( - + {renderItems} - + ({ padding: theme.spacing(3), borderRadius: theme.shape.borderRadius, - background: theme.palette.backgroundA.dark, + background: theme.palette.greenAccent.light, boxShadow: theme.shadows[3], margin: 'auto', width: '100%', diff --git a/src/components/forms/selectors/CollectionStatisticsSelector.jsx b/src/components/forms/selectors/CollectionStatisticsSelector.jsx index 4bd22f8..fc686d7 100644 --- a/src/components/forms/selectors/CollectionStatisticsSelector.jsx +++ b/src/components/forms/selectors/CollectionStatisticsSelector.jsx @@ -4,12 +4,13 @@ import { FormControl, InputLabel } from '@mui/material'; import SelectComponent from '../reusable/Select'; import { useStatisticsStore, useMode, useFormContext } from '../../../context'; import { StyledChartBox } from '../../../pages/pageStyles/StyledComponents'; -import { enqueueSnackbar } from 'notistack'; +import { useSnackbar } from 'notistack'; import { Controller } from 'react-hook-form'; const CollectionStatisticsSelector = () => { const { formMethods } = useFormContext(); const [selectedStat, setSelectedStat] = React.useState(''); + const { enqueueSnackbar } = useSnackbar(); // Assuming useOverlay has enqueueSnackbar method const { theme } = useMode(); const { control, diff --git a/src/components/forms/selectors/useTimeRange.jsx b/src/components/forms/selectors/useTimeRange.jsx index 6f1b0ea..23ccc26 100644 --- a/src/components/forms/selectors/useTimeRange.jsx +++ b/src/components/forms/selectors/useTimeRange.jsx @@ -5,7 +5,11 @@ import useSubmitHandler from '../hooks/useSubmitHandler'; function useTimeRange() { const { formMethods, onSubmit } = useFormContext(); + const { selectedCollection } = useSelectedCollection(); + if (!selectedCollection) { + return console.error('No collection selected'); + } const averagedChartData = selectedCollection?.averagedChartData; const selectedTimeRange = formMethods.watch('timeRange', '24hr'); const timeRangeOptions = useMemo(() => { diff --git a/src/config.json b/src/config.json index 0d90ac9..d7bfb09 100644 --- a/src/config.json +++ b/src/config.json @@ -6,6 +6,7 @@ "sourceMap": true, "outDir": "dist", "paths": { + "*": ["src/*"], "@material-ui/core": ["../node_modules/@material-ui/core"], "@material-ui/icons": ["../node_modules/@material-ui/icons"], "@material-ui/lab": ["../node_modules/@material-ui/lab"], diff --git a/src/context/Helpers.jsx b/src/context/Helpers.jsx index 54072f0..3d54a29 100644 --- a/src/context/Helpers.jsx +++ b/src/context/Helpers.jsx @@ -1,68 +1,20 @@ -import { useCallback, useEffect, useState } from 'react'; -import { useCookies } from 'react-cookie'; - -export const BASE_URL = `${process.env.REACT_APP_SERVER}/api`; -export const createUrl = (path) => `${BASE_URL}/${path}`; /** * The base URL for all API calls. * @type {String} */ export const BASE_API_URL = `${process.env.REACT_APP_SERVER}/api/users`; - /** * Creates a full API URL from a given path. * @param {String} path - API path. * @returns {String} Full API URL. */ export const createApiUrl = (path) => `${BASE_API_URL}/${path}`; - /** - * Filters a collection of data points based on a time threshold. + * Calculates the total price of a collection based on the card prices and quantities. * - * @param {Array} collection - The data points collection. - * @param {number} threshold - Time threshold for filtering. - * @returns {Array} - Array with data points filtered by the time threshold. + * @param {Object} collection - The collection object with card information. + * @returns {number} The total price of the collection. */ -const filterCollectionByTimeRange = (collection, threshold) => { - return collection?.filter((point) => { - const pointTime = new Date(point.x).getTime(); - return pointTime >= threshold; - }); -}; -/** - * Filters data points based on a time range. - * - * @param {Array} data - Array of objects containing data points. - * @param {string} timeRange - Time range string for filtering data points. - * @returns {Array} - Array with data points filtered by the time range. - */ -export const filterDataByTimeRange = (data, timeRange) => { - if (!data || data.length === 0) return []; - - const now = new Date().getTime(); - console.log('TIME RANGE:', timeRange); - const timeThreshold = timeRange; - console.log('Time Threshold:', timeThreshold); - if (!timeThreshold) { - console.error(`Invalid timeRange provided: ${timeRange}`); - return []; - } - - return data?.map((collection) => ({ - ...collection, - data: filterCollectionByTimeRange(collection.data, now - timeThreshold), - })); -}; - -export const removeDuplicateCollections = (collections) => { - const seen = new Set(); - return collections.filter((collection) => { - const duplicate = seen.has(collection._id); - seen.add(collection._id); - return !duplicate; - }); -}; - export const calculateAndUpdateTotalPrice = (collection) => { let totalPrice = 0; if (collection && collection.cards) { @@ -72,11 +24,9 @@ export const calculateAndUpdateTotalPrice = (collection) => { } return totalPrice; }; - export const roundToNearestTenth = (value) => { return Math.round(value * 10) / 10; }; - 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); @@ -86,15 +36,28 @@ export const getCardQuantity = (collectionId, allCollections) => { const collection = allCollections.find((coll) => coll._id === collectionId); return collection ? collection.cards.length : 0; }; -export const useUserId = () => { - const [cookies] = useCookies(['authUser']); - const [userId, setUserId] = useState(null); - - useEffect(() => { - setUserId(cookies?.authUser?.id); - }, [cookies]); - - return userId; +export const getCartCardQuantity = (cart, cardId) => { + let totalItems = 0; + let quantityOfSameId = 0; + cart?.items?.forEach((item) => { + totalItems += item.quantity; + if (item.id === cardId) { + quantityOfSameId += item.quantity; + } + }); + return { totalItems, quantityOfSameId }; +}; +/** + * Checks if a card exists in a cart, collection, or deck. + * + * @param {Object} entity - The cart, collection, or deck to search within. + * @param {Object} cardToFind - The card to search for. + * @return {boolean} - Returns true if the card exists in the entity, otherwise false. + */ +export const doesCardExistInEntity = (entity, cardToFind) => { + const entityCards = entity.cards || entity.items || []; + const cardExists = entityCards.some((card) => card.id === cardToFind.id); + return cardExists; }; export const isEmpty = (obj) => { return ( @@ -102,43 +65,6 @@ export const isEmpty = (obj) => { !Object.entries(obj || {}).length ); }; -export const validateData = (data, eventName, functionName) => { - const dataType = typeof data || data.data || data.data.data || data.message; - console.log( - '----------------------------------------------------------------------------------------------------' - ); - console.log( - `| [SUCCESS] Received data of type: ${dataType} in ${functionName} triggered by event: ${eventName}] |` - ); - console.log( - '----------------------------------------------------------------------------------------------------' - ); - if (data === null || data === undefined) { - console.log( - '----------------------------------------------------------------------------------------------------' - ); - console.warn( - `[Warning] Received null or undefined data in ${functionName} triggered by event: ${eventName}` - ); - console.log( - '----------------------------------------------------------------------------------------------------' - ); - return false; - } - if (isEmpty(data) && isEmpty(data?.data) && isEmpty(data?.data?.data)) { - console.log( - '----------------------------------------------------------------------------------------------------' - ); - console.error( - `[Error] Received empty data object or array in ${functionName} triggered by event: ${eventName}` - ); - console.log( - '----------------------------------------------------------------------------------------------------' - ); - return false; - } - return true; -}; /** * Creates a new price entry object. * @param {number} price - The price to be added to the price entry. @@ -150,22 +76,20 @@ export const createNewPriceEntry = (price) => { timestamp: new Date(), }; }; -export const calculateCollectionValue = (collection) => { - if (!collection) return 0; +const roundDecimalToWholeNumber = (value) => { + return Math.round(value * 100) / 100; +}; +export const calculateChangePercentage = (collectionData) => { + const history = collectionData?.collectionValueHistory; + if (!history || history.length < 2) return 'N/A'; - const cards = collection?.cards; + const oldValue = roundDecimalToWholeNumber(history[1]?.num || 0); // First entry's num value + const newValue = roundDecimalToWholeNumber( + history[history.length - 1]?.num || 0 + ); // Last entry's num value - if (!Array.isArray(cards)) { - console.warn('Invalid collection format', collection, cards); - return 0; - } + if (oldValue === 0) return 'N/A'; // Avoid division by zero - 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; + const percentageChange = ((newValue - oldValue) / oldValue) * 100; + return percentageChange?.toFixed(2) + '%'; // Format to 2 decimal places }; diff --git a/src/context/MAIN_CONTEXT/AuthContext/authContext.js b/src/context/MAIN_CONTEXT/AuthContext/authContext.js index 625cf63..0d988fb 100644 --- a/src/context/MAIN_CONTEXT/AuthContext/authContext.js +++ b/src/context/MAIN_CONTEXT/AuthContext/authContext.js @@ -4,200 +4,65 @@ import React, { useEffect, createContext, useContext, - useRef, useMemo, useState, } from 'react'; -import { useCookies } from 'react-cookie'; -import useLogger from '../../hooks/useLogger'; import { defaultContextValue } from '../../constants'; -import { Redirect, useNavigate } from 'react-router-dom'; -import useFetchWrapper from '../../hooks/useFetchWrapper'; +import { useNavigate } from 'react-router-dom'; import jwt_decode from 'jwt-decode'; -import { CircularProgress, Snackbar } from '@mui/material'; +import useUserData from '../UserContext/useUserData'; +import useManageCookies from '../../hooks/useManageCookies'; +import useAuthManager from './useAuthManager'; export const AuthContext = createContext(defaultContextValue.AUTH_CONTEXT); -const REACT_APP_SERVER = process.env.REACT_APP_SERVER; - export default function AuthProvider({ children }) { const navigate = useNavigate(); - const [cookies, setCookie, removeCookie] = useCookies([ + const { addCookies, getCookie, deleteCookies } = useManageCookies(); + const { user, handleRemoveUser } = useUserData(); + const { login, logout, signup } = useAuthManager(); + const { accessToken, isLoggedIn, authUser } = getCookie([ 'accessToken', - 'refreshToken', 'isLoggedIn', 'authUser', - 'userId', ]); - const [openSnackbar, setOpenSnackbar] = useState(false); - const [user, setUser] = useState({ - isLoggedIn: false, - accessToken: '', - userId: '', - }); - - // Helper function to decode JWT and set user state - const decodeAndSetUser = (accessToken) => { - const decoded = jwt_decode(accessToken); - setUser({ - isLoggedIn: true, - accessToken, - userId: decoded.userId, // Or any other way you extract userId from token or cookies - }); - }; - - useEffect(() => { - const { accessToken } = cookies; - if (accessToken) { - decodeAndSetUser(accessToken); - } else { - setUser({ - isLoggedIn: false, - accessToken: '', - userId: '', - }); - } - }, [cookies.accessToken]); - - const [responseMessage, setResponseMessage] = useState(''); - const { fetchWrapper } = useFetchWrapper(); - // const { logEvent } = useLogger('AuthContext'); - // Helper function to set cookies - const setAuthCookies = (data) => { - const { accessToken, refreshToken, user } = data; - const authData = jwt_decode(accessToken); - - setCookie('accessToken', accessToken, { path: '/' }); - setCookie('refreshToken', refreshToken, { path: '/' }); - setCookie('isLoggedIn', true, { path: '/' }); - // setCookie('userBasicData', basicData, { path: '/' }); - // setCookie('userSecurityData', securityData, { path: '/' }); - setCookie('user', user, { path: '/' }); - setCookie('authUser', authData, { path: '/' }); - setCookie('userId', user._id, { path: '/' }); - navigate('/home'); - }; - - const clearAuthCookies = () => { - // List all cookie names to remove - ['accessToken', 'refreshToken', 'user'].forEach((cookieName) => - removeCookie(cookieName, { path: '/' }) - ); - navigate('/login'); - }; - - const executeAuthAction = async (actionType, url, requestData, loadingId) => { - try { - const responseData = await fetchWrapper( - `${REACT_APP_SERVER}/api/users/${url}`, - 'POST', - requestData, - loadingId - ); - if (!responseData?.data) throw new Error('Invalid response structure'); - setAuthCookies(responseData.data); - } catch (error) { - console.error('Auth action error:', error); - setResponseMessage( - error.message || 'An error occurred during authentication.' - ); - } - }; - // const logout = useCallback(() => { - // clearAuthCookies(); - // }, [removeCookie, navigate]); - const logout = useCallback(() => { - ['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( - 'signin', - 'signin', - { - userSecurityData: { username, password }, - }, - 'login' - ); - }, - [executeAuthAction] - ); - - const signup = useCallback( - async (username, password, firstName, lastName, email) => { - await executeAuthAction( - 'signup', - 'signup', - { - userSecurityData: { firstName, lastName, username, password, email }, - }, - 'signup' - ); - }, - [executeAuthAction] - ); - const checkTokenValidity = useCallback(() => { - const accessToken = cookies.accessToken; + console.log('Checking token validity...', { accessToken }); if (!accessToken) { - 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 + navigate('/login'); return; } - try { const { exp } = jwt_decode(accessToken); const isTokenExpired = Date.now() >= exp * 1000; if (isTokenExpired) { logout(); + handleRemoveUser(); } } catch (error) { console.error('Token validation error:', error); logout(); } - }, [cookies.accessToken, logout]); - - // Token validity check could be triggered periodically or on specific events + }, [user.accessToken]); useEffect(() => { + if (!isLoggedIn) return; checkTokenValidity(); }, [checkTokenValidity]); - const renderSnackbar = ( - } - /> - ); const contextValue = useMemo( () => ({ ...user, isLoggedIn: - process.env.AUTH_ENVIRONMENT !== 'disabled' - ? !!cookies.accessToken - : true, + process.env.AUTH_ENVIRONMENT !== 'disabled' ? !!user.accessToken : true, login, signup, logout, - responseMessage, }), - [cookies.accessToken, login, signup, logout, responseMessage, user] + [login, signup, logout, user] ); return ( - - {children} {renderSnackbar} - + {children} ); } diff --git a/src/context/MAIN_CONTEXT/AuthContext/helpers.jsx b/src/context/MAIN_CONTEXT/AuthContext/helpers.jsx deleted file mode 100644 index f4c3d61..0000000 --- a/src/context/MAIN_CONTEXT/AuthContext/helpers.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import jwt_decode from 'jwt-decode'; - -// login status -// export const LOGGED_IN_COOKIE = 'isLoggedIn'; -export const LOGGED_IN_COOKIE = false; -// token -export const AUTH_COOKIE = 'authToken'; -// user login token + data -export const AUTH_USER_COOKIE = 'authUser'; -// user basic data -export const USER_BASIC_DATA_COOKIE = 'userBasicData'; -// user security data -export const USER_SECURITY_DATA_COOKIE = 'userSecurityData'; -// all user data -export const USER_COOKIE = 'user'; -// user id -export const USER_ID_COOKIE = 'userId'; -// Validator function -export const validateData = (data, eventName, functionName) => { - if (!data || Object.keys(data).length === 0) { - console.warn(`Invalid data in ${functionName} for ${eventName}`); - return false; - } - return true; -}; - -// Process the server response based on the action type (Login/Signup) -export const processResponseData = (response, type) => { - // if (!validateData(data, `${type} Response`, `process${type}Data`)) - // return null; - console.log('data --------------->', response); - if (!response.data.accessToken) return null; - const processedData = { - accessToken: response.data.accessToken, - refreshToken: response.data.refreshToken, - authData: jwt_decode(response.data.accessToken), - refreshData: jwt_decode(response.data.refreshToken), - basicData: response.data?.user?.userBasicData, - securityData: response.data?.user?.userSecurityData, - responseData: response.data, - }; - console.log('accessToken --------------->', processedData.accessToken); - console.log('refreshToken --------------->', processedData.refreshToken); - console.log('processedData --------------->', processedData); - - return processedData; -}; diff --git a/src/context/MAIN_CONTEXT/AuthContext/useAuthManager.jsx b/src/context/MAIN_CONTEXT/AuthContext/useAuthManager.jsx new file mode 100644 index 0000000..d79a6d2 --- /dev/null +++ b/src/context/MAIN_CONTEXT/AuthContext/useAuthManager.jsx @@ -0,0 +1,105 @@ +import { useCallback, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import jwt_decode from 'jwt-decode'; +import useFetchWrapper from '../../hooks/useFetchWrapper'; +import useManageCookies from '../../hooks/useManageCookies'; +import useUserData from '../UserContext/useUserData'; + +function useAuthManager() { + const navigate = useNavigate(); + const { addCookies, getCookie, deleteCookies } = useManageCookies(); + const { authUser, isLoggedIn, accessToken } = getCookie([ + 'authUser', + 'isLoggedIn', + 'accessToken', + ]); + const { handleSetUser } = useUserData(); + const { fetchWrapper } = useFetchWrapper(); + + const setAuthCookies = useCallback( + (data) => { + const { accessToken, refreshToken } = data; + const authData = jwt_decode(accessToken); + addCookies( + ['accessToken', 'refreshToken', 'isLoggedIn', 'authUser', 'userId'], + [accessToken, refreshToken, true, authData, authData.userId], + { path: '/' } + ); + handleSetUser(authData); // Adjust according to your implementation + navigate('/home'); + }, + [addCookies, handleSetUser, navigate] + ); + + const clearAuthCookies = useCallback(() => { + deleteCookies([ + 'accessToken', + 'refreshToken', + 'userId', + 'authUser', + 'isLoggedIn', + ]); + navigate('/login'); + }, []); + + const decodeAndSetUser = useCallback( + (accessToken) => { + const decoded = jwt_decode(accessToken); + handleSetUser(decoded); // Adjust according to your implementation + }, + [handleSetUser] + ); + + const executeAuthAction = useCallback( + async (actionType, url, requestData) => { + try { + const responseData = await fetchWrapper( + `${process.env.REACT_APP_SERVER}/api/users/${url}`, + 'POST', + requestData, + `${url}` + ); + if (!responseData?.data) throw new Error('Invalid response structure'); + setAuthCookies(responseData.data); + } catch (error) { + console.error('Auth action error:', error); + } + }, + [fetchWrapper, setAuthCookies] + ); + + const login = useCallback( + async (formData) => { + const { username, password } = formData; + await executeAuthAction('login', 'signin', { + userSecurityData: { username, password }, + }); + }, + [executeAuthAction] + ); + + const signup = useCallback( + async (formData) => { + const { username, password, firstName, lastName, email } = formData; + await executeAuthAction('signup', 'signup', { + userSecurityData: { firstName, lastName, username, password, email }, + }); + }, + [executeAuthAction] + ); + + const logout = useCallback(() => { + clearAuthCookies(); + }, []); + + useEffect(() => { + if (!isLoggedIn || !accessToken) return; + if (accessToken) { + decodeAndSetUser(accessToken); + } + }, []); + + return { login, logout, signup }; +} + +export default useAuthManager; diff --git a/src/context/MAIN_CONTEXT/CardContext/CardContext.jsx b/src/context/MAIN_CONTEXT/CardContext/CardContext.jsx index 5dd2983..d005fce 100644 --- a/src/context/MAIN_CONTEXT/CardContext/CardContext.jsx +++ b/src/context/MAIN_CONTEXT/CardContext/CardContext.jsx @@ -1,5 +1,5 @@ import { createContext, useContext, useMemo } from 'react'; -import { useCardStoreHook } from '../../hooks/useCardStore'; +import { useCardStoreHook } from './useCardStore'; import { defaultContextValue } from '../../constants'; const { CARD_CONTEXT } = defaultContextValue; diff --git a/src/context/hooks/useCardStore.jsx b/src/context/MAIN_CONTEXT/CardContext/useCardStore.jsx similarity index 65% rename from src/context/hooks/useCardStore.jsx rename to src/context/MAIN_CONTEXT/CardContext/useCardStore.jsx index 2c3870f..9bca160 100644 --- a/src/context/hooks/useCardStore.jsx +++ b/src/context/MAIN_CONTEXT/CardContext/useCardStore.jsx @@ -1,12 +1,10 @@ import { useState, useEffect, useCallback } from 'react'; import axios from 'axios'; -import { useAppContext, useAuthContext, useFormContext } from '..'; -import useLogger from './useLogger'; -import useApiResponseHandler from './useApiResponseHandler'; -import useLocalStorage from './useLocalStorage'; // Ensure this is the correct path to your hook -import { defaultContextValue } from '../constants'; -import { useLoading } from './useLoading'; -const { CARD_CONTEXT } = defaultContextValue; +import { useFormContext } from '../..'; +import useLogger from '../../hooks/useLogger'; +import useLocalStorage from '../../hooks/useLocalStorage'; // Ensure this is the correct path to your hook +import { useLoading } from '../../hooks/useLoading'; +import useManageCookies from '../../hooks/useManageCookies'; function debounce(func, wait) { let timeout; @@ -21,46 +19,24 @@ function debounce(func, wait) { } export const useCardStoreHook = () => { - const { userId } = useAuthContext(); + const { getCookie } = useManageCookies(); + const { userId } = getCookie(['userId']); const { resetForm } = useFormContext(); // Assuming this is where you get your resetForm function - const { cardsWithQuantities } = useAppContext(); // Assuming this is where you get your getCardQuantities function const logger = useLogger('CardProvider'); - const handleApiResponse = useApiResponseHandler(); - // const [openConfigurator, setOpenConfigurator] = useState(false); - const [previousSearchData, setPreviousSearchData] = useLocalStorage( 'previousSearchData', [] ); 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 { - isLoading, - isAnyLoading, - startLoading, - stopLoading, - setError, - error, - clearLoading, - } = useLoading(); - // const [loadingSearchResults, setLoadingSearchResults] = useState(false); + const { isLoading, startLoading, stopLoading } = useLoading(); const [cardsVersion, setCardsVersion] = useState(0); // New state for tracking data version const [searchSettings, setSearchSettings] = useLocalStorage( 'searchSettings', {} ); - // const [isConfiguratorOpen, setIsConfiguratorOpen] = useState(false); - - // const toggleConfigurator = () => { - // setIsConfiguratorOpen(!isConfiguratorOpen); - // }; // USE EFFECT TO TRACK OPEN CONFIGURATOR STATE, AND SET TRUE if the sstate changes - // useEffect(() => { - // console.log('Configurator Open State:', isConfiguratorOpen); - // }, [isConfiguratorOpen]); - useEffect(() => { if (initialLoad) { logger.logEvent('INITIAL SEARCH VALUES', { searchData }); @@ -74,13 +50,11 @@ export const useCardStoreHook = () => { // previous: previousSearchData, }); }, [searchData]); - const clearSearchData = () => { setSearchData([]); setIsDataValid(false); logger.logEvent('Search Data Cleared'); }; - const handleRequest = useCallback( debounce(async (searchParams) => { startLoading('isSearchLoading'); @@ -96,13 +70,13 @@ export const useCardStoreHook = () => { requestBody ); - const data = handleApiResponse(response, 'handleRequest'); + // const data = handleApiResponse(response, 'handleRequest'); - if (data?.data?.length > 0) { + if (response?.data?.data?.length > 0) { logger.logEvent('Data Fetched Successfully', { - dataLength: data.data.length, + dataLength: response?.data.data.length, }); - const limitedData = data.data.slice(0, 30); // Limit to 30 cards + const limitedData = response?.data.data.slice(0, 30); // Limit to 30 cards setIsDataValid(true); setSearchData(limitedData); // Directly set the new searchData } else { @@ -118,7 +92,7 @@ export const useCardStoreHook = () => { } }, 100), [] - ); // 500ms delay, adjust as needed + ); async function fetchRandomCardsAndSet() { startLoading('fetchRandomCardsAndSet'); try { @@ -130,19 +104,14 @@ export const useCardStoreHook = () => { 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 if (!initialLoad) { logger.logEvent('UPDATED SEARCH VALUES', { searchData }); } @@ -151,19 +120,13 @@ export const useCardStoreHook = () => { searchData, searchSettings, setSearchSettings, - // previousSearchData, isDataValid, setSearchData, cardsVersion, setCardsVersion, - // toggleConfigurator, - // setIsConfiguratorOpen, fetchRandomCardsAndSet, handleRequest, randomCards, - // isConfiguratorOpen, - // toggleConfigurator, - // setPreviousSearchData, setIsDataValid, clearSearchData, loadingSearchResults: isLoading('isSearchLoading'), diff --git a/src/context/MAIN_CONTEXT/CartContext/CartContext.js b/src/context/MAIN_CONTEXT/CartContext/CartContext.js index 2327534..3357bb8 100644 --- a/src/context/MAIN_CONTEXT/CartContext/CartContext.js +++ b/src/context/MAIN_CONTEXT/CartContext/CartContext.js @@ -8,413 +8,57 @@ import React, { useRef, useMemo, } from 'react'; -import { useCookies } from 'react-cookie'; -import { useUserContext } from '../UserContext/UserContext'; -import { getCardQuantity } from './helpers'; -import useApiResponseHandler from '../../hooks/useApiResponseHandler'; -import useLogger from '../../hooks/useLogger'; -import { useLoading } from '../../hooks/useLoading'; -import useFetchWrapper from '../../hooks/useFetchWrapper'; - +import { useCartManager } from './useCartManager'; +import { getCartCardQuantity } from '../../Helpers'; +const defaultCart = { + userId: '', + _id: '', + items: [], + quantity: 0, // Total quantity of items + totalPrice: 0, // Total price of items + totalQuantity: 0, +}; export const CartContext = createContext({ - cartData: { - _id: '', - userId: '', - cart: [], - quantity: 0, // Total quantity of items - totalPrice: 0, // Total price of items - totalQuantity: 0, - }, - cart: [], - getCardQuantity: () => {}, + cart: defaultCart, + createUserCart: () => {}, + addCardsToCart: () => {}, + removeCardsFromCart: () => {}, + fetchCartForUser: () => {}, addOneToCart: () => {}, removeOneFromCart: () => {}, - deleteFromCart: () => {}, - getTotalCost: () => {}, - fetchUserCart: () => {}, - createUserCart: () => {}, }); - export const CartProvider = ({ children }) => { - const [cookies, setCookie] = useCookies(['authUser', 'isLoggedIn', 'userId']); - const { authUser, isLoggedIn, userId } = cookies; - const [error, setError] = useState(null); - const createApiUrl = useCallback( - (path) => `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart${path}`, - [userId] - ); - const { fetchWrapper, responseCache } = useFetchWrapper(); - const handleApiResponse = useApiResponseHandler(); - const logger = useLogger('CartContext'); - const [cartData, setCartData] = useState({ - _id: '', - userId: '', - cart: [], - quantity: 0, // Total quantity of items - totalPrice: 0, // Total price of items - totalQuantity: 0, - }); - const [selectedCart, setSelectedCart] = useState([]); - const [selectedCards, setSelectedCards] = useState([]); - const cartId = useRef(selectedCart?.cart?._id); - const [hasFetchedCart, setHasFetchedCart] = useState(false); - const { isLoading } = useLoading(); - const updateSelectedCart = useCallback((cart) => { - setSelectedCart(cart); - setSelectedCards(cart?.cart?.slice(0, 30) || []); - }, []); - const [totalQuantity, setTotalQuantity] = useState(0); - const [totalPrice, setTotalPrice] = useState(0); - const setCartDataAndCookie = useCallback( - (newCartData) => { - if (newCartData && Array.isArray(newCartData?.cart)) { - setCartData(newCartData); - setSelectedCart(newCartData); - setCookie('cartData', newCartData, { - path: '/', - secure: true, - sameSite: 'none', - }); - setCookie('cart', newCartData?.cart, { - path: '/', - secure: true, - sameSite: 'none', - }); - } - }, - [setCookie] - ); - const getTotalCost = () => { - return cartData?.cart?.reduce( - (acc, card) => acc + card?.card_prices[0].tcgplayer_price * card.quantity, - 0 - ); - }; - const totalCost = useMemo( - () => - cartData?.cart?.reduce( - (total, item) => - total + item.quantity * item.card_prices[0]?.tcgplayer_price, - 0 - ), - [cartData?.cart] - ); - const removeCardsFromCart = async (cards, cardIds, cart) => { - const cardsToRemove = []; - const cardsToDecrement = []; - - for (const card of cards) { - const existingCard = cart?.cart?.find((c) => c.id === card.id); - if (existingCard) { - if (existingCard.quantity > 1) { - // Decrement card quantity if more than one - existingCard.tag = 'decremented'; - cardsToDecrement.push(existingCard); - } else { - // Remove the card if quantity is 1 - card.tag = 'removed'; - cardsToRemove.push(card); - } - } - } - - // Remove cards from the cart - try { - if (cardsToRemove.length > 0) { - logger.logEvent('removeCardsFromCart REMOVE', { - cartId, - cardsToRemove, - }); - const response = await fetchWrapper( - createApiUrl(`/${cart?._id}/remove`), - 'DELETE', - { cards: cardsToRemove } - ); - const data = handleApiResponse(response, 'removeCardsFromCart'); - updateSelectedCart(data); - setCartDataAndCookie(data); - } - - // Decrement quantity of cards in the cart - if (cardsToDecrement.length > 0) { - logger.logEvent('removeCardsFromCart DECREMENT', { - cartId, - cardsToDecrement, - }); - const response = await fetchWrapper( - createApiUrl(`/${cart?._id}/update`), - 'PUT', - { userId, cart: cardsToDecrement, method: 'PUT', type: 'decrement' } - ); - const data = handleApiResponse(response, 'removeCardsFromCart'); - updateSelectedCart(data); - setCartDataAndCookie(data); - } - } catch (error) { - logger.logEvent('removeCardsFromCart error', error); - throw error; - } - }; - const updateItemQuantity = (cart, cardInfo, increment = true) => { - const itemIndex = cart.findIndex((item) => item.id === cardInfo.id); - - // If item is found in the cart - if (itemIndex !== -1) { - const existingItem = cart[itemIndex]; - const updatedQuantity = increment - ? existingItem.quantity + 1 - : existingItem.quantity - 1; - - // Update item's quantity and total price or remove if quantity is 0 - if (updatedQuantity > 0) { - cart[itemIndex] = { - ...existingItem, - quantity: updatedQuantity, - totalPrice: updatedQuantity * existingItem.price, - }; - } else { - cart.splice(itemIndex, 1); // Remove the item if quantity becomes 0 - } - } else if (increment) { - // Add new item if it doesn't exist in the cart - cart.push({ - ...cardInfo, - quantity: 1, - totalPrice: cardInfo.price, - }); - } - - return cart; - }; - const createUserCart = useCallback(async () => { - if (!userId) return; - const loadingId = 'createUserCart'; - try { - const data = await fetchWrapper( - `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart/createCart`, - 'POST', - userId, - loadingId - ); - if (data) { - setCartDataAndCookie(data); - } else { - throw new Error('No data returned from the server.'); - } - } catch (error) { - console.error(error.message || 'Failed to create user cart'); - } - }, [userId, fetchWrapper, setCartDataAndCookie]); - const fetchUserCart = useCallback(async () => { - const loadingID = 'fetchUserCart'; - if (!userId || isLoading(loadingID)) return; - try { - const responseData = await fetchWrapper( - `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart`, - 'GET', - null, - loadingID - ); - - if ( - (responseData && responseData?.status === 200) || - responseData?.status === 201 - ) { - console.log('SUCCESS: fetching user cart'); - setCartDataAndCookie(responseData?.data); - } - } catch (error) { - console.error(error); - setError(error.message || 'Failed to fetch user cart'); - logger.logEvent('Failed to fetch user cart', error.message); - } finally { - setHasFetchedCart(true); - // stopLoading(loadingID); - } - }, [ - userId, - isLoading, - createApiUrl, - fetchWrapper, - responseCache, - setCartDataAndCookie, - setError, - logger, - ]); - const updateCart = useCallback( - async (cartId, updatedCart, method, type) => { - if (!userId || !cartId) return; - - const formattedCartData = { - userId, - cart: updatedCart, - method, - type, - quantity: updatedCart.reduce((total, item) => total + item.quantity, 0), - totalPrice: updatedCart.reduce( - (total, item) => total + item.price * item.quantity, - 0 - ), - }; - - try { - const data = await fetchWrapper( - `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart/${cartId}/update`, - 'PUT', - formattedCartData, - 'updateCart' - ); - if (data) { - setCartDataAndCookie(data); - } else { - console.error( - 'Failed to update cart: No data returned from the server' - ); - } - } catch (error) { - console.error('Error updating cart:', error.message); - } - }, - [userId, fetchWrapper, setCartDataAndCookie] - ); - const addCardsToCart = async (cards, cart) => { - console.log('ADD CARDS TO CART: ', cards, cart); - const newCards = []; - const updatedCards = []; - - for (const card of cards) { - const existingCard = cart?.cart?.find((c) => c.id === card.id); - - if (existingCard) { - // If the card already exists in the cart, increment quantity - existingCard.tag = 'incremented'; - updatedCards.push(existingCard); - } else { - // If the card doesn't exist in the cart, add it with quantity 1 - card.tag = 'added'; - newCards.push({ ...card, quantity: 1 }); - } - } - - // Add new cards to the cart - if (newCards.length > 0) { - logger.logEvent('addCardsToCart ADD', { newCards, cart }); - const response = await fetchWrapper( - createApiUrl(`/${cart?._id}/add`), - 'POST', - { userId, cartUpdates: newCards, method: 'POST', type: 'addNew' } - ); - const data = handleApiResponse(response, 'addCardsToCart'); - updateSelectedCart(data); - setCartDataAndCookie(data); - } - - // Update existing cards in the cart - if (updatedCards.length > 0) { - logger.logEvent('addCardsToCart UPDATE', { updatedCards, cart }); - const response = await fetchWrapper( - createApiUrl(`/${cart?._id}/update`), - 'PUT', - { - userId, - cartUpdates: updatedCards, - method: 'PUT', - type: 'increment', - } - ); - const data = handleApiResponse(response, 'addCardsToCart'); - updateSelectedCart(data); - setCartDataAndCookie(data); - } - }; - const addOneToCart = useCallback( - async (cardInfo) => { - if (!cartData?._id) return; - - const updatedCart = updateItemQuantity( - [...cartData.cart], - cardInfo, - true - ); - const method = 'PUT'; // Always 'PUT' because we're updating the cart - - const updatedCartData = await updateCart( - cartData._id, - updatedCart, - method - ); - if (updatedCartData) setCartData(updatedCartData); - }, - [cartData, updateCart, setCartData] - ); - const removeOneFromCart = useCallback( - async (cardInfo) => { - const updatedCart = updateItemQuantity( - [...cartData.cart], - cardInfo, - false - ); - const method = - updatedCart.length < cartData.cart.length ? 'DELETE' : 'PUT'; - - const updatedCartData = await updateCart( - cartData?._id, - updatedCart, - method - ); - if (updatedCartData) setCartData(updatedCartData); - }, - [cartData, updateCart, setCartData] - ); - const deleteFromCart = useCallback( - async (cardInfo) => { - const updatedCart = cartData?.cart.filter( - (item) => item.id !== cardInfo.id - ); - const updatedCartData = await updateCart(cartData._id, updatedCart); - if (updatedCartData) setCartData(updatedCartData); - }, - [cartData?._id, cartData?.cart, updateCart] - ); + const { + cart, + totalCost, + createUserCart, + addCardsToCart, + removeCardsFromCart, + } = useCartManager(); const contextValue = useMemo( () => ({ - cartData, + cart, totalCost, - totalQuantity, - totalPrice, - cart: cartData?.cart, - cartCardQuantity: cartData?.cart?.reduce( + cartCardQuantity: cart?.items?.reduce( (acc, card) => acc + card.quantity, 0 ), - cartCardCount: cartData?.cart?.length, - cartValue: cartData?.cart?.reduce( + cartCardCount: cart?.items?.length, + cartValue: cart?.items?.reduce( (acc, card) => acc + card.card_prices[0]?.tcgplayer_price * card.quantity, 0 ), - getTotalCost, - getCardQuantity, - // addOneToCart, - // removeOneFromCart, - addOneToCart: (cardInfo) => addCardsToCart([cardInfo], selectedCart), + // getTotalCost, + getCardQuantity: getCartCardQuantity, + addOneToCart: (cardInfo) => addCardsToCart([cardInfo], cart), removeOneFromCart: (cardInfo) => - removeCardsFromCart([cardInfo], [cardInfo.id], selectedCart), - deleteFromCart, - fetchCartForUser: fetchUserCart, + removeCardsFromCart([cardInfo], [cardInfo.id], cart), + // deleteFromCart, + // fetchCartForUser: fetchUserCart, createUserCart, }), - [ - totalQuantity, - totalPrice, - getCardQuantity, - addOneToCart, - removeOneFromCart, - deleteFromCart, - cartData, - fetchUserCart, - createUserCart, - ] + [getCartCardQuantity, cart, createUserCart] ); // useEffect(() => { diff --git a/src/context/MAIN_CONTEXT/CartContext/helpers.jsx b/src/context/MAIN_CONTEXT/CartContext/helpers.jsx deleted file mode 100644 index 3ded392..0000000 --- a/src/context/MAIN_CONTEXT/CartContext/helpers.jsx +++ /dev/null @@ -1,11 +0,0 @@ -export const getCardQuantity = (cartData, cardId) => { - let totalItems = 0; - let quantityOfSameId = 0; - cartData?.cart?.forEach((item) => { - totalItems += item.quantity; - if (item.id === cardId) { - quantityOfSameId += item.quantity; - } - }); - return { totalItems, quantityOfSameId }; -}; diff --git a/src/context/MAIN_CONTEXT/CartContext/useCartManager.jsx b/src/context/MAIN_CONTEXT/CartContext/useCartManager.jsx new file mode 100644 index 0000000..b46bd58 --- /dev/null +++ b/src/context/MAIN_CONTEXT/CartContext/useCartManager.jsx @@ -0,0 +1,202 @@ +import { useCallback, useState, useEffect, useMemo } from 'react'; +import useManageCookies from '../../hooks/useManageCookies'; +import useLocalStorage from '../../hooks/useLocalStorage'; +import useLogger from '../../hooks/useLogger'; +import { useLoading } from '../../hooks/useLoading'; +import useFetchWrapper from '../../hooks/useFetchWrapper'; + +const defaultCart = { + userId: '', + _id: '', + items: [], + itemIds: [], + quantity: 0, + totalPrice: 0, + totalQuantity: 0, +}; + +export const useCartManager = () => { + const { addCookies, getCookie, deleteCookies } = useManageCookies(); + const { userId, isLoggedIn } = getCookie(['userId', 'isLoggedIn']); + const [cart, setCart] = useLocalStorage('cart', defaultCart); + const [hasFetched, setHasFetched] = useState(false); + const { fetchWrapper } = useFetchWrapper(); + const logger = useLogger('CartHook'); + const { isLoading, status } = useLoading(); + const createApiUrl = useCallback( + (path) => `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart${path}`, + [userId] + ); + const updateCartLocally = useCallback( + (newCartData) => { + setCart(newCartData); + }, + [setCart] + ); + const fetchUserCart = useCallback(async () => { + // if (!userId || isLoading('fetchUserCart') || !hasFetched) + if (!userId || !isLoggedIn || status === 'loading' || !hasFetched) return; + + try { + const response = await fetchWrapper( + `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart`, + 'GET', + null, + 'fetchUserCart' + ); + updateCartLocally(response.data); + setHasFetched(true); + } catch (error) { + logger.logError('Error fetching user cart', error); + } + }, [userId, isLoading, fetchWrapper, updateCartLocally, logger]); + const createUserCart = useCallback( + async (cartData, operation = 'create') => { + if (!userId) return; + try { + const response = await fetchWrapper( + `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart/create`, + operation === 'create' ? 'POST' : 'PUT', + cartData, + 'createUserCart' + ); + updateCartLocally(response.data); + } catch (error) { + logger.logError(`Error ${operation} user cart`, error); + } + }, + [userId, cart, fetchWrapper, updateCartLocally, logger] + ); + const addCardsToCart = useCallback( + async (cards, cartObj) => { + console.log('ADD CARDS TO CART: ', cards, cartObj); + const newCards = []; + const updatedCards = []; + + for (const card of cards) { + const existingCard = cartObj?.items?.find((c) => c.id === card.id); + + if (existingCard) { + existingCard.tag = 'incremented'; + updatedCards.push(existingCard); + } else { + card.tag = 'added'; + newCards.push({ ...card, quantity: 1 }); + } + } + + // Add new cards to the cart + if (newCards.length > 0) { + logger.logEvent('addCardsToCart ADD', { newCards, cartObj }); + const response = await fetchWrapper( + createApiUrl('/add'), + 'POST', + { userId, cartUpdates: newCards, method: 'POST', type: 'addNew' }, + 'addCardsToCart' + ); + updateCartLocally(response.data); + } + + // Update existing cards in the cart + if (updatedCards.length > 0) { + logger.logEvent('addCardsToCart UPDATE', { updatedCards, cartObj }); + const response = await fetchWrapper( + createApiUrl('/update'), + 'PUT', + { + userId, + cartUpdates: updatedCards, + method: 'PUT', + type: 'increment', + }.cartUpdates, + 'addCardsToCart' + ); + updateCartLocally(response.data); + } + }, + [createApiUrl, fetchWrapper, updateCartLocally, logger] + ); + const removeCardsFromCart = useCallback( + async (cards, cardIds, data) => { + const cardsToRemove = []; + const cardsToDecrement = []; + + for (const card of cards) { + const existingCard = cart?.items?.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); + } + } + } + + // Remove cards from the cart + try { + if (cardsToRemove.length > 0) { + logger.logEvent('removeCardsFromCart REMOVE', { + cardsToRemove, + }); + const response = await fetchWrapper( + createApiUrl('/remove'), + 'DELETE', + { cards: cardsToRemove }, + 'removeCardsFromCart' + ); + updateCartLocally(response.data); + } + + if (cardsToDecrement.length > 0) { + logger.logEvent('removeCardsFromCart DECREMENT', { + cardsToDecrement, + }); + const response = await fetchWrapper( + createApiUrl('/update'), + 'PUT', + { + userId, + cart: cardsToDecrement, + method: 'PUT', + type: 'decrement', + }, + 'removeCardsFromCart' + ); + updateCartLocally(response.data); + } + } catch (error) { + logger.logEvent('removeCardsFromCart error', error); + throw error; + } + }, + [createApiUrl, fetchWrapper, updateCartLocally, logger, cart, userId] + ); + // useEffect(() => { + // updateCartLocally(defaultCart); + // }, []); + useEffect(() => { + fetchUserCart(); + }, []); + + // Calculate totals, quantities using useMemo for performance + const totalCost = useMemo( + () => cart.items.reduce((acc, item) => acc + item.price * item.quantity, 0), + [cart.items] + ); + + return { + cart, + totalCost, + cartCardQuantity: cart.items.length, + fetchUserCart, + createUserCart, + updateCartLocally, + addCardsToCart, + removeCardsFromCart, + addOneToCart: (cardInfo) => addCardsToCart([cardInfo], cart), + removeOneFromCart: (cardInfo) => + removeCardsFromCart([cardInfo], [cardInfo.id], cart), + }; +}; diff --git a/src/context/MAIN_CONTEXT/ChartContext/ChartContext.jsx b/src/context/MAIN_CONTEXT/ChartContext/ChartContext.jsx deleted file mode 100644 index 1902a38..0000000 --- a/src/context/MAIN_CONTEXT/ChartContext/ChartContext.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { createContext, useContext, useEffect, useMemo, useState } from 'react'; -import { useCollectionStore } from '../CollectionContext/CollectionContext'; -import { defaultChartConstants, defaultContextValue } from '../../constants'; -const { TIME_RANGES, SELECTED_TIME_RANGE, TIME_RANGE_PROPS, TIME_RANGES_KEYS } = - defaultChartConstants; - -const ChartContext = createContext(defaultContextValue.CHART_CONTEXT); - -export const ChartProvider = ({ children }) => { - const { selectedCollection } = useCollectionStore(); - - const contextValue = useMemo( - () => ({ - nivoChartData: selectedCollection?.nivoChartData, - newNivoChartData: selectedCollection?.newNivoChartData, - muiChartData: selectedCollection?.muiChartData, - nivoTestData: selectedCollection?.nivoTestData, - }), - [ - selectedCollection?.nivoChartData, - selectedCollection?.newNivoChartData, - selectedCollection?.muiChartData, - selectedCollection?.nivoTestData, - ] - ); - return ( - - {children} - - ); -}; - -export const useChartContext = () => { - const context = useContext(ChartContext); - if (!context) { - throw new Error('useChartContext must be used within a ChartProvider'); - } - return context; -}; diff --git a/src/context/MAIN_CONTEXT/CollectionContext/CollectionContext.jsx b/src/context/MAIN_CONTEXT/CollectionContext/CollectionContext.jsx index 18d5e5b..0f67cfc 100644 --- a/src/context/MAIN_CONTEXT/CollectionContext/CollectionContext.jsx +++ b/src/context/MAIN_CONTEXT/CollectionContext/CollectionContext.jsx @@ -7,57 +7,57 @@ import { useMemo, useState, } from 'react'; -import { calculateCollectionValue } from '../../../assets/currentlyUnused/collectionUtility'; -import { useAuthContext } from '../AuthContext/authContext'; -import useCollectionManager from './useCollectionManager'; import { defaultContextValue } from '../../constants'; -import useSelectedCollection from './useSelectedCollection'; -import { json } from 'react-router-dom'; - +import useManageCookies from '../../hooks/useManageCookies'; export const CollectionContext = createContext( defaultContextValue.COLLECTION_CONTEXT ); export const CollectionProvider = ({ children }) => { - const { isLoggedIn, authUser, userId } = useAuthContext(); - const { - selectedCollection, - allCollections, - showCollections, - selectedCollectionId, - } = useSelectedCollection(); - const { - fetchCollections, - createNewCollection, - deleteCollection, - updateCollection, - addCardsToCollection, - removeCardsFromCollection, - selectedCollectionError, - error, - hasFetchedCollections, - handleError, - setSelectedCollectionError, - } = useCollectionManager(); + const { addCookie, getCookie, deleteCookie } = useManageCookies(); + const { isLoggedIn, authUser, userId } = getCookie([ + 'isLoggedIn', + 'authUser', + 'userId', + ]); + // const { + // selectedCollection, + // allCollections, + // showCollections, + // selectedCollectionId, + // } = useSelectedCollection(); + // const { + // fetchCollections, + // createNewCollection, + // deleteCollection, + // updateCollection, + // addCardsToCollection, + // removeCardsFromCollection, + // selectedCollectionError, + // error, + // hasFetchedCollections, + // handleError, + // setSelectedCollectionError, + // } = useCollectionManager(); - useEffect(() => { - // Check if collections need to be fetched for the logged-in user - if (!hasFetchedCollections && isLoggedIn && userId) { - fetchCollections(); - } - }, []); + // useEffect(() => { + // // Check if collections need to be fetched for the logged-in user + // if (!hasFetchedCollections && isLoggedIn && userId) { + // fetchCollections(); + // } + // }, []); const contextValue = useMemo( () => ({ - ...selectedCollection, - selectedCollection: selectedCollection, - selectedCollectionId, - allCollections, - showCollections, - error, - selectedCollectionError, + // ...selectedCollection, + // selectedCollection: selectedCollection, + // selectedCollectionId, + // allCollections, + // showCollections, + // error, + // selectedCollectionError, }), - [selectedCollection, allCollections, showCollections, error] + [] ); useEffect(() => { diff --git a/src/context/MAIN_CONTEXT/CollectionContext/helpers.jsx b/src/context/MAIN_CONTEXT/CollectionContext/helpers.jsx deleted file mode 100644 index ad90888..0000000 --- a/src/context/MAIN_CONTEXT/CollectionContext/helpers.jsx +++ /dev/null @@ -1,123 +0,0 @@ -export const calculatePriceChanges = (data) => { - if (!Array.isArray(data) || data.length === 0) return []; - - const sortedData = data.sort((a, b) => new Date(a.x) - new Date(b.x)); - const latestDataPoint = sortedData[sortedData.length - 1]; - const latestTime = new Date(latestDataPoint.x).getTime(); - const twentyFourHoursAgo = latestTime - 24 * 60 * 60 * 1000; - - let closestIndex = -1; - let closestTimeDifference = Number.MAX_SAFE_INTEGER; - - for (let i = 0; i < sortedData.length - 1; i++) { - const time = new Date(sortedData[i].x).getTime(); - const timeDifference = Math.abs(time - twentyFourHoursAgo); - - if (timeDifference < closestTimeDifference) { - closestTimeDifference = timeDifference; - closestIndex = i; - } - } - - if (closestIndex !== -1) { - const pastPrice = sortedData[closestIndex].y; - const priceChange = latestDataPoint.y - pastPrice; - const percentageChange = ((priceChange / pastPrice) * 100).toFixed(2); - return [ - { - startDate: sortedData[closestIndex].x, - lowPoint: pastPrice.toFixed(2), - highPoint: latestDataPoint?.y?.toFixed(2), - endDate: latestDataPoint?.x, - priceChange: priceChange.toFixed(2), - percentageChange: `${percentageChange}%`, - priceIncreased: priceChange > 0, - }, - ]; - } - - return []; -}; - -export const getTopCollection = (collections) => { - return collections?.reduce( - (max, collection) => - max.totalPrice > collection.totalPrice ? max : collection, - collections[0] - ); -}; - -export const getTopCard = (cards) => { - return cards?.reduce( - (max, card) => (max.price > card.price ? max : card), - cards[0] - ); -}; - -export const calculateTotalPriceOfAllCollections = (collections) => { - return collections - ?.reduce((total, collection) => total + collection.totalPrice, 0) - .toFixed(2); -}; - -export const calculateStatistics = (data, timeRange, allCollections, cards) => { - if (!data || !Array.isArray(data.data) || data.data.length === 0) return {}; - - const filteredData = data?.data?.filter( - (item) => new Date(item?.x).getTime() >= Date.now() - timeRange - ); - if (filteredData.length === 0) return {}; - - let highPoint = Math.max(...filteredData.map((item) => item.y)); - let lowPoint = Math.min(...filteredData.map((item) => item.y)); - let sum = filteredData.reduce((acc, curr) => acc + curr.y, 0); - let averageData = calculatePriceChanges(filteredData); - let average = sum / filteredData.length || 0; - let volume = filteredData.length; - let mean = sum / volume; - let squaredDiffs = filteredData.map((item) => { - const diff = item.y - mean; - return diff * diff; - }); - let volatility = Math.sqrt(squaredDiffs.reduce((a, b) => a + b, 0) / volume); - - const topCollection = getTopCollection(allCollections); - const topCard = getTopCard(cards); - const totalPriceAllCollections = - calculateTotalPriceOfAllCollections(allCollections); - - return { - highPoint: highPoint.toFixed(2), - lowPoint: lowPoint.toFixed(2), - twentyFourHourAverage: { - startDate: averageData[0]?.startDate, - endDate: averageData[0]?.endDate, - lowPoint: averageData[0]?.lowPoint, - highPoint: averageData[0]?.highPoint, - priceChange: averageData[0]?.priceChange, - percentageChange: averageData[0]?.percentageChange, - priceIncreased: averageData[0]?.priceIncreased, - }, - average: average.toFixed(2), - volume, - volatility: volatility.toFixed(2), - general: { - totalPrice: totalPriceAllCollections, - topCard: topCard?.name, // or any other identifier for the card - topCollection: topCollection?.name, // or any other identifier for the collection - }, - }; -}; - -export const calculateStatsForCollection = (collection, timeRange) => { - try { - const data = collection?.chartData?.allXYValues || []; - return calculateStatistics({ data }, timeRange) || {}; - } catch (error) { - console.error( - `Error calculating statistics for collection ${collection._id}:`, - error - ); - return {}; // Default value in case of error - } -}; diff --git a/src/context/MAIN_CONTEXT/CollectionContext/useCollectionManager.jsx b/src/context/MAIN_CONTEXT/CollectionContext/useCollectionManager.jsx index adacd45..2576ccb 100644 --- a/src/context/MAIN_CONTEXT/CollectionContext/useCollectionManager.jsx +++ b/src/context/MAIN_CONTEXT/CollectionContext/useCollectionManager.jsx @@ -1,27 +1,47 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ 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 useManageCookies from '../../hooks/useManageCookies'; import useSelectedCollection from './useSelectedCollection'; - +const defaultExportValue = { + fetchCollections: () => {}, + createNewCollection: () => {}, + deleteCollection: () => {}, + updateCollection: () => {}, + addNewCollection: () => {}, + removeCollection: () => {}, + setCustomError: () => {}, + customError: '', + refreshCollections: () => {}, + addOneToCollection: () => {}, + removeOneFromCollection: () => {}, + updateOneInCollection: () => {}, + hasFetchedCollections: false, + handleError: () => {}, +}; const useCollectionManager = () => { - const { fetchWrapper, status } = useFetchWrapper(); - const { userId, isLoggedIn } = useAuthContext(); // Now getting userId from context + const { fetchWrapper, status, data } = useFetchWrapper(); + const { addCookie, getCookie, deleteCookie } = useManageCookies(); + const { isLoggedIn, authUser, userId } = getCookie([ + 'isLoggedIn', + 'authUser', + 'userId', + ]); const logger = useLogger('useCollectionManager'); + const collectionData = useSelectedCollection(); + if (!collectionData) { + console.error('Collection data not found'); + return null; + } const { selectedCollection, - allCollections, - showCollections, selectedCollectionId, - customError: selectedCollectionError, + customError, + setCustomError, + refreshCollections, updateCollectionsData, - handleSelectCollection, - resetCollection, - setCustomError: setSelectedCollectionError, - } = useSelectedCollection(); + } = collectionData; const [error, setError] = useState(null); const [hasFetchedCollections, setHasFetchedCollections] = useState(false); const handleError = useCallback( @@ -57,7 +77,6 @@ const useCollectionManager = () => { updateCollectionsData, handleError, ]); - const performAction = useCallback( async (path, method, data, actionName, options = {}) => { if (!userId || !isLoggedIn || status === 'loading') { @@ -65,13 +84,12 @@ const useCollectionManager = () => { return; } - if (!selectedCollection) { - setSelectedCollectionError('No collection selected'); + if (!selectedCollection || selectedCollection === null) { + console.warn('No collection selected'); return; } options.beforeAction?.(); - console.log( 'PERFORM ACTION', path, @@ -83,9 +101,7 @@ const useCollectionManager = () => { try { console.log('URL', path); - await fetchWrapper(path, method, data, actionName); - options.afterAction?.(); fetchCollections(); // Refresh collections after any action } catch (error) { @@ -100,27 +116,69 @@ const useCollectionManager = () => { createApiUrl, fetchCollections, handleError, - setSelectedCollectionError, + setCustomError, selectedCollection, ] ); - const createNewCollection = (data) => - performAction(createApiUrl('create'), 'POST', data, 'createNewCollection'); - const deleteCollection = (collectionId) => - performAction( - createApiUrl(`${collectionId}/delete`), - 'DELETE', - { collectionId: collectionId }, - 'deleteCollection' - ); - const updateCollection = (collectionId, updatedData) => - performAction( - createApiUrl(`${collectionId}/update`), - 'PUT', - updatedData, - 'updateCollection' - ); + const createNewCollection = useCallback(async (datas) => { + const newCollection = { + name: datas.name, + description: datas.description, + }; + + const path = createApiUrl('create'); + performAction(path, 'POST', newCollection, 'createNewCollection'); + }, []); + + // performAction(createApiUrl('create'), 'POST', datas, 'createNewCollection'); + const deleteCollection = useCallback( + async (collectionId) => { + performAction( + createApiUrl(`${collectionId}/delete`), + 'DELETE', + { collectionId: collectionId }, + 'deleteCollection', + { + beforeAction: () => { + console.log('BEFORE ACTION', collectionId); + }, + // afterAction: () => { + // console.log(`Collection ${collectionId} deleted successfully.`); + // removeCollection(collectionId, data); + // refreshCollections(); // Refresh collections after any action + // }, + afterAction: async () => { + console.log(`Collection ${collectionId} deleted successfully.`); + // Fetch the latest collections after deletion + try { + // removeCollection(collectionId, data); + // const updatedCollections = await fetchWrapper( + // createApiUrl('allCollections'), + // 'GET' + // ); + // refreshCollections(updatedCollections.data); + } catch (error) { + handleError(error, 'fetching collections after deletion'); + } + }, + } + ); + }, + [performAction, createApiUrl, fetchWrapper, handleError] + ); + + const updateCollection = useCallback( + async (collectionId, updatedData) => { + performAction( + createApiUrl(`${collectionId}/update`), + 'PUT', + updatedData, + 'updateCollection' + ); + }, + [performAction, createApiUrl] + ); const addCardsToCollection = useCallback( (newCards, collection) => { if (selectedCollectionId === 'selectedCollectionId') @@ -157,7 +215,6 @@ const useCollectionManager = () => { options ); } else { - // ADD NEW CARD performAction( createApiUrl(`${selectedCollectionId}/cards/add`), 'POST', @@ -166,13 +223,6 @@ const useCollectionManager = () => { options ); } - // performAction( - // createCardApiUrl('update'), - // 'PUT', - // { cards: newCards }, - // 'addCardsToCollection', - // ['object'] - // ); }, [performAction] ); @@ -188,27 +238,41 @@ const useCollectionManager = () => { }, [performAction] ); - return { - fetchCollections, - createNewCollection, - deleteCollection, - updateCollection, - addCardsToCollection, - addOneToCollection: (cards, collection) => - addCardsToCollection(cards, collection), - removeCardsFromCollection, - removeOneFromCollection: (cards, collection) => - removeCardsFromCollection(cards, collection), - selectedCollection, - allCollections, - showCollections, - selectedCollectionId, - selectedCollectionError, - error, - hasFetchedCollections, - handleError, - setSelectedCollectionError, - }; + const memoizedReturnValues = useMemo( + () => ({ + fetchCollections, + createNewCollection, + deleteCollection, + updateCollection, + addCardsToCollection, + removeCardsFromCollection, + customError, + setCustomError, + refreshCollections, + updateCollectionsData, + hasFetchedCollections, + handleError, + removeOneFromCollection: (cards, collection) => + removeCardsFromCollection(cards, collection), + addOneToCollection: (cards, collection) => + addCardsToCollection(cards, collection), + }), + [ + fetchCollections, + createNewCollection, + deleteCollection, + updateCollection, + addCardsToCollection, + removeCardsFromCollection, + customError, + setCustomError, + refreshCollections, + updateCollectionsData, + hasFetchedCollections, + handleError, + ] + ); + return memoizedReturnValues; }; export default useCollectionManager; diff --git a/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx b/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx index dc36753..ab36e85 100644 --- a/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx +++ b/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx @@ -1,77 +1,100 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { - DEFAULT_COLLECTION, - SELECTED_COLLECTION_ID, - DEFAULT_CARDS_COUNT, -} from '../../constants'; +import { DEFAULT_COLLECTION, SELECTED_COLLECTION_ID } from '../../constants'; import useLocalStorage from '../../hooks/useLocalStorage'; -import jsonData from '../../../data/nivoTestData.json'; import _set from 'lodash/set'; -import useTimeRange from '../../../components/forms/selectors/useTimeRange'; -import { calculateStatistics, calculateStatsForCollection } from './helpers'; -import { a } from 'react-spring'; - +const defaultCollections = { + allIds: [SELECTED_COLLECTION_ID], + byId: { + [SELECTED_COLLECTION_ID]: DEFAULT_COLLECTION, + }, + selectedId: SELECTED_COLLECTION_ID, + prevSelectedId: SELECTED_COLLECTION_ID, + showCollections: false, +}; function useSelectedCollection() { - const { nivoTestData } = jsonData; const [collections, setCollections] = useLocalStorage('collections', { allIds: [], byId: { [SELECTED_COLLECTION_ID]: DEFAULT_COLLECTION, }, selectedId: SELECTED_COLLECTION_ID, + prevSelectedId: null, showCollections: true, - nivoTestData: nivoTestData, }); + useEffect(() => { + if (!collections || collections?.allIds?.length === 0) { + setCollections(defaultCollections); + } + }, []); + useEffect(() => { + if ( + collections.allIds.includes(SELECTED_COLLECTION_ID) && + collections.allIds.length > 1 + ) { + setCollections((prev) => { + const updatedAllIds = prev.allIds.filter( + (id) => id !== SELECTED_COLLECTION_ID + ); + const updatedById = { ...prev.byId }; + delete updatedById[SELECTED_COLLECTION_ID]; + + return { + ...prev, + allIds: updatedAllIds, + byId: updatedById, + // Update selectedId to the first collection if the selected collection is removed + selectedId: + prev.selectedId === SELECTED_COLLECTION_ID + ? updatedAllIds[0] + : prev.selectedId, + }; + }); + } + }, [collections.allIds, collections.byId, setCollections]); const [selectedCollectionId, setSelectedCollectionId] = useState(null); const [customError, setCustomError] = useState(null); const prevSelectedCollectionIdRef = useRef(null); - // const { selectedTimeRange } = useTimeRange(); - if (!Array.isArray(collections.allIds) || !collections.allIds.length) { - return null; - } const [selectedStat, setSelectedStat] = useState(''); - // const validCollections = - // Array.isArray(collections.allIds) && collections.allIds.length > 0; - // const statsByCollectionId = useMemo( - // () => - // validCollections - // ? collections.byId?.reduce((acc, collection) => { - // acc[collection?._id] = calculateStatsForCollection( - // collection, - // selectedTimeRange - // ); - // return acc; - // }, {}) - // : {}, - // [collections.byId, selectedTimeRange] - // // [collections.allIds, selectedTimeRange] - // ); - const createMarkers = (selectedCollection) => { - if (!selectedCollection || !selectedCollection.collectionStatistics) - return []; + useEffect(() => { + prevSelectedCollectionIdRef.current = selectedCollectionId; + // collections.prevSelectedId = selectedCollectionId; + }, [selectedCollectionId]); + // Assuming collections.byId is an object containing all collections keyed by their IDs + + const getSelectedCollection = useMemo(() => { + const selectedCollection = collections.byId[collections.selectedId]; + if (!selectedCollection) { + setCustomError('Selected collection not found.'); + return collections.byId[collections.allIds[0]]; + } + return selectedCollection; + }, [collections.byId, collections.selectedId]); + + const createMarkers = (selected) => { + if (!selected || !selected.collectionStatistics) return []; const { highPoint, lowPoint, avgPrice, percentageChange } = - selectedCollection.collectionStatistics; + selected.collectionStatistics; return [ { axis: 'y', value: percentageChange, lineStyle: { stroke: '#b0413e', strokeWidth: 2 }, - legend: `${selectedCollection.name} High`, + legend: `${selected.name} High`, legendOrientation: 'vertical', }, { axis: 'y', value: lowPoint, lineStyle: { stroke: '#b0413e', strokeWidth: 2 }, - legend: `${selectedCollection.name} Low`, + legend: `${selected.name} Low`, legendOrientation: 'vertical', }, { axis: 'y', value: avgPrice, lineStyle: { stroke: '#b0413e', strokeWidth: 2 }, - legend: `${selectedCollection.name} Avg`, + legend: `${selected.name} Avg`, legendOrientation: 'vertical', }, ]; @@ -80,32 +103,20 @@ function useSelectedCollection() { if (!collections.selectedId) return []; return createMarkers(collections.byId[collections.selectedId]); }, [collections.allIds]); // Add dependencies as necessary, e.g., someSelectedCollectionId - useEffect(() => { - prevSelectedCollectionIdRef.current = selectedCollectionId; - }, [selectedCollectionId]); - const getSelectedCollection = useMemo( - () => collections.byId[collections.selectedId] || DEFAULT_COLLECTION, - [collections.byId, collections.selectedId] - ); const handleSelectCollection = useCallback( (collection) => { console.log('SELECTED COLLECTION ID', collection?._id); - // setSelectedCollectionId(collection?._id); setCustomError(null); - if (!collections.byId[collection?._id]) { setCustomError('Invalid collection selected'); return; } + // setSelectedCollectionId(collection?._id); setCollections((prev) => ({ ...prev, selectedId: collection?._id, showCollections: !prev.showCollections, - // showCollections: true, })); - // if (prevSelectedCollectionIdRef.current !== collection?._id) { - // toggleShowCollections(); - // } setCustomError(null); }, [collections.byId, setCollections, setSelectedCollectionId] @@ -126,6 +137,33 @@ function useSelectedCollection() { })); setCustomError(null); }, [setCollections]); + const refreshCollections = useCallback( + (updatedCollections) => { + // Directly use the updatedCollections parameter to update the state + setCollections((prev) => { + // Assuming updatedCollections is an array of collection objects with _id properties + const updatedById = updatedCollections.reduce( + (acc, collection) => { + acc[collection._id] = collection; + return acc; + }, + { ...prev.byId } + ); + console.log('UPDATED COLLECTIONS', updatedById); + const updatedAllIds = updatedCollections.map( + (collection) => collection._id + ); + console.log('UPDATED ALL IDS', updatedAllIds); + return { + ...prev, + allIds: updatedAllIds, + byId: updatedById, + selectedId: collections.selectedId || prev.allIds[0], + }; + }); + }, + [setCollections] + ); const updateCollectionsData = useCallback( (newCollections) => { console.log('updateCollectionsData', newCollections); @@ -134,6 +172,20 @@ function useSelectedCollection() { newCollections?.forEach((collection) => { updatedById[collection._id] = collection; }); + // FILTER UNDEFINED VALUES + const updatedAllIds = Object.keys(updatedById).filter( + (key) => updatedById[key] !== undefined + ); + console.log('UPDATED COLLECTIONS', updatedById); + console.log('UPDATED ALL IDS', updatedAllIds); + console.log('UPDATED ALL IDS', Object.keys(updatedById)); + + // return { + // ...prev, + // allIds: updatedAllIds, + // byId: updatedById, + // // selectedId: collections.selectedId || prev.allIds[0], + // }; return { ...prev, byId: updatedById, @@ -141,7 +193,7 @@ function useSelectedCollection() { }; }); }, - [setCollections] + [setCollections, collections.selectedId] ); const addNewCollection = useCallback( (newCollection) => { @@ -151,65 +203,73 @@ function useSelectedCollection() { [updateCollectionsData] ); const removeCollection = useCallback( - (collectionId) => { + (collectionId, remainingCollectionIds) => { setCollections((prev) => { - const { [collectionId]: _, ...remainingById } = prev.byId; + console.log('REMOVE COLLECTION', collectionId, remainingCollectionIds); + const updatedById = { ...prev.byId }; + delete updatedById[collectionId]; // Remove the collection + const updatedAllIds = prev.allIds.filter((id) => id !== collectionId); + return { ...prev, - allIds: prev.allIds.filter((id) => id !== collectionId), - byId: remainingById, + byId: updatedById, + allIds: updatedAllIds, + // Ensure selectedId is null if the deleted collection was selected selectedId: prev.selectedId === collectionId ? null : prev.selectedId, }; }); + // refreshCollections(); // Optionally, refresh collections if they are fetched from an external source }, - [setCollections] + [setCollections, refreshCollections] ); - const prevCollectionsRef = useRef(); useEffect(() => { if (prevCollectionsRef.current) { console.log('Collections data updated:', collections); } prevCollectionsRef.current = collections; - }, [collections]); // Dependency array ensures this runs only when collections change + }, [collections.allIds, collections.byId[collections.selectedId]]); return { - selectedCollectionId: collections.selectedId, - selectedCollection: getSelectedCollection, - allCollections: Object.values(collections.byId), + selectedCollectionId: + collections.selectedId || prevSelectedCollectionIdRef.current, + selectedCollection: getSelectedCollection || {}, + allIds: collections?.allIds || [], + allCollections: Object.values(collections?.byId), showCollections: !!collections.showCollections, - nivoTestData: nivoTestData, + byId: collections?.byId || {}, + + selectedStat, + setSelectedStat, + markers, handleSelectCollection, + // selectedCollection: getSelectedCollection, handleBackToCollections: resetCollection, updateCollectionField, resetCollection, updateCollectionsData, customError, - // toggleShowCollections, + refreshCollections, addNewCollection, removeCollection, setCustomError, - prevSelectedCollectionId: prevSelectedCollectionIdRef.current, - // stats: - // calculateStatistics( - // { data: null }, - // selectedTimeRange, - // Object.values(collections.byId) - // ) || {}, - - // allStats: [statsByCollectionId], - // statsByCollectionId: statsByCollectionId[collections?.selectedId] || {}, - - selectedStat, - markers, - setSelectedStat, - // totalValue, - // topFiveCards, - // calculateTotalPriceOfAllCollections, - // calculatePriceChanges, - // getTopCard, - // getTopCollection, }; } export default useSelectedCollection; +// const validCollections = +// Array.isArray(collections.allIds) && collections.allIds.length > 0; +// const statsByCollectionId = useMemo( +// () => +// validCollections +// ? collections.byId?.reduce((acc, collection) => { +// acc[collection?._id] = calculateStatsForCollection( +// collection, +// selectedTimeRange +// ); +// return acc; +// }, {}) +// : {}, +// [collections.byId, selectedTimeRange] +// // [collections.allIds, selectedTimeRange] +// ); diff --git a/src/context/MAIN_CONTEXT/DeckContext/DeckContext.js b/src/context/MAIN_CONTEXT/DeckContext/DeckContext.js index 419c15f..0c3e5c1 100644 --- a/src/context/MAIN_CONTEXT/DeckContext/DeckContext.js +++ b/src/context/MAIN_CONTEXT/DeckContext/DeckContext.js @@ -5,19 +5,22 @@ import { calculateAndUpdateTotalPrice, getCardQuantity, } 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 useSelectedDeck from './useSelectedDeck.jsx'; // Import the deck management hook import useDeckManager from './useDeckManager.jsx'; // Import the deck management hook +import useManageCookies from '../../hooks/useManageCookies.jsx'; export const DeckContext = createContext(); export const DeckProvider = ({ children }) => { - const { userId } = useAuthContext(); - const handleApiResponse = useApiResponseHandler(); + const { addCookie, getCookie, deleteCookie } = useManageCookies(); + const { isLoggedIn, authUser, userId } = getCookie([ + 'isLoggedIn', + 'authUser', + 'userId', + ]); const logger = useLogger('DeckProvider'); const { fetchWrapper } = useFetchWrapper(); const { startLoading, stopLoading, isLoading } = useLoading(); @@ -26,26 +29,16 @@ export const DeckProvider = ({ children }) => { const { selectedDeck, allDecks, - setSelectedDeck, - fetchAndUpdateDecks, - updateDeckDetails, - createUserDeck, - deleteUserDeck, - addCardToDeck, - removeCardFromDeck, + handleSelectDeck: setSelectedDeck, + addCardToSelectedDeck: addCardToDeck, + updateDeck: updateDeckDetails, } = useSelectedDeck(); const { fetchDecks, createNewDeck, deleteDeck, - updateDeck, error, - hasFetchedDecks, - handleError, - setSelectedDeckError, - addCardsToDeck, - removeCardsFromDeck, addOneToDeck, removeOneFromDeck, } = useDeckManager(); @@ -74,7 +67,6 @@ export const DeckProvider = ({ children }) => { startLoading, stopLoading, fetchWrapper, - handleApiResponse, logger, error, getCardQuantity: (cardId) => getCardQuantity(cardId, selectedDeck), @@ -89,15 +81,12 @@ export const DeckProvider = ({ children }) => { updateDeckDetails, createNewDeck, deleteDeck, - addCardToDeck, - removeCardFromDeck, createApiUrl, isLoading, startLoading, stopLoading, fetchWrapper, - handleApiResponse, logger, ] ); @@ -114,370 +103,3 @@ export const useDeckStore = () => { } return context; }; - -// 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 - -// export const DeckContext = createContext(defaultContextValue); - -// 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 -// ); - -// 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, -// }; - -// 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); - -// 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 -// } - -// 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 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, -// 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} -// ); -// }; - -// 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/useDeckManager.jsx b/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx index fd35e4b..7f27df4 100644 --- a/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx +++ b/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx @@ -1,15 +1,17 @@ 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'; +import useManageCookies from '../../hooks/useManageCookies'; const useDeckManager = () => { const { fetchWrapper, status } = useFetchWrapper(); - const { userId, isLoggedIn } = useAuthContext(); + const { addCookie, getCookie, deleteCookie } = useManageCookies(); + const { isLoggedIn, authUser, userId } = getCookie([ + 'isLoggedIn', + 'authUser', + 'userId', + ]); const logger = useLogger('useDeckManager'); const { selectedDeck, @@ -18,9 +20,10 @@ const useDeckManager = () => { updateMultipleDecks, handleSelectDeck, addCardToSelectedDeck, + deckUpdated, setCustomError: setSelectedDeckError, } = useSelectedDeck(); - const [decks] = useLocalStorage('decks', []); + // const [decks] = useLocalStorage('decks', []); const [error, setError] = useState(null); const [hasFetchedDecks, setHasFetchedDecks] = useState(false); const handleError = useCallback( @@ -33,7 +36,6 @@ const useDeckManager = () => { ); 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 { @@ -44,8 +46,6 @@ const useDeckManager = () => { 'fetchDecks' ); updateMultipleDecks(responseData?.data); - - // handleSelectDeck(responseData?.data[decks?.byId?.[selectedDeckId]]); setHasFetchedDecks(true); } catch (error) { handleError(error, 'fetchDecks'); @@ -71,7 +71,6 @@ const useDeckManager = () => { setSelectedDeckError('No deck selected'); return; } - options.beforeAction?.(); console.log( 'PERFORM ACTION', @@ -130,14 +129,6 @@ const useDeckManager = () => { } return deck; }); - // setDecks((prevDecks) => { - // return prevDecks.map((deck) => { - // if (deck._id === deckId) { - // updatedDeck = { ...deck, name, description, tags, color }; - // return updatedDeck; - // } - // return deck; - // }); }); try { @@ -178,6 +169,8 @@ const useDeckManager = () => { }, afterAction: () => { if (existingCard) { + addCardToSelectedDeck(newCards); // This method should handle the logic internally + console.log('Card already exists in deck'); return; } @@ -255,12 +248,8 @@ const useDeckManager = () => { createNewDeck, deleteDeck, updateDeck, - selectedDeck, - allDecks, - error, hasFetchedDecks, handleError, - setSelectedDeckError, addCardsToDeck, removeCardsFromDeck, addOneToDeck: (cards, deck) => addCardsToDeck(cards, deck), diff --git a/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx b/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx index 30f714b..fe1c84b 100644 --- a/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx +++ b/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx @@ -6,43 +6,81 @@ import { } from '../../constants'; import useLocalStorage from '../../hooks/useLocalStorage'; import _set from 'lodash/set'; - +const defaultDecks = { + allIds: [SELECTED_DECK_ID], + byId: { + [SELECTED_DECK_ID]: DEFAULT_DECK, + }, + selectedId: SELECTED_DECK_ID, + prevSelectedId: SELECTED_DECK_ID, + showDecks: true, +}; 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, setSelectedDeckId] = useState(null); - // const selectedDeckId = useState(decks?.selectedId)[0]; - // const selectedDeck = decks.byId[selectedDeckId] || DEFAULT_DECK; - const prevSelectedDeckIdRef = useRef(null); + const [decks, setDecks] = useLocalStorage('decks', defaultDecks); + const [deckUpdated, setDeckUpdated] = useState(false); // Track when a deck is updated + const [customError, setCustomError] = useState(null); useEffect(() => { - prevSelectedDeckIdRef.current = selectedDeckId; - }, [selectedDeckId]); + if (!decks || decks?.allIds?.length === 0) { + setDecks(defaultDecks); + } + }, [decks]); + const addCardToSelectedDeck = useCallback( + (card) => { + if (!decks.selectedId) return; + const updatedDeck = { + ...decks.byId[decks.selectedId], + cards: [...decks.byId[decks.selectedId].cards, card], + }; + + setDecks((prevDecks) => ({ + ...prevDecks, + byId: { + ...prevDecks.byId, + [decks.selectedId]: updatedDeck, + }, + })); + setDeckUpdated(true); // Signal that a deck has been updated + }, + [decks] + ); + useEffect(() => { + if (decks.allIds.includes(SELECTED_DECK_ID) && decks.allIds.length > 1) { + setDecks((prev) => { + const updatedAllIds = prev.allIds.filter( + (id) => id !== SELECTED_DECK_ID + ); + const updatedById = { ...prev.byId }; + delete updatedById[SELECTED_DECK_ID]; + + return { + ...prev, + allIds: updatedAllIds, + byId: updatedById, + // Update selectedId to the first deck if the selected deck is removed + selectedId: + prev.selectedId === SELECTED_DECK_ID + ? updatedAllIds[0] + : prev.selectedId, + }; + }); + } + }, [decks.allIds, decks.byId, setDecks]); const getSelectedDeck = useMemo( () => decks.byId[decks?.selectedId], [decks.byId, decks.selectedId] ); const updateDeck = useCallback( - (updatedDeck, deckId = selectedDeckId) => { + (updatedDeck, deckId = decks.selectedId) => { setDecks((prev) => ({ ...prev, byId: { ...prev.byId, [deckId]: updatedDeck, }, - deckHasBeenUpdated: true, })); }, - [setDecks, selectedDeckId] + [setDecks, decks.selectedId] ); const updateMultipleDecks = useCallback( (decksArray) => { @@ -59,7 +97,6 @@ const useSelectedDeck = () => { ...prev, byId: updatedById, allIds: Array.from(updatedAllIds), - deckHasBeenUpdated: true, }; }); }, @@ -67,18 +104,19 @@ const useSelectedDeck = () => { ); const handleSelectDeck = useCallback( (deck) => { - const deckId = deck?._id; - console.log('SELECTED DECK ID', deckId); + console.log('SELECTED DECK ID', deck?._id); + setCustomError(null); + if (!decks.byId[deck?._id]) { + setCustomError('Invalid deck selected'); + return; + } setDecks((prev) => ({ ...prev, - selectedId: deckId, - selectedDeck: deck, - selectedDeckCards: deck?.cards, - deckHasBeenSelected: true, + selectedId: deck._id, showDecks: !prev.showDecks, })); }, - [setDecks, selectedDeckId] + [setDecks, decks.selectedId] ); const addNewDeck = useCallback( (newDeck) => { @@ -107,32 +145,32 @@ const useSelectedDeck = () => { }, [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] - ); const prevDecksRef = useRef(); useEffect(() => { if (prevDecksRef.current) { - console.log('Collections data updated:', decks); + console.log('Decks data updated:', decks); } prevDecksRef.current = decks; - }, [decks]); // Dependency array ensures this runs only when collections change + }, [ + decks.allIds[ + decks.allIds.includes(SELECTED_DECK_ID) + ? decks.allIds.indexOf(SELECTED_DECK_ID) + : decks.allIds.length - 1 + ], + ]); // decks array ensures this runs only when collections change + useEffect(() => { + // Reset the deckUpdated state after it's been set to true + if (deckUpdated) { + setDeckUpdated(false); + } + }, [deckUpdated]); return { selectedDeckId: decks.selectedId, selectedDeck: getSelectedDeck, allDecks: Object.values(decks.byId), showDecks: !!decks.showDecks, - deckHasBeenSelected: decks.deckHasBeenSelected, - deckHasBeenUpdated: decks.deckHasBeenUpdated, + deckUpdated, // You can use this value in your components to trigger re-renders updateDeck, updateMultipleDecks, @@ -144,277 +182,14 @@ const useSelectedDeck = () => { }; 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, -// }; -// }); +// if (!decks.selectedId) return; +// const updatedDeck = { +// ...decks.byId[decks.selectedId], +// cards: [...decks.byId[decks.selectedId].cards, card], +// }; +// updateDeck(updatedDeck); // }, -// [setDecks] +// [decks.byId, updateDeck, decks.selectedId] // ); diff --git a/src/context/MAIN_CONTEXT/UserContext/UserContext.js b/src/context/MAIN_CONTEXT/UserContext/UserContext.js index cd28a19..6c3c7d9 100644 --- a/src/context/MAIN_CONTEXT/UserContext/UserContext.js +++ b/src/context/MAIN_CONTEXT/UserContext/UserContext.js @@ -1,98 +1,28 @@ -import React, { - createContext, - useContext, - useCallback, - useEffect, - useState, -} from 'react'; -import { useCookies } from 'react-cookie'; // Note: If not used, consider removing -import useFetchWrapper from '../../hooks/useFetchWrapper'; -import { useAuthContext } from '../AuthContext/authContext'; -import useApiResponseHandler from '../../hooks/useApiResponseHandler'; // Note: Ensure it's utilized if needed -import { defaultContextValue } from '../../constants'; -import useLocalStorage from '../../hooks/useLocalStorage'; -import { useLoading } from '../../hooks/useLoading'; -import useLogger from '../../hooks/useLogger'; +// UserProvider component +import React, { createContext, useContext } from 'react'; +import useUserData from './useUserData'; // Adjust the path as necessary +import { useCookies } from 'react-cookie'; +import useManageCookies from '../../hooks/useManageCookies'; -export const UserContext = createContext(defaultContextValue.USER_CONTEXT); +export const UserContext = createContext(); export const UserProvider = ({ children }) => { - const { userId, isLoggedIn } = useAuthContext(); // Assuming useAuthContext now provides userId directly + // const [cookies] = useCookies(['isLoggedIn', 'authUser', 'userId']); + const { getCookie } = useManageCookies(); + const { isLoggedIn, userId } = getCookie(['isLoggedIn', 'userId']); + const { user, error, hasFetchedUser, updateUser, fetchUserData } = + useUserData(); - const [user, setUser] = useLocalStorage('user', null); - const logger = useLogger('UserProvider'); - const { isLoading } = useLoading(); - const { fetchWrapper, status } = useFetchWrapper(); - const [hasFetchedUser, setHasFetchedUser] = useState(false); - const [error, setError] = useState(null); - const createApiUrl = useCallback( - (path) => `${process.env.REACT_APP_SERVER}/api/users/${userId}/${path}`, - [userId] - ); - - // Fetch user data from the server and update local storage if necessary - const fetchUserData = useCallback(async () => { - 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( - createApiUrl('userData'), - 'GET', - null, - 'fetchUserData' - ); - 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, user]); - - useEffect(() => { - if (userId && isLoggedIn && !hasFetchedUser) fetchUserData(); - }, [userId, isLoggedIn, hasFetchedUser]); - - const updateUser = useCallback( - async (updatedUserData) => { - if (!userId || !isLoggedIn || isLoading('updateUserData')) return; - try { - await fetchWrapper( - `${process.env.REACT_APP_SERVER}/api/users/${userId}/updateUserData`, - 'PUT', - updatedUserData, - 'updateUserData' - ); - fetchUserData(); // Refetch user data to ensure UI is up-to-date - setError(null); - } catch (error) { - setError('Failed to update user data'); - logger.error('Error updating user data:', error); - } - }, - [userId, isLoggedIn, fetchWrapper, fetchUserData, logger, isLoading] - ); - - const values = { + const contextValue = { + user, userId, isLoggedIn, error, hasFetchedUser, - user, updateUser, getUserData: fetchUserData, }; - const contextValue = - process.env.AUTH_ENVIRONMENT !== 'disabled' ? values : defaultContextValue; - return ( {children} ); diff --git a/src/context/MAIN_CONTEXT/UserContext/useUserData.jsx b/src/context/MAIN_CONTEXT/UserContext/useUserData.jsx new file mode 100644 index 0000000..5018a7c --- /dev/null +++ b/src/context/MAIN_CONTEXT/UserContext/useUserData.jsx @@ -0,0 +1,137 @@ +// hooks/useUserData.js +import { useCallback, useState, useEffect } from 'react'; +import useLocalStorage from '../../hooks/useLocalStorage'; +import useFetchWrapper from '../../hooks/useFetchWrapper'; +import useLogger from '../../hooks/useLogger'; +import { useCookies } from 'react-cookie'; +import useManageCookies from '../../hooks/useManageCookies'; + +function useUserData() { + const { addCookies, getCookie, deleteCookies } = useManageCookies(); + const { userId, isLoggedIn, authUser } = getCookie([ + 'userId', + 'isLoggedIn', + 'authUser', + ]); + const [user, setUser] = useLocalStorage('user', { + username: '', + userId: '', + email: '', + isLoggedIn: false, + loginStatus: false, + accessToken: '', + refreshToken: '', + userBasicData: {}, + userSecurityData: {}, + collections: [], + decks: [], + cart: { + items: [], + totalPrice: 0, + }, + }); + const logger = useLogger('useUserData'); + const { fetchWrapper } = useFetchWrapper(); + const [hasFetchedUser, setHasFetchedUser] = useState(false); + const [error, setError] = useState(null); + + const createApiUrl = useCallback( + (path) => `${process.env.REACT_APP_SERVER}/api/users/${userId}/${path}`, + [userId] + ); + + const handleSetUser = useCallback( + (user) => { + setUser(user); + setHasFetchedUser(true); + }, + [setUser, setHasFetchedUser] + ); + const handleRemoveUser = useCallback(() => { + console.log('REMOVE USER'); + setUser({ + username: '', + userId: '', + email: '', + isLoggedIn: false, + loginStatus: false, + accessToken: '', + refreshToken: '', + userBasicData: {}, + userSecurityData: {}, + collections: [], + decks: [], + cart: { + items: [], + totalPrice: 0, + }, + }); + setHasFetchedUser(false); + }, [setUser, setHasFetchedUser, logger]); + const fetchUserData = useCallback(async () => { + if (!userId || !isLoggedIn) return; + + if (user && Object.keys(user).length > 0) { + setHasFetchedUser(true); + return; + } + + try { + const responseData = await fetchWrapper(createApiUrl('userData'), 'GET'); + handleSetUser(responseData?.data); + } 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, user, fetchWrapper, setUser, logger]); + + const updateUser = useCallback( + async (updatedUserData) => { + if (!userId || !isLoggedIn) return; + + try { + const params = { + id: userId, + updatedUserData: updatedUserData, + }; + const updatedData = await fetchWrapper( + createApiUrl('updateUserData'), + 'PUT', + params + ); + + handleSetUser(updatedData?.data); + // fetchUserData(); // Refetch to ensure UI is up-to-date + } catch (error) { + console.error('Error updating user data:', error); + setError('Failed to update user data'); + logger.logError('Error updating user data:', error); + } + }, + [userId, isLoggedIn, fetchWrapper, fetchUserData, logger] + ); + + useEffect(() => { + if (userId && isLoggedIn && !hasFetchedUser) fetchUserData(); + }, [userId, isLoggedIn, hasFetchedUser, fetchUserData]); + + // useEffect(() => { + // if (authUser === null || authUser === undefined || authUser === '') { + // handleRemoveUser(); + // logger.logEvent('User logged out. Clearing user data.'); + // } + // }, []); + + return { + user, + error, + hasFetchedUser, + updateUser, + fetchUserData, + handleSetUser, + handleRemoveUser, + }; +} + +export default useUserData; diff --git a/src/context/MISC_CONTEXT/AppContext/AppContextProvider.jsx b/src/context/MISC_CONTEXT/AppContext/AppContextProvider.jsx index f5703f7..24c4035 100644 --- a/src/context/MISC_CONTEXT/AppContext/AppContextProvider.jsx +++ b/src/context/MISC_CONTEXT/AppContext/AppContextProvider.jsx @@ -6,188 +6,40 @@ import React, { useMemo, useState, } from 'react'; -import { DeckContext } from '../../MAIN_CONTEXT/DeckContext/DeckContext'; -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'; +import { useCompileCardData } from './useCompileCardData'; const AppContext = createContext(defaultContextValue.APP_CONTEXT); export const AppContextProvider = ({ children }) => { - 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 [collectionMetaData, setCollectionMetaData] = useLocalStorage( - 'collectionMetaData', - [] - ); - const { selectedCollection, allCollections } = Collection; - const { allIds } = useSelectedCollection(); - const { selectedDeck, allDecks } = Deck; - const { cartData } = Cart; - // const createMarkers = (selectedCollection) => { - // if (!selectedCollection || !selectedCollection.collectionStatistics) - // return []; - - // const { highPoint, lowPoint, avgPrice, percentageChange } = - // selectedCollection.collectionStatistics; - // return [ - // { - // axis: 'y', - // value: percentageChange, - // lineStyle: { stroke: '#b0413e', strokeWidth: 2 }, - // legend: `${selectedCollection.name} High`, - // legendOrientation: 'vertical', - // }, - // { - // axis: 'y', - // value: lowPoint, - // lineStyle: { stroke: '#b0413e', strokeWidth: 2 }, - // legend: `${selectedCollection.name} Low`, - // legendOrientation: 'vertical', - // }, - // { - // axis: 'y', - // value: avgPrice, - // lineStyle: { stroke: '#b0413e', strokeWidth: 2 }, - // legend: `${selectedCollection.name} Avg`, - // legendOrientation: 'vertical', - // }, - // ]; - // }; - // const markers = useMemo(() => { - // if (!selectedCollection) return []; - // return createMarkers(selectedCollection); - // }, [allCollections]); // Add dependencies as necessary, e.g., someSelectedCollectionId - const compileCollectionMetaData = useCallback(() => { - if (!allCollections || allCollections.length === 0) return; - - const metaData = { - totalValue: allCollections?.reduce( - (total, collection) => total + collection?.totalPrice, - 0 - ), - numCardsCollected: allCollections?.reduce((total, collection) => { - const collectionTotal = collection?.cards?.reduce( - (collectionTotal, card) => { - // Use the quantity property of each card, defaulting to 1 if not available - return collectionTotal + (card?.quantity || 1); - }, - 0 - ); - return total + collectionTotal; - }, 0), - numCollections: allIds?.length || 0, - topFiveCards: cardsWithQuantities - ?.sort((a, b) => b.price - a.price) - .slice(0, 5), - }; - - setCollectionMetaData(metaData); + const { + compileCardsWithQuantities, + isCardInContext, + collectionMetaData, + cardsWithQuantities, + compileCollectionMetaData, + } = useCompileCardData(); + useEffect(() => { + compileCollectionMetaData(); }, []); - const selectedCollectionMetaData = useMemo(() => { - if (!selectedCollection) return; - - const metaData = { - totalValue: selectedCollection?.totalPrice, - numCardsCollected: selectedCollection?.cards?.reduce((total, card) => { - // Use the quantity property of each card, defaulting to 1 if not available - return total + (card?.quantity || 1); - }, 0), - numCollections: 1, - topFiveCards: cardsWithQuantities - ?.sort((a, b) => b.price - a.price) - .slice(0, 5), - }; - - return metaData; - }, [selectedCollection, cardsWithQuantities]); - - const isCardInContext = useCallback( - (card) => { - const cardsList = { - Collection: selectedCollection?.cards, - Deck: selectedDeck?.cards, - Cart: cartData?.cart, - }; - return !!cardsList[selectedContext]?.find((c) => c?.id === card?.id); - }, - [selectedContext, selectedCollection, selectedDeck, cartData] - ); - 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; - }, []); - const combinedCards = [...deckCards, ...cartCards, ...collectionCards]; - const cardQuantities = combinedCards?.reduce((acc, card) => { - if (acc[card.id]) { - acc[card.id].quantity += card?.quantity; - } else { - acc[card.id] = { ...card, quantity: card.quantity }; - } - return acc; - }, {}); - - const quantities = Object.values(cardQuantities); - setCardsWithQuantities(combinedCards); - // setAllCardsWithQuantities(combinedCards); - - return quantities; - }; - useEffect(() => { compileCardsWithQuantities(); - }, []); // Dependency array based on when you want to recalculate - useEffect(() => { - compileCollectionMetaData(); - }, [compileCollectionMetaData]); // Re-calculate metadata when allCollections changes - + }, []); const appContextValues = useMemo( () => ({ - Deck, - Cart, - Collection, - selectedCollection, - allCollections, - selectedDeck, - allDecks, - cartData, - cardQuantities: compileCardsWithQuantities(), - cardsWithQuantities: cardsWithQuantities, - // ACTIONS - getCardQuantities: compileCardsWithQuantities, isCardInContext, checkIfCardIsInContext: isCardInContext, collectionMetaData, + cardsWithQuantities, }), [ - Deck, - Cart, - Collection, isCardInContext, collectionMetaData, - // cardsWithQuantities, - ] // Include cardsWithQuantities here + cardsWithQuantities, + compileCardsWithQuantities, + isCardInContext, + compileCollectionMetaData, + ] ); return ( diff --git a/src/context/MISC_CONTEXT/AppContext/useCompileCardData.jsx b/src/context/MISC_CONTEXT/AppContext/useCompileCardData.jsx new file mode 100644 index 0000000..d07ab6f --- /dev/null +++ b/src/context/MISC_CONTEXT/AppContext/useCompileCardData.jsx @@ -0,0 +1,124 @@ +import { useCallback, useMemo } from 'react'; +import { useCartManager } from '../../MAIN_CONTEXT/CartContext/useCartManager'; +import useSelectedCollection from '../../MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +import useSelectedDeck from '../../MAIN_CONTEXT/DeckContext/useSelectedDeck'; +import useSelectedContext from '../../hooks/useSelectedContext'; +import useLocalStorage from '../../hooks/useLocalStorage'; + +export const useCompileCardData = () => { + const { selectedDeck, allDecks } = useSelectedDeck(); + const { selectedCollection, allCollections } = useSelectedCollection(); + const { cart } = useCartManager(); + const { selectedContext } = useSelectedContext(); + const [collectionMetaData, setCollectionMetaData] = useLocalStorage( + 'collectionMetaData', + [] + ); + const [cardsWithQuantities, setCardsWithQuantities] = useLocalStorage( + 'cardsWithQuantities', + [] + ); + const [allUserCards, setAllUserCards] = useLocalStorage('allUserCards', []); + const compileCardsWithQuantities = useCallback(() => { + if (!allCollections && !allDecks && !cart) return []; + + const cards = [...(cart?.items || [])]; + allCollections.forEach((collection) => { + cards.push(...collection.cards); + }); + allDecks.forEach((deck) => { + cards.push(...deck.cards); + }); + console.log('COMPILING ALL CARDS WITH QUANTITIES', cards); + setAllUserCards(cards); + const cardQuantities = cards?.reduce((acc, card) => { + acc[card.id] = card.quantity; + return acc; + }, {}); + console.log('CARD QUANTITIES', cardQuantities); + setCardsWithQuantities(cardQuantities); + setAllUserCards(cards); + return cards; + }, [allCollections[selectedCollection?._id]?.cards]); + const isCardInContext = useCallback( + (card) => { + const cardsList = { + Collection: selectedCollection?.cards, + Deck: selectedDeck?.cards, + Cart: cart?.items, + }; + return !!cardsList[selectedContext]?.find((c) => c?.id === card?.id); + }, + [selectedContext, selectedCollection, selectedDeck, cart] + ); + const compileCollectionMetaData = useCallback(() => { + if (!allCollections || allCollections?.length === 0) return; + const cards = compileCardsWithQuantities(); + const uniqueCards = new Map(); + cards.forEach((card) => { + if (!uniqueCards.has(card.id)) { + if (card?.variant?.cardModel === 'CardInCollection') + uniqueCards.set(card.id, card); + } + }); + const uniqueCardsArray = Array.from(uniqueCards.values()); + const topFiveCardsInCollection = uniqueCardsArray + .sort((a, b) => b.price - a.price) + .slice(0, 5); + const metaData = { + totalValue: allCollections?.reduce( + (total, collection) => total + collection?.totalPrice, + 0 + ), + numCardsCollected: allCollections?.reduce((total, collection) => { + const collectionTotal = collection?.cards?.reduce( + (collectionTotal, card) => { + // Use the quantity property of each card, defaulting to 1 if not available + return collectionTotal + (card?.quantity || 1); + }, + 0 + ); + return total + collectionTotal; + }, 0), + numCollections: allCollections?.length || 0, + topFiveCards: topFiveCardsInCollection, + tooltips: [], + pieChartData: allCollections?.map((collection) => ({ + name: collection.name, + value: parseFloat(collection.totalPrice), // Ensure value is a number + })), + }; + // allCollections.reduce((meta, collection) => { + // meta.tooltips.push( + // `${collection?.name}: $${collection?.totalPrice.toFixed(2)}` + // ); + // return meta; + // }, metaData); + // allCollections.forEach((collection) => { + // // const collectionTotal = collection?.cards?.reduce( + // // (collectionTotal, card) => { + // // // Use the quantity property of each card, defaulting to 1 if not available + // // return collectionTotal + (card?.quantity || 1); + // // }, + // // 0 + // // ); + // // metaData.tooltips.push({ + // // // name: collection.name, + // // name: `${collection?.name}: $${collection?.totalPrice?.toFixed(2)}`, + // // value: collection?.totalPrice?.toFixed(2), + // // }); + // }); + + console.log('META DATA', metaData); + setCollectionMetaData(metaData); + }, [allCollections, setCollectionMetaData]); + + return { + compileCardsWithQuantities, + isCardInContext, + compileCollectionMetaData, + cardsWithQuantities, + collectionMetaData, + allUserCards, + }; +}; diff --git a/src/context/UTILITIES_CONTEXT/ColorModeContext/ColorModeProvider.jsx b/src/context/UTILITIES_CONTEXT/ColorModeContext/ColorModeProvider.jsx index 18ae30d..b535430 100644 --- a/src/context/UTILITIES_CONTEXT/ColorModeContext/ColorModeProvider.jsx +++ b/src/context/UTILITIES_CONTEXT/ColorModeContext/ColorModeProvider.jsx @@ -1,7 +1,7 @@ import { useState, useMemo, createContext, useEffect, useContext } from 'react'; import { createTheme } from '@mui/material'; -import { useCookies } from 'react-cookie'; import { themeSettings } from '../../../assets/themes/themeSettings'; +import useManageCookies from '../../hooks/useManageCookies'; export const ColorModeContext = createContext({ mode: 'dark', @@ -12,25 +12,24 @@ export const ColorModeContext = createContext({ }); export const ColorModeProvider = ({ children }) => { - // Get the mode from the cookie or default to 'dark' if the cookie doesn't exist - const [cookie, setCookie] = useCookies(['colorMode']); - const initialMode = cookie['colorMode'] || 'dark'; + const { addCookies, getCookie, deleteCookies } = useManageCookies(); + const { initialMode } = getCookie(['colorMode']) || 'dark'; const [mode, setMode] = useState(initialMode); useEffect(() => { // Set the cookie whenever the mode changes - setCookie('colorMode', mode, { path: '/' }); - }, [mode, setCookie]); + addCookies(['colorMode'], [mode], { path: '/' }); + }, [mode]); const colorMode = useMemo( () => ({ toggleColorMode: () => { const newMode = mode === 'dark' ? 'dark' : 'light'; setMode(newMode); - setCookie('colorMode', newMode); // also set the cookie here for immediate effect + addCookies(['colorMode'], [newMode], { path: '/' }); }, }), - [mode, setCookie] + [mode, addCookies] ); const theme = useMemo(() => createTheme(themeSettings(mode)), [mode]); @@ -41,7 +40,7 @@ export const ColorModeProvider = ({ children }) => { theme, setMode: (newMode) => { setMode(newMode); - setCookie('colorMode', newMode); // also set the cookie here for immediate effect + addCookies(['colorMode'], [newMode], { path: '/' }); }, toggleColorMode: colorMode.toggleColorMode, }; diff --git a/src/context/UTILITIES_CONTEXT/FormContext/FormContext.jsx b/src/context/UTILITIES_CONTEXT/FormContext/FormContext.jsx index cd2d204..3e9e467 100644 --- a/src/context/UTILITIES_CONTEXT/FormContext/FormContext.jsx +++ b/src/context/UTILITIES_CONTEXT/FormContext/FormContext.jsx @@ -17,28 +17,27 @@ import { handleZodValidation, } from './schemas'; // Update the path as necessary import { defaultContextValue } from '../../constants'; -import { ZodError } from 'zod'; -import { useAuthContext } from '../../MAIN_CONTEXT/AuthContext/authContext'; -import { useCardStoreHook } from '../../hooks/useCardStore'; -import useCollectionManager from '../../MAIN_CONTEXT/CollectionContext/useCollectionManager'; -import { useDeckStore } from '../../MAIN_CONTEXT/DeckContext/DeckContext'; -import { useChartContext } from '../../MAIN_CONTEXT/ChartContext/ChartContext'; +import { useCardStoreHook } from '../../MAIN_CONTEXT/CardContext/useCardStore'; import { useLoading } from '../../hooks/useLoading'; import useDeckManager from '../../MAIN_CONTEXT/DeckContext/useDeckManager'; +import useTimeRange from '../../../components/forms/selectors/useTimeRange'; +import useCollectionManager from '../../MAIN_CONTEXT/CollectionContext/useCollectionManager'; +import LoadingOverlay from '../../../layout/REUSABLE_COMPONENTS/LoadingOverlay'; +import useAuthManager from '../../MAIN_CONTEXT/AuthContext/useAuthManager'; const initializeFormState = (schema) => Object.keys(schema).reduce((acc, key) => ({ ...acc, [key]: false }), {}); - const FormContext = createContext(defaultContextValue.FORM_CONTEXT); - export const FormProvider = ({ children }) => { - const { signup, login, isLoggedIn, userId } = useAuthContext(); + const { signup, login } = useAuthManager(); const { handleRequest, setSearchSettings, searchSettings } = useCardStoreHook(); - const { createNewCollection, updateCollection, selectedCollection } = - useCollectionManager(); + const collectionmanagedata = useCollectionManager(); + if (!collectionmanagedata) { + return ; + } + const { createNewCollection, updateCollection } = collectionmanagedata; const { updateDeckDetails, deleteDeck, createNewDeck } = useDeckManager(); - const { setTimeRange } = useChartContext(); - + // const { setTimeRange } = useTimeRange(); // ** * * * * * * * * * * * * * * * * * * * |/\ /\ /\| ** * * * * * * * * * * * * * * * * * * * // ** || | | | || ** // ** CONTEXT ACTIONS @@ -63,7 +62,6 @@ export const FormProvider = ({ children }) => { resolver: zodResolver(formSchemas[currentSchemaKey]), defaultValues: getDefaultValuesFromSchema(formSchemas[currentSchemaKey]), }); - // Use useCallback to ensure these functions are memoized const setFormSchema = useCallback( (schemaKey) => { setCurrentSchemaKey(schemaKey); @@ -73,20 +71,27 @@ export const FormProvider = ({ children }) => { }, [methods] ); + // const setFormSchema = useCallback( + // (schemaKey) => { + // setCurrentSchemaKey(schemaKey); + // methods.reset(getDefaultValuesFromSchema(formSchemas[schemaKey])); + // }, + // [methods] + // ); const initialValues = getDefaultValuesFromSchema( formSchemas[Object.keys(formSchemas)[0]] ); const values = useRef(initialValues); const touched = useRef(initializeFormState(formSchemas)); const dirty = useRef(initializeFormState(formSchemas)); - const handleTimeRangeChange = useCallback( - (timeRangeValue) => { - console.log('TIME RANGE CHANGE', timeRangeValue); - // Example: setTimeRange could update the context state and trigger re-renders or API calls as needed - setTimeRange(timeRangeValue); - }, - [setTimeRange] - ); + // const handleTimeRangeChange = useCallback( + // (timeRangeValue) => { + // console.log('TIME RANGE CHANGE', timeRangeValue); + // // Example: setTimeRange could update the context state and trigger re-renders or API calls as needed + // setTimeRange(timeRangeValue); + // }, + // [setTimeRange] + // ); const handleFieldChange = (formId, field, value) => { setFormStates((prev) => ({ ...prev, @@ -96,11 +101,7 @@ export const FormProvider = ({ children }) => { }, })); }; - const toggleAuthMode = () => { - setCurrentSchemaKey((prevForm) => - prevForm === 'loginForm' ? 'signupForm' : 'loginForm' - ); - }; + const formHandlers = { signupForm: (formData) => signup( @@ -116,8 +117,6 @@ export const FormProvider = ({ children }) => { createNewCollection(formData, additionalData), updateCollectionForm: (formData, additionalData) => updateCollection(additionalData, formData), - createCollectionForm: (formData, additionalData) => - createNewCollection(additionalData, formData), updateDeckForm: (formData, additionalData) => updateDeckDetails(formData, additionalData), addDeckForm: (formData, additionalData) => @@ -128,8 +127,8 @@ export const FormProvider = ({ children }) => { setSearchSettings(formData, additionalData), collectionSearchForm: (formData, additionalData) => console.log(formData, additionalData), - timeRangeSelector: (formData, additionalData) => - handleTimeRangeChange(formData, additionalData), + // timeRangeSelector: (formData, additionalData) => + // handleTimeRangeChange(formData, additionalData), searchSettingsSelector: (formData, additionalData) => setSearchSettings(formData, additionalData), rememberMeForm: (formData) => { @@ -138,13 +137,14 @@ export const FormProvider = ({ children }) => { }, authSwitch: (formData) => { console.log('Auth Switch Form Data:', formData); - toggleAuthMode(); + setCurrentSchemaKey((prevForm) => + !prevForm === 'loginForm' ? 'signupForm' : 'loginForm' + ); }, }; const onSubmit = useCallback( async (formData, additionalData) => { startLoading(currentSchemaKey); - const formHandler = formHandlers[currentSchemaKey]; try { console.log( @@ -152,6 +152,7 @@ export const FormProvider = ({ children }) => { `formdata ${formData}`, `additionalData ${additionalData}` ); + await formHandler(formData); } catch (error) { console.error(`Error submitting ${currentSchemaKey}:`, error); @@ -161,7 +162,7 @@ export const FormProvider = ({ children }) => { }, [ currentSchemaKey, - handleTimeRangeChange, + // handleTimeRangeChange, setSearchSettings, startLoading, stopLoading, @@ -169,8 +170,17 @@ export const FormProvider = ({ children }) => { ] ); useEffect(() => { + console.log( + 'FormProvider: Setting up form handlers', + Object.keys(formHandlers), + 'Current Schema Key:', + currentSchemaKey + ); setFormSchema(currentSchemaKey); + // console.log('SETTING INITIAL VALUES:', initialValues); + // methods.reset(getDefaultValuesFromSchema(formSchemas[currentSchemaKey])); }, [currentSchemaKey, setFormSchema]); + // EFFECT: Setting initial values when the fo const onChange = (formData, currentSchemaKey) => { console.log('Form data changed:', formData, currentSchemaKey); const validation = handleZodValidation( @@ -182,10 +192,6 @@ export const FormProvider = ({ children }) => { console.log('Form data is valid:', validation.data); handleRequest(validation.data.searchTerm); // Use handleRequest for the search form } - if (formData === false) { - console.log('Signup Mode:', validation.data.signupMode); - toggleAuthMode(); - } }; const handleSetAllForms = useCallback(() => { const allFormConfigs = Object.keys(formSchemas).map((formId) => ({ @@ -194,6 +200,7 @@ export const FormProvider = ({ children }) => { schema: formSchemas[formId], })); console.log('Setting all forms:', allFormConfigs); + setForms(allFormConfigs); }, []); const handleSearchTermChange = useCallback( @@ -236,15 +243,14 @@ export const FormProvider = ({ children }) => { dirty.current[name] = true; console.log('Form field blurred:', name); }, []); + // EFFECT: Setting all forms when the component mounts useEffect(() => { console.log('SETTING ALL FORMS:', formSchemas); handleSetAllForms(formSchemas); }, [handleSetAllForms]); - useEffect(() => { methods.reset(getDefaultValuesFromSchema(formSchemas[currentSchemaKey])); }, [currentSchemaKey, methods]); - const setInitialValues = useCallback( (newInitialValues) => { if (newInitialValues) { @@ -254,10 +260,27 @@ export const FormProvider = ({ children }) => { }, [methods] ); - - // Ensure initial values are set when they change + // EFFECT: When the currentSchemaKey changes, reset the form state // useEffect(() => { - // if (initialValues ) { + // console.log( + // 'FormProvider: Setting up form handlers', + // Object.keys(formHandlers), + // 'Current Schema Key:', + // currentSchemaKey + // ); + // setFormSchema(currentSchemaKey); + // // console.log('SETTING INITIAL VALUES:', initialValues); + // // methods.reset(getDefaultValuesFromSchema(formSchemas[currentSchemaKey])); + // }, [currentSchemaKey, setFormSchema]); + // // EFFECT: Setting initial values when the form schema changes + // useEffect(() => { + // console.log('SETTING INITIAL VALUES:', initialValues); + // methods.reset(getDefaultValuesFromSchema(formSchemas[currentSchemaKey])); + // }, [currentSchemaKey, methods]); + // EFFECT: When the initialValues prop changes, update the form state + // useEffect(() => { + // if (initialValues) { + // console.log('Setting initial values:', initialValues); // setInitialValues(initialValues); // } // }, [initialValues, setInitialValues]); @@ -275,7 +298,7 @@ export const FormProvider = ({ children }) => { currentForm: currentSchemaKey, forms, getValues: methods.getValues, - handleTimeRangeChange, + // handleTimeRangeChange, handleSearchTermChange, handleFieldChange, handleChange, @@ -302,7 +325,7 @@ export const FormProvider = ({ children }) => { formSchemas, isFormDataLoading, - handleTimeRangeChange, + // handleTimeRangeChange, handleSearchTermChange, handleFieldChange, handleChange, @@ -323,134 +346,3 @@ export const FormProvider = ({ children }) => { }; export const useFormContext = () => useContext(FormContext); -// switch (formId) { -// case 'signupForm': -// await signup( -// formData.firstName, -// formData.lastName, -// formData.username, -// formData.password, -// formData.email -// ); -// break; -// case 'loginForm': -// await login(formData.username, formData.password); -// break; -// case 'updateUserDataForm': -// // await updateUserData(formData); -// break; -// case 'updateCollectionForm': -// await updateAndSyncCollection(additionalData, formData); -// break; -// case 'addCollectionForm': -// await createNewCollection(formData); -// break; -// case 'updateDeckForm': -// await updateDeckDetails(formData); -// break; -// case 'addDeckForm': -// await createUserDeck(formData.name, formData.description); -// break; -// case 'searchForm': -// await handleRequest(formData.searchTerm); -// break; -// case 'timeRangeSelector': -// console.log('Time range selected:', formData.timeRange); -// handleTimeRangeChange(formData.timeRange); // Directly use formData.timeRange -// break; -// case 'searchSettingsSelector': -// setSearchSettings(formData); -// break; -// default: -// console.error('Unhandled form submission:', formId); -// } -// Function to dynamically set the current form and its schema -// const setCurrentForm = useCallback((formId) => { -// setCurrentFormId(formId); -// }, []); -// const useFormMethods = useCallback((formId) => { -// const schema = formSchemas[formId] || formSchemas.default; -// return useForm({ -// resolver: zodResolver(schema), -// defaultValues: schema.safeParse({}), // Assuming you have a parse method to get default values from Zod schema -// }); -// }, []); -// Initialize useForm with the first form schema as default -// const initialFormKey = Object.keys(formSchemas)[0]; -// const methods = useForm({ -// resolver: zodResolver(formSchemas[initialFormKey]), -// defaultValues: getDefaultValuesFromSchema(formSchemas[initialFormKey]), -// }); -// const [forms, setForms] = useState({}); - -// const [currentForm, setCurrentForm] = useState({}); -// const onSubmit = useCallback(async (formData, formId, additionalData) => { - -// setIsFormDataLoading(true); -// // const validation = handleZodValidation(formData, formSchemas[formId]); -// // console.log('isValid:', validation.isValid); -// try { - -// try { -// if (formId === 'signupForm') { -// console.log('Submitting signup form:', formData); -// await signup( -// formData.firstName, -// formData.lastName, -// formData.username, -// formData.password, -// formData.email -// ); -// } else if (formId === 'loginForm') { -// console.log('Submitting login form:', formData); -// await login(formData.username, formData.password); -// } else if (formId === 'updateUserDataForm') { -// console.log('Submitting update user data form:', formData); -// // await updateUserData(formData); -// } else if (formId === 'updateCollectionForm') { -// console.log('Updating collection:', formData); -// console.log('Selected collection:', additionalData); - -// const updatedData = { -// name: formData.name, -// description: formData.description, -// }; -// await updateAndSyncCollection(additionalData, updatedData); -// } else if (formId === 'addCollectionForm') { -// console.log('Adding collection:', formData); -// const newData = { -// name: formData.name, -// description: formData.description, -// }; -// await createNewCollection(newData); -// } else if (formId === 'updateDeckForm') { -// console.log('Updating deck:', formData); -// await updateDeckDetails(formData); -// } else if (formId === 'addDeckForm') { -// console.log('Adding deck:', formData); -// await createUserDeck(formData.name, formData.description); -// } else if (formId === 'searchForm') { -// console.log('Submitting search form:', formData); -// await handleRequest(formData.searchTerm); // Use handleRequest for the search form -// } else if (formId === 'TimeRangeSchema') { -// console.log('Submitting TimeRange form:', formData); -// setTimeRange(formData.timeRange); -// // handleTimeRangeChange(formData); -// } else if (formId === 'searchSettingsForm') { -// console.log('Submitting SearchSettings form:', formData); -// setSearchSettings(formData); -// } else if (formId === 'defaultForm') { -// console.log('Submitting default form:', formData); -// } -// console.log(`${formId} form submitted successfully`, formData); -// // Reset form logic here if needed -// } catch (error) { -// console.error('Error submitting form:', error); -// } finally { -// setIsFormDataLoading(false); -// } -// } else { -// console.error('Form validation failed:', validation.errors); -// // Optionally, display validation.errors using your UI logic -// setIsFormDataLoading(false); -// } diff --git a/src/context/UTILITIES_CONTEXT/FormContext/helpers.jsx b/src/context/UTILITIES_CONTEXT/FormContext/helpers.jsx deleted file mode 100644 index 4a40489..0000000 --- a/src/context/UTILITIES_CONTEXT/FormContext/helpers.jsx +++ /dev/null @@ -1,227 +0,0 @@ -import { z } from 'zod'; - -/* eslint-disable @typescript-eslint/no-empty-function */ -// export const defaultContextValue = { -// forms: {}, -// formErrors: {}, -// initialFormStates: {}, -// currentForm: {}, -// schemas: {}, -// currentFormType: '', -// errors: {}, -// setForms: () => {}, -// setFormErrors: () => {}, -// setCurrentForm: () => {}, -// handleChange: () => {}, -// handleSubmit: () => {}, -// resetForm: () => {}, -// handleRequest: () => {}, -// register: () => {}, -// setFormType: () => {}, -// setCurrentFormType: () => {}, -// }; - -// Utility function to set value at a given path in an object -export const setValueAtPath = (obj, path, value) => { - const keys = path.split('.'); - let current = obj; - for (let i = 0; i < keys.length - 1; i++) { - current[keys[i]] = current[keys[i]] || {}; - current = current[keys[i]]; - } - current[keys[keys.length - 1]] = value; -}; - -// Initial state for different forms -// export const schemas = { -// loginForm: z.object({ -// securityData: z.object({ -// username: z.string(), -// password: z.string(), -// email: z.string(), -// role_data: z.object({ -// name: z.string(), -// capabilities: z.array(z.string()), -// }), -// }), -// basicData: z.object({ -// firstName: z.string(), -// lastName: z.string(), -// }), -// }), -// signupForm: z.object({ -// securityData: z.object({ -// username: z.string(), -// password: z.string(), -// email: z.string(), -// role_data: z.object({ -// name: z.string(), -// capabilities: z.array(z.string()), -// }), -// }), -// basicData: z.object({ -// firstName: z.string(), -// lastName: z.string(), -// }), -// }), -// updateUserDataForm: z.object({ -// firstName: z.string(), -// lastName: z.string(), -// email: z.string(), -// phone: z.string(), -// role_data: z.object({ -// name: z.string(), -// capabilities: z.array(z.string()), -// }), -// dateOfBirth: z.string(), -// gender: z.string(), -// age: z.string(), -// status: z.string(), -// signupMode: z.boolean(), -// }), -// collectionForm: z.object({ -// updateCollectionForm: z.object({ -// name: z.string(), -// description: z.string(), -// }), -// addCollectionForm: z.object({ -// name: z.string(), -// description: z.string(), -// }), -// }), -// searchForm: z.object({ -// searchTerm: z.string(), -// searchParams: z.object({ -// name: z.string(), -// type: z.string(), -// race: z.string(), -// archetype: z.string(), -// attribute: z.string(), -// level: z.string(), -// }), -// }), -// customerInfoFields: z.object({ -// firstName: z.string(), -// lastName: z.string(), -// address: z.object({ -// line1: z.string(), -// line2: z.string(), -// city: z.string(), -// state: z.string(), -// zip: z.string(), -// country: z.string(), -// }), -// timezone: z.string(), -// phone: z.string(), -// email: z.string(), -// }), -// }; - -// loginForm: { -// securityData: { -// username: '', -// password: '', -// email: '', -// role_data: { -// name: 'admin', -// capabilities: ['create', 'read', 'update', 'delete'], -// }, -// }, -// basicData: { -// firstName: '', -// lastName: '', -// }, -// }, -// signupForm: { -// securityData: { -// username: '', -// password: '', -// email: '', -// role_data: { -// name: 'admin', -// capabilities: ['create', 'read', 'update', 'delete'], -// }, -// }, -// basicData: { -// firstName: '', -// lastName: '', -// }, -// }, -// updateUserDataForm: { -// firstName: '', -// lastName: '', -// email: '', -// phone: '', -// role_data: { -// name: 'admin', -// capabilities: ['create', 'read', 'update', 'delete'], -// }, -// dateOfBirth: '', -// gender: '', -// age: '', -// status: '', -// signupMode: false, -// }, -// collectionForm: { -// updateCollectionForm: { -// name: '', -// description: '', -// }, -// addCollectionForm: { -// // New form with same initial values as updateCollectionForm -// name: '', -// description: '', -// }, -// }, -// searchForm: { -// searchTerm: '', -// searchParams: { -// name: '', -// type: '', -// race: '', -// archetype: '', -// attribute: '', -// level: '', -// }, -// }, -// customerInfoFields: { -// firstName: '', -// lastName: '', -// address: { -// line1: { type: String }, -// line2: { type: String }, -// city: { type: String }, -// state: { type: String }, -// zip: { type: String }, -// country: { type: String }, -// }, -// timezone: { type: String }, -// phone: { type: String }, -// email: { type: String }, -// }, -// securityData: { -// username: '', -// password: '', -// email: '', -// role_data: { -// name: 'admin', -// capabilities: ['create', 'read', 'update', 'delete'], -// }, -// }, -// basicData: { -// firstName: '', -// lastName: '', -// }, -// searchData: { -// searchTerm: '', -// searchParams: { -// name: '', -// race: '', -// type: '', -// level: '', -// rarity: '', -// attribute: '', -// id: '', -// }, -// }, -// ...other form types as needed diff --git a/src/context/UTILITIES_CONTEXT/SideBarContext/SideBarProvider.jsx b/src/context/UTILITIES_CONTEXT/SideBarContext/SideBarProvider.jsx index 26b91ba..d254b8b 100644 --- a/src/context/UTILITIES_CONTEXT/SideBarContext/SideBarProvider.jsx +++ b/src/context/UTILITIES_CONTEXT/SideBarContext/SideBarProvider.jsx @@ -1,15 +1,22 @@ import React, { createContext, useContext, useState } from 'react'; -import { useAuthContext } from '../../MAIN_CONTEXT/AuthContext/authContext'; import HomeIcon from '@mui/icons-material/Home'; 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 '../../../layout/REUSABLE_COMPONENTS/icons/DeckBuilderIcon'; +import useManageCookies from '../../hooks/useManageCookies'; +import useAuthManager from '../../MAIN_CONTEXT/AuthContext/useAuthManager'; const SidebarContext = createContext(); export const SidebarProvider = ({ children }) => { - const { login, logout, isLoggedIn } = useAuthContext(); + const { addCookie, getCookie, deleteCookie } = useManageCookies(); + const { isLoggedIn, authUser, userId } = getCookie([ + 'isLoggedIn', + 'authUser', + 'userId', + ]); + const { login, logout } = useAuthManager(); // TOPBAR const [visibleItems, setVisibleItems] = useState([]); const baseMenuItems = [ diff --git a/src/context/constants.jsx b/src/context/constants.jsx index b511d2a..abf555c 100644 --- a/src/context/constants.jsx +++ b/src/context/constants.jsx @@ -2,7 +2,7 @@ import { createNewPriceEntry } from './Helpers'; import jsonData from '../data/nivoTestData.json'; const { nivoTestData } = jsonData; -import user from './user'; +import user from '../data/user'; // import { lightTheme, indigoTheme } from '../assets/themes/colors'; // ! DEFAULT VALUES FOR CARD OBJECTS @@ -114,7 +114,6 @@ const defaultCollectionStatistics = { }, }, }; - // const defaultPriceEntry = { // num: 0, // timestamp: new Date(), @@ -220,7 +219,6 @@ const createDefaultCard = () => ({ // Example usage const defaultCard = createDefaultCard(); console.log(defaultCard); - const defaultCollection = { name: '', description: '', @@ -294,6 +292,7 @@ const defaultCollection = { averagedChartData: new Map(), muiChartData: [], cards: [], + _id: '', addDefaultCard: function () { const newCard = createDefaultCard(); this.cards.push(newCard); @@ -309,6 +308,7 @@ const defaultCollection = { Array.from({ length: numberOfCards }).forEach(() => this.addDefaultCard()); }, }; +console.log(defaultCollection); // ! DEFAULT VALUES FOR DECK OBJECTS const defaultDeck = { diff --git a/src/context/hooks/useCounter.jsx b/src/context/hooks/generic/useCounter.jsx similarity index 97% rename from src/context/hooks/useCounter.jsx rename to src/context/hooks/generic/useCounter.jsx index 9f95c48..9d05327 100644 --- a/src/context/hooks/useCounter.jsx +++ b/src/context/hooks/generic/useCounter.jsx @@ -1,5 +1,5 @@ import { useState, useEffect, useRef } from 'react'; -import useLogger from './useLogger'; // Assuming you have a useLogger hook for logging +import useLogger from '../useLogger'; // Assuming you have a useLogger hook for logging import { isEqual } from 'lodash'; const useCounter = (inputData, options = {}) => { diff --git a/src/context/hooks/useDebounce.jsx b/src/context/hooks/generic/useDebounce.jsx similarity index 100% rename from src/context/hooks/useDebounce.jsx rename to src/context/hooks/generic/useDebounce.jsx diff --git a/src/context/hooks/useIsFirstRender.jsx b/src/context/hooks/generic/useIsFirstRender.jsx similarity index 100% rename from src/context/hooks/useIsFirstRender.jsx rename to src/context/hooks/generic/useIsFirstRender.jsx diff --git a/src/context/hooks/index.jsx b/src/context/hooks/index.jsx deleted file mode 100644 index db26ec4..0000000 --- a/src/context/hooks/index.jsx +++ /dev/null @@ -1,193 +0,0 @@ -// Import custom hooks -import useFetch from './useFetch'; -import useFetchWrapper from './useFetchWrapper'; -import useMap from './useMap'; -import useIdle from './useIdle'; -// import useCounter from './useCounter'; -import useList from './useList'; -import useObjectState from './useObjectState'; -import usePrevious from './usePrevious'; -import usePagination from './usePagination'; -import useSet from './useSet'; -import useToggle from './useToggle'; -import useSnackBar from './useSnackBar'; -import { useVisible } from './useVisible'; -import useLogger from './useLogger'; -import useDialog from './useDialog'; -import useApiResponseHandler from './useApiResponseHandler'; -import useRenderCount from './useRenderCount'; -import useLocalStorage from './useLocalStorage'; - -/** - * Hook: useLogger - * Description: Logs component lifecycle events, state changes, and custom events. - * Useful for debugging, monitoring, or performance optimization. - * @param {string} componentName - The name of the component. - * @param {object} [options] - Optional settings for the logger. - * @returns {Function} - Function to log custom events. - */ -const { logEvent, setStateAndLog } = useLogger('ComponentName', {}); - -/** - * Hook: useLocalStorage - * Description: Manages a state value in local storage. - * Useful for persisting data across sessions or pages. - * @param {string} key - The key to store the value in local storage. - * @param {*} initialValue - The initial value to set. - * @returns {[value, setValue]} - The stored value and a function to update it. - * Used By: - * @function CardProvider - src/context/hooks/useCardStore.jsx - */ -// const [value, setValue] = useLocalStorage('key', 'initialValue'); -/** - * Hook: useFetch - * Description: Manages data fetching with loading and error states. - * Used for fetching data from an API and automatically updates the component when the data is retrieved. - * @param {string} url - The URL to fetch data from. - * @returns { - * data: Object, - * loading: boolean, - * error: string - * } - * Used By: - * @function CollectionForm - src/components/forms/CollectionForm.jsx - * - src/components/forms/CollectionForm.jsx - */ -const { data, loading, error } = useFetch('API_URL'); - -/** - * Hook: useFetchWrapper - * Description: Manages data fetching with loading and error states. - * Used for fetching data from an API and automatically updates the component when the data is retrieved. - * @param {string} url - The URL to fetch data from. - * @param {string} method - The HTTP method to use for the request. - * @returns {JSON} - The response data from the API. - * Used By: - * @function CollectionForm - src/components/forms/CollectionForm.jsx - */ -const fetchWrapper = useFetchWrapper(); - -/** - * Hook: useMap - * Description: Provides functionalities to manipulate map object. - * Useful for managing key-value pairs with add, delete, and clear operations. - */ -const { map, setMap, deleteKey, clear, get, has } = useMap(); - -/** - * Hook: useIdle - * Description: Detects user inactivity over a specified duration. - * Can trigger actions like auto-logout or UI changes when the user is idle. - * @param {number} timeout - The timeout in milliseconds to consider the user idle. - * @param {function} onIdle - The function to call when the user is idle. - * @returns {boolean} - The idle state of the user. - */ -const { isIdle } = useIdle(1000, () => console.log('User is idle!')); - -/** - * Hook: useCounter - * Description: Manages a counter state with increment, decrement, and reset functionalities. - * Ideal for counters, numeric inputs, or pagination components. - */ -// const { count, increment, decrement, reset } = useCounter(); -/** - * Hook: useList - * Description: Manages list states with add, remove, and update functionalities. - * Useful for dynamic lists or arrays in your UI, like a to-do list. - */ -const { list, add, remove, update, clearList } = useList(); - -/** - * Hook: useObjectState - * Description: Manages an object state with nested object support. - * Useful for managing complex states with multiple values. - * @returns { - * objectState: Object - The current state object. - * setObjectState: Function - Function to update the state object. - * setNestedState: Function - Function to update a nested state. - * } - */ - -const [objectState, setObjectState, setNestedState] = useObjectState(); - -/** - * Hook: usePrevious - * Description: Keeps track of the previous value of a state or prop. - * Useful in comparison scenarios or detecting changes in values over time. - * @param {*} value - The value to track. - */ -const { previousValue } = usePrevious(); - -/** - * Hook: usePagination - * Description: Manages pagination logic like current page and items per page. - * Ideal for implementing pagination on lists or data fetched from APIs. - */ -const { currentPage, itemsPerPage, setItemsPerPage, setCurrentPage } = - usePagination(); - -/** - * Hook: useSet - * Description: Manages a Set with functionalities like add, delete, and clear. - * Useful for handling unique values, like in filtering or selection scenarios. - */ -const { set, addSetItem, removeSetItem, clearSet } = useSet(); - -/** - * Hook: useToggle - * Description: Toggles a boolean state, useful for handling binary states like open/close, show/hide. - * Commonly used in modals, dropdowns, or toggling UI elements. - */ -const [isToggled, toggle] = useToggle(); - -/** - * Hook: useSnackBar - * Description: Manages snack bar notifications in the application. - * Useful for showing temporary messages or alerts to the user. - */ -// const { openSnackBar, closeSnackBar } = useSnackBar(); - -/** - * Hook: useVisible - * Description: Manages visibility states for various components or sections. - * Allows setting visibility individually for named elements. - * @returns { - * isVisible: Function - Function to check if a named element is visible. - * show: Function - Function to show a named element. - * hide: Function - Function to hide a named element. - * toggle: Function - Function to toggle the visibility of a named element. - * } - * Example: - * const { isVisible, show, hide, toggleVisible } = useVisible(); - */ -const { isVisible, show, hide, toggleVisible } = useVisible(); - -/** - * Hook: useDialog - * Description: Manages dialog states for various components or sections. - * Allows setting visibility individually for named elements. - * @returns { - * openDialog: Function - Function to open a named dialog. - * closeDialog: Function - Function to close a named dialog. - * isDialogOpen: Function - Function to check if a named dialog is open. - * } - */ - -/** - * Hook: useApiResponseHandler - * Description: Handles API response data and logs the details. - * Useful for handling API responses and logging the details to the console. - * @returns { - * handleApiResponse: Function - Function to handle the API response. - * } - */ - -/** - * Hook: useRenderCount - * Description: Keeps track of the number of times a component has rendered. - * Useful for performance optimization and debugging. - * @returns { - * renderCount: number - The number of times the component has rendered. - * } - */ -const renderCount = useRenderCount(); diff --git a/src/context/hooks/oldhooks/index.jsx b/src/context/hooks/oldhooks/index.jsx new file mode 100644 index 0000000..6f8cdbf --- /dev/null +++ b/src/context/hooks/oldhooks/index.jsx @@ -0,0 +1,193 @@ +// // Import custom hooks +// import useFetch from './useFetch'; +// import useFetchWrapper from './useFetchWrapper'; +// import useMap from './useMap'; +// import useIdle from './useIdle'; +// // import useCounter from './useCounter'; +// import useList from './useList'; +// import useObjectState from './useObjectState'; +// import usePrevious from './usePrevious'; +// import usePagination from './usePagination'; +// import useSet from './useSet'; +// import useToggle from './useToggle'; +// import useSnackBar from './useSnackBar'; +// import { useVisible } from './useVisible'; +// import useLogger from './useLogger'; +// import useDialog from './useDialog'; +// import useApiResponseHandler from './useApiResponseHandler'; +// import useRenderCount from './useRenderCount'; +// import useLocalStorage from './useLocalStorage'; + +// /** +// * Hook: useLogger +// * Description: Logs component lifecycle events, state changes, and custom events. +// * Useful for debugging, monitoring, or performance optimization. +// * @param {string} componentName - The name of the component. +// * @param {object} [options] - Optional settings for the logger. +// * @returns {Function} - Function to log custom events. +// */ +// const { logEvent, setStateAndLog } = useLogger('ComponentName', {}); + +// /** +// * Hook: useLocalStorage +// * Description: Manages a state value in local storage. +// * Useful for persisting data across sessions or pages. +// * @param {string} key - The key to store the value in local storage. +// * @param {*} initialValue - The initial value to set. +// * @returns {[value, setValue]} - The stored value and a function to update it. +// * Used By: +// * @function CardProvider - src/context/hooks/useCardStore.jsx +// */ +// // const [value, setValue] = useLocalStorage('key', 'initialValue'); +// /** +// * Hook: useFetch +// * Description: Manages data fetching with loading and error states. +// * Used for fetching data from an API and automatically updates the component when the data is retrieved. +// * @param {string} url - The URL to fetch data from. +// * @returns { +// * data: Object, +// * loading: boolean, +// * error: string +// * } +// * Used By: +// * @function CollectionForm - src/components/forms/CollectionForm.jsx +// * - src/components/forms/CollectionForm.jsx +// */ +// const { data, loading, error } = useFetch('API_URL'); + +// /** +// * Hook: useFetchWrapper +// * Description: Manages data fetching with loading and error states. +// * Used for fetching data from an API and automatically updates the component when the data is retrieved. +// * @param {string} url - The URL to fetch data from. +// * @param {string} method - The HTTP method to use for the request. +// * @returns {JSON} - The response data from the API. +// * Used By: +// * @function CollectionForm - src/components/forms/CollectionForm.jsx +// */ +// const fetchWrapper = useFetchWrapper(); + +// /** +// * Hook: useMap +// * Description: Provides functionalities to manipulate map object. +// * Useful for managing key-value pairs with add, delete, and clear operations. +// */ +// const { map, setMap, deleteKey, clear, get, has } = useMap(); + +// /** +// * Hook: useIdle +// * Description: Detects user inactivity over a specified duration. +// * Can trigger actions like auto-logout or UI changes when the user is idle. +// * @param {number} timeout - The timeout in milliseconds to consider the user idle. +// * @param {function} onIdle - The function to call when the user is idle. +// * @returns {boolean} - The idle state of the user. +// */ +// const { isIdle } = useIdle(1000, () => console.log('User is idle!')); + +// /** +// * Hook: useCounter +// * Description: Manages a counter state with increment, decrement, and reset functionalities. +// * Ideal for counters, numeric inputs, or pagination components. +// */ +// // const { count, increment, decrement, reset } = useCounter(); +// /** +// * Hook: useList +// * Description: Manages list states with add, remove, and update functionalities. +// * Useful for dynamic lists or arrays in your UI, like a to-do list. +// */ +// const { list, add, remove, update, clearList } = useList(); + +// /** +// * Hook: useObjectState +// * Description: Manages an object state with nested object support. +// * Useful for managing complex states with multiple values. +// * @returns { +// * objectState: Object - The current state object. +// * setObjectState: Function - Function to update the state object. +// * setNestedState: Function - Function to update a nested state. +// * } +// */ + +// const [objectState, setObjectState, setNestedState] = useObjectState(); + +// /** +// * Hook: usePrevious +// * Description: Keeps track of the previous value of a state or prop. +// * Useful in comparison scenarios or detecting changes in values over time. +// * @param {*} value - The value to track. +// */ +// const { previousValue } = usePrevious(); + +// /** +// * Hook: usePagination +// * Description: Manages pagination logic like current page and items per page. +// * Ideal for implementing pagination on lists or data fetched from APIs. +// */ +// const { currentPage, itemsPerPage, setItemsPerPage, setCurrentPage } = +// usePagination(); + +// /** +// * Hook: useSet +// * Description: Manages a Set with functionalities like add, delete, and clear. +// * Useful for handling unique values, like in filtering or selection scenarios. +// */ +// const { set, addSetItem, removeSetItem, clearSet } = useSet(); + +// /** +// * Hook: useToggle +// * Description: Toggles a boolean state, useful for handling binary states like open/close, show/hide. +// * Commonly used in modals, dropdowns, or toggling UI elements. +// */ +// const [isToggled, toggle] = useToggle(); + +// /** +// * Hook: useSnackBar +// * Description: Manages snack bar notifications in the application. +// * Useful for showing temporary messages or alerts to the user. +// */ +// // const { openSnackBar, closeSnackBar } = useSnackBar(); + +// /** +// * Hook: useVisible +// * Description: Manages visibility states for various components or sections. +// * Allows setting visibility individually for named elements. +// * @returns { +// * isVisible: Function - Function to check if a named element is visible. +// * show: Function - Function to show a named element. +// * hide: Function - Function to hide a named element. +// * toggle: Function - Function to toggle the visibility of a named element. +// * } +// * Example: +// * const { isVisible, show, hide, toggleVisible } = useVisible(); +// */ +// const { isVisible, show, hide, toggleVisible } = useVisible(); + +// /** +// * Hook: useDialog +// * Description: Manages dialog states for various components or sections. +// * Allows setting visibility individually for named elements. +// * @returns { +// * openDialog: Function - Function to open a named dialog. +// * closeDialog: Function - Function to close a named dialog. +// * isDialogOpen: Function - Function to check if a named dialog is open. +// * } +// */ + +// /** +// * Hook: useApiResponseHandler +// * Description: Handles API response data and logs the details. +// * Useful for handling API responses and logging the details to the console. +// * @returns { +// * handleApiResponse: Function - Function to handle the API response. +// * } +// */ + +// /** +// * Hook: useRenderCount +// * Description: Keeps track of the number of times a component has rendered. +// * Useful for performance optimization and debugging. +// * @returns { +// * renderCount: number - The number of times the component has rendered. +// * } +// */ +// const renderCount = useRenderCount(); diff --git a/src/context/hooks/oldhooks/useApiResponseHandler.jsx b/src/context/hooks/oldhooks/useApiResponseHandler.jsx new file mode 100644 index 0000000..d187fc6 --- /dev/null +++ b/src/context/hooks/oldhooks/useApiResponseHandler.jsx @@ -0,0 +1,22 @@ +// import useLogger from './useLogger'; + +// const useApiResponseHandler = () => { +// const logger = useLogger('ApiResponseHandler'); + +// const handleApiResponse = (response, functionName) => { +// const { message, data } = response; + +// // Log the details using the logger +// logger.logEvent('apiResponse:', { +// message: message, +// data: data, +// source: functionName, +// }); + +// return data; +// }; + +// return handleApiResponse; +// }; + +// export default useApiResponseHandler; diff --git a/src/context/hooks/oldhooks/useCardActions.jsx b/src/context/hooks/oldhooks/useCardActions.jsx new file mode 100644 index 0000000..b0eacfb --- /dev/null +++ b/src/context/hooks/oldhooks/useCardActions.jsx @@ -0,0 +1,113 @@ +// import { useCallback, useState } from 'react'; +// import useCounter from '../useCounter'; +// import useDialog from './useDialog'; + +// export const useCardActions = ( +// context, +// card, +// selectedCollection, +// selectedDeck, +// addOneToCollection, +// removeOneFromCollection, +// addOneToDeck, +// removeOneFromDeck, +// addOneToCart, +// removeOneFromCart, +// onSuccess, +// onFailure, +// page +// ) => { +// const { openDialog } = useDialog(); // Destructure openDialog from useDialog hook +// const [isDialogOpen, setIsDialogOpen] = useState(false); + +// const closeDialog = useCallback(() => setIsDialogOpen(false), []); // Close dialog function + +// const { data, increment, decrement } = useCounter(card, context, { +// max: page === 'DeckBuilder' ? 3 : undefined, // Limit to 3 for Deck context +// }); + +// /** @internal Place an object in an array */ +// const placeObjectInArray = useCallback((object) => { +// const objectInArray = [object]; +// return objectInArray; +// }, []); + +// const performAction = useCallback( +// (action) => { +// const updateQuantity = (actionType) => { +// actionType === 'add' ? increment(card.id) : decrement(card.id); +// }; +// const array = placeObjectInArray(data); +// console.log('DATA =>=>=> ', data); +// const actionFunctions = { +// Collection: { +// add: () => { +// console.log('array === ', array); +// updateQuantity('add'); +// addOneToCollection(array, selectedCollection); +// }, +// remove: () => { +// updateQuantity('remove'); +// removeOneFromCollection( +// placeObjectInArray(data), +// placeObjectInArray(data?.id), +// selectedCollection +// ); +// }, +// }, +// Deck: { +// add: () => { +// updateQuantity('add'); +// addOneToDeck(data, selectedDeck); +// }, +// remove: () => { +// updateQuantity('remove'); +// removeOneFromDeck(data, selectedDeck); +// }, +// }, +// Cart: { +// add: () => { +// updateQuantity('add'); +// addOneToCart(data); +// }, +// remove: () => { +// updateQuantity('remove'); +// removeOneFromCart(data); +// }, +// }, +// }; + +// try { +// actionFunctions[context][action](); +// onSuccess?.(); +// } catch (error) { +// onFailure?.(error); +// } +// }, +// [ +// addOneToCart, +// addOneToCollection, +// addOneToDeck, +// context, +// data, +// decrement, +// increment, +// onFailure, +// openDialog, +// page, +// placeObjectInArray, +// removeOneFromCart, +// removeOneFromCollection, +// removeOneFromDeck, +// selectedCollection, +// selectedDeck, +// onSuccess, +// ] +// ); + +// return { +// count: data?.quantity, +// performAction, +// closeDialog, +// }; +// }; diff --git a/src/layout/useCardCronJob.jsx b/src/context/hooks/oldhooks/useCardCronJob.jsx similarity index 100% rename from src/layout/useCardCronJob.jsx rename to src/context/hooks/oldhooks/useCardCronJob.jsx diff --git a/src/context/hooks/useDialog.jsx b/src/context/hooks/oldhooks/useDialog.jsx similarity index 100% rename from src/context/hooks/useDialog.jsx rename to src/context/hooks/oldhooks/useDialog.jsx diff --git a/src/context/hooks/useApiResponseHandler.jsx b/src/context/hooks/useApiResponseHandler.jsx deleted file mode 100644 index 2f3ae9a..0000000 --- a/src/context/hooks/useApiResponseHandler.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import useLogger from './useLogger'; - -const useApiResponseHandler = () => { - const logger = useLogger('ApiResponseHandler'); - - const handleApiResponse = (response, functionName) => { - const { message, data } = response; - - // Log the details using the logger - logger.logEvent('apiResponse:', { - message: message, - data: data, - source: functionName, - }); - - return data; - }; - - return handleApiResponse; -}; - -export default useApiResponseHandler; diff --git a/src/context/hooks/useAuthDialog.jsx b/src/context/hooks/useAuthDialog.jsx deleted file mode 100644 index 336305a..0000000 --- a/src/context/hooks/useAuthDialog.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useCallback, useState } from 'react'; -import { useAuthContext } from '../MAIN_CONTEXT/AuthContext/authContext'; -import useDialog from './useDialog'; - -const useAuthDialog = () => { - const { isLoggedIn, logout } = useAuthContext(); - const { openDialog, closeDialog } = useDialog(); - - // Toggle login dialog based on authentication status - const toggleLoginDialog = useCallback(() => { - if (!isLoggedIn) { - openDialog('Login'); - } else { - logout(); - closeDialog('Login'); - } - }, [isLoggedIn, openDialog, closeDialog, logout]); - - return { toggleLoginDialog, isLoggedIn, logout }; -}; - -export default useAuthDialog; diff --git a/src/context/hooks/useCardActions.jsx b/src/context/hooks/useCardActions.jsx deleted file mode 100644 index 6706f62..0000000 --- a/src/context/hooks/useCardActions.jsx +++ /dev/null @@ -1,113 +0,0 @@ -import { useCallback, useState } from 'react'; -import useCounter from './useCounter'; -import useDialog from './useDialog'; - -export const useCardActions = ( - context, - card, - selectedCollection, - selectedDeck, - addOneToCollection, - removeOneFromCollection, - addOneToDeck, - removeOneFromDeck, - addOneToCart, - removeOneFromCart, - onSuccess, - onFailure, - page -) => { - const { openDialog } = useDialog(); // Destructure openDialog from useDialog hook - const [isDialogOpen, setIsDialogOpen] = useState(false); - - const closeDialog = useCallback(() => setIsDialogOpen(false), []); // Close dialog function - - const { data, increment, decrement } = useCounter(card, context, { - max: page === 'DeckBuilder' ? 3 : undefined, // Limit to 3 for Deck context - }); - - /** @internal Place an object in an array */ - const placeObjectInArray = useCallback((object) => { - const objectInArray = [object]; - return objectInArray; - }, []); - - const performAction = useCallback( - (action) => { - const updateQuantity = (actionType) => { - actionType === 'add' ? increment(card.id) : decrement(card.id); - }; - const array = placeObjectInArray(data); - console.log('DATA =>=>=> ', data); - const actionFunctions = { - Collection: { - add: () => { - console.log('array === ', array); - updateQuantity('add'); - addOneToCollection(array, selectedCollection); - }, - remove: () => { - updateQuantity('remove'); - removeOneFromCollection( - placeObjectInArray(data), - placeObjectInArray(data?.id), - selectedCollection - ); - }, - }, - Deck: { - add: () => { - updateQuantity('add'); - addOneToDeck(data, selectedDeck); - }, - remove: () => { - updateQuantity('remove'); - removeOneFromDeck(data, selectedDeck); - }, - }, - Cart: { - add: () => { - updateQuantity('add'); - addOneToCart(data); - }, - remove: () => { - updateQuantity('remove'); - removeOneFromCart(data); - }, - }, - }; - - try { - actionFunctions[context][action](); - onSuccess?.(); - } catch (error) { - onFailure?.(error); - } - }, - [ - addOneToCart, - addOneToCollection, - addOneToDeck, - context, - data, - decrement, - increment, - onFailure, - openDialog, - page, - placeObjectInArray, - removeOneFromCart, - removeOneFromCollection, - removeOneFromDeck, - selectedCollection, - selectedDeck, - onSuccess, - ] - ); - - return { - count: data?.quantity, - performAction, - closeDialog, - }; -}; diff --git a/src/context/hooks/useDialogState.jsx b/src/context/hooks/useDialogState.jsx index 2480399..ed5c484 100644 --- a/src/context/hooks/useDialogState.jsx +++ b/src/context/hooks/useDialogState.jsx @@ -7,6 +7,8 @@ const useDialogState = ( isEditDeckDialogOpen: false, isAddCollectionDialogOpen: false, isEditCollectionDialogOpen: false, + isAuthDialogOpen: false, + isSelectionErrorDialogOpen: false, } ) => { const [state, setState] = useState(initialState); diff --git a/src/context/hooks/useEventHandlers.jsx b/src/context/hooks/useEventHandlers.jsx new file mode 100644 index 0000000..3e0ccc7 --- /dev/null +++ b/src/context/hooks/useEventHandlers.jsx @@ -0,0 +1,23 @@ +import { debounce } from 'lodash'; +import { useCallback, useState } from 'react'; + +export const useEventHandlers = () => { + const [hoveredData, setHoveredData] = useState(null); + const debouncedSetHoveredData = useCallback( + debounce(setHoveredData, 100), + [] + ); + const handleMouseMove = useCallback( + (point) => { + debouncedSetHoveredData( + point ? { x: point?.data?.x, y: point?.data?.y } : null + ); + }, + [debouncedSetHoveredData] + ); + const handleMouseLeave = useCallback( + () => debouncedSetHoveredData(null), + [debouncedSetHoveredData] + ); + return { hoveredData, handleMouseMove, handleMouseLeave }; +}; diff --git a/src/context/hooks/useFetchWrapper.jsx b/src/context/hooks/useFetchWrapper.jsx index c6878e4..b4247e8 100644 --- a/src/context/hooks/useFetchWrapper.jsx +++ b/src/context/hooks/useFetchWrapper.jsx @@ -2,8 +2,7 @@ import { useState, useCallback } from 'react'; import useLogger from './useLogger'; import useLocalStorage from './useLocalStorage'; import { useLoading } from './useLoading'; -import useSnackbarManager from './useSnackbarManager'; - +import { useSnackbar } from 'notistack'; const useFetchWrapper = () => { const [status, setStatus] = useState('idle'); const [data, setData] = useState(null); @@ -11,16 +10,21 @@ const useFetchWrapper = () => { const [error, setError] = useState(null); const { logEvent } = useLogger('useFetchWrapper'); const { startLoading, stopLoading, isLoading } = useLoading(); - const { showSuccess, showInfo, showError } = useSnackbarManager(); - + const { enqueueSnackbar } = useSnackbar(); + const showNotification = (message, variant) => { + enqueueSnackbar(message, { + variant: variant, + anchorOrigin: { + vertical: 'top', + horizontal: 'right', + }, + }); + }; const fetchWrapper = useCallback( async (url, method = 'GET', body = null, loadingID) => { setStatus('loading'); startLoading(loadingID); - - // Showing loading snackbar - const loadingSnackbar = showInfo('Loading', loadingID); - + showNotification(`Loading ${loadingID}...`, 'info'); try { const headers = { 'Content-Type': 'application/json' }; const options = { @@ -47,35 +51,25 @@ const useFetchWrapper = () => { ...prevCache, [loadingID]: responseData, })); - - // Showing success snackbar - showSuccess(`Your ${loadingID} data has been fetched successfully.`); + showNotification( + `Success: Your ${loadingID} data has been fetched.`, + 'success' + ); 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()}`); + showNotification( + `Error fetching ${loadingID}: ${error.toString()}`, + 'error' + ); } finally { stopLoading(loadingID); - - // Closing loading snackbar - // closeSnackbar(loadingSnackbar); } }, - [ - setResponseCache, - startLoading, - stopLoading, - logEvent, - showSuccess, - showInfo, - showError, - // closeSnackbar, - ] + [startLoading, stopLoading, logEvent, setResponseCache] ); return { diff --git a/src/context/hooks/useFormData.jsx b/src/context/hooks/useFormData.jsx new file mode 100644 index 0000000..c87168d --- /dev/null +++ b/src/context/hooks/useFormData.jsx @@ -0,0 +1,18 @@ +import { useState } from 'react'; + +export const useFormData = (initialValues) => { + const [formData, setFormData] = useState(initialValues); + + const updateFormData = (event) => { + setFormData({ + ...formData, + [event.target.name]: event.target.value, + }); + }; + + const resetFormData = () => { + setFormData(initialValues); + }; + + return { formData, updateFormData, resetFormData }; +}; diff --git a/src/context/hooks/useGridItems.jsx b/src/context/hooks/useGridItems.jsx index d562efe..9734e63 100644 --- a/src/context/hooks/useGridItems.jsx +++ b/src/context/hooks/useGridItems.jsx @@ -1,13 +1,12 @@ import React, { useMemo } from 'react'; 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'; 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'; +import { useCardStoreHook } from '../MAIN_CONTEXT/CardContext/useCardStore'; const useGridItems = ({ itemsPerPage, cards, @@ -20,12 +19,10 @@ const useGridItems = ({ deckId, }) => { const { loadingSearchResults } = useCardStoreHook(); - 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 const gridItems = useMemo(() => { - console.log('GRID ITEMS:', cards); return ( isLoading || loadingSearchResults ? Array.from({ length: itemsPerPage }) @@ -115,7 +112,6 @@ const useGridItems = ({ )} )} - {/* 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 */} diff --git a/src/context/hooks/useLoading.jsx b/src/context/hooks/useLoading.jsx index 60aa343..97473e5 100644 --- a/src/context/hooks/useLoading.jsx +++ b/src/context/hooks/useLoading.jsx @@ -1,45 +1,11 @@ import { useCallback, useState } from 'react'; -const GENERAL_LOADING_IDS = new Set([ - 'isLoading', - 'isDataLoading', - 'isFormDataLoading', - 'isPageLoading', - 'isSearchLoading', -]); // Define general loading IDs const GENERAL_LOADING_STATES = { isLoading: 'isLoading', isDataLoading: 'isDataLoading', isFormDataLoading: 'isFormDataLoading', isPageLoading: 'isPageLoading', ifIsSearchLoading: 'isSearchLoading', -}; // Define general loading states -const ADDITIONAL_LOADING_STATES = { - loadingTimeoutExpired: 'loadingTimeoutExpired', - error: 'error', -}; // Define additional loading states -const DEFAULT_VALUES = { - loadingStatus: { - isLoading: false, - isDataLoading: false, - isFormDataLoading: false, - isPageLoading: false, - isSearchLoading: false, - error: null, - loadingTimeoutExpired: false, - loadingType: '', - }, - error: null, - // setActivelyLoading: () => {}, - // returnDisplay: () => null, - // setLoading: () => {}, - // setError: () => {}, - // setPageError: () => {}, - // setIsLoading: () => {}, - // setIsDataLoading: () => {}, - // setIsFormDataLoading: () => {}, - // setIsPageLoading: () => {}, - // setLoadingTimeoutExpired: () => {}, -}; // Define default values +}; /** * A hook to manage loading states within components. diff --git a/src/context/hooks/useLocalStorage.jsx b/src/context/hooks/useLocalStorage.jsx index e8a6200..865582d 100644 --- a/src/context/hooks/useLocalStorage.jsx +++ b/src/context/hooks/useLocalStorage.jsx @@ -1,5 +1,4 @@ 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; @@ -16,6 +15,7 @@ function useLocalStorage(key, initialValue) { try { const item = window.localStorage.getItem(key); + return item ? safeJsonParse(item) : initialValue; } catch (error) { console.error(`Error reading localStorage key "${key}":`, error); @@ -33,10 +33,11 @@ function useLocalStorage(key, initialValue) { } try { - // React to external changes in local storage const onStorageChange = (event) => { if (event.key === key) { - setStoredValue(JSON.parse(event.newValue)); + setStoredValue( + event.newValue ? JSON.parse(event.newValue) : initialValue + ); } }; @@ -45,7 +46,7 @@ function useLocalStorage(key, initialValue) { } catch (error) { console.error('Listening for local storage changes failed:', error); } - }, [key]); + }, [key, initialValue]); // Set value to localStorage and update local state const setValue = (value) => { @@ -63,11 +64,18 @@ function useLocalStorage(key, initialValue) { // window.localStorage.setItem(key, JSON.stringify(valueToStore)); setStoredValue(valueToStore); window.localStorage.setItem(key, JSON.stringify(valueToStore)); - + const storageEvent = new StorageEvent('storage', { + key: key, + newValue: JSON.stringify(valueToStore), + oldValue: window.localStorage.getItem(key), + storageArea: localStorage, + url: window.location.href, + }); + window.dispatchEvent(storageEvent); // Optionally, for cross-tab communication, you might want to trigger a storage event manually - window.dispatchEvent( - new Event('storage', { key, newValue: JSON.stringify(valueToStore) }) - ); + // window.dispatchEvent( + // new Event('storage', { key, newValue: JSON.stringify(valueToStore) }) + // ); } catch (error) { console.error(`Error setting localStorage key "${key}":`, error); } diff --git a/src/context/hooks/useManageCookies.jsx b/src/context/hooks/useManageCookies.jsx new file mode 100644 index 0000000..bea34f7 --- /dev/null +++ b/src/context/hooks/useManageCookies.jsx @@ -0,0 +1,63 @@ +import { useCookies } from 'react-cookie'; + +function useManageCookies() { + const [cookies, setCookie, removeCookie] = useCookies(); + const addCookie = (name, value, options = {}) => { + setCookie(name, value, options); + }; + const addCookies = (names, values, options = {}) => { + if (Array.isArray(names) && Array.isArray(values)) { + names.forEach((name, index) => { + const value = values[index]; + if (value !== undefined) { + // Check to prevent setting undefined cookies + setCookie(name, JSON.stringify(value), options); + } + }); + } else { + setCookie(names, JSON.stringify(values), options); + } + }; + const getCookie = (nameOrNames) => { + const parseCookieValue = (value) => { + try { + value = JSON.parse(value); + } catch (e) { + if (value === 'true') return true; + if (value === 'false') return false; + } + return value; + }; + + if (Array.isArray(nameOrNames)) { + return nameOrNames.reduce((acc, name) => { + acc[name] = parseCookieValue(cookies[name]); + return acc; + }, {}); + } else { + // Handling a single cookie name + // return { + // [nameOrNames]: parseCookieValue(cookies[nameOrNames]), + // }; + return parseCookieValue(cookies[nameOrNames]); + } + }; + + // Function to remove a cookie + const deleteCookie = (name) => { + removeCookie(name); + }; + const deleteCookies = (names) => { + if (Array.isArray(names)) { + names.forEach((name) => { + removeCookie(name); + }); + } else { + removeCookie(names); + } + }; + + return { addCookie, addCookies, getCookie, deleteCookie, deleteCookies }; +} + +export default useManageCookies; diff --git a/src/context/hooks/useSelectedContext.jsx b/src/context/hooks/useSelectedContext.jsx index f4fda06..45f9c9f 100644 --- a/src/context/hooks/useSelectedContext.jsx +++ b/src/context/hooks/useSelectedContext.jsx @@ -1,11 +1,9 @@ import { useState, useCallback, useEffect } from 'react'; const useSelectedContext = () => { - // State to hold the current selected context const [selectedContext, setSelectedContext] = useState(null); const [isContextSelected, setIsContextSelected] = useState(false); - // // Method to set a new context const setContext = useCallback((newContext) => { console.log('Context selected:', newContext); setSelectedContext(newContext); diff --git a/src/context/hooks/useSnackbarManager.jsx b/src/context/hooks/useSnackbarManager.jsx deleted file mode 100644 index 01c7915..0000000 --- a/src/context/hooks/useSnackbarManager.jsx +++ /dev/null @@ -1,59 +0,0 @@ -// 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/useGetRandomCard.jsx b/src/context/hooks/ygo-specific/useGetRandomCard.jsx similarity index 100% rename from src/context/hooks/useGetRandomCard.jsx rename to src/context/hooks/ygo-specific/useGetRandomCard.jsx diff --git a/src/context/hooks/useOverlay.jsx b/src/context/hooks/ygo-specific/useOverlay.jsx similarity index 100% rename from src/context/hooks/useOverlay.jsx rename to src/context/hooks/ygo-specific/useOverlay.jsx diff --git a/src/context/index.js b/src/context/index.js index 7b10cd1..b196029 100644 --- a/src/context/index.js +++ b/src/context/index.js @@ -5,25 +5,15 @@ export { useCardStore } from './MAIN_CONTEXT/CardContext/CardContext'; export { useCollectionStore } from './MAIN_CONTEXT/CollectionContext/CollectionContext'; export { useModalContext } from './UTILITIES_CONTEXT/ModalContext/ModalContext'; export { useUserContext } from './MAIN_CONTEXT/UserContext/UserContext'; -// export { useCombinedContext } from './MISC_CONTEXT/CombinedContext/CombinedProvider'; -// export { useSocketContext } from './UTILITIES_CONTEXT/SocketContext/SocketProvider'; export { useSidebarContext } from './UTILITIES_CONTEXT/SideBarContext/SideBarProvider'; -export { useChartContext } from './MAIN_CONTEXT/ChartContext/ChartContext'; 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 '../assets/currentlyUnused/CardImagesContext/CardImagesContext'; export { useAuthContext } from './MAIN_CONTEXT/AuthContext/authContext'; -// 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 { useSnackbarContext } from './UTILITIES_CONTEXT/SnackbarContext/SnackbarContext'; // Contexts -export { default as ErrorBoundary } from './ErrorBoundary'; +export { default as ErrorBoundary } from '../layout/REUSABLE_COMPONENTS/ErrorBoundary'; export { default as AuthProvider } from './MAIN_CONTEXT/AuthContext/authContext'; export { CartProvider } from './MAIN_CONTEXT/CartContext/CartContext'; export { DeckProvider } from './MAIN_CONTEXT/DeckContext/DeckContext'; @@ -31,12 +21,8 @@ export { CardProvider } from './MAIN_CONTEXT/CardContext/CardContext'; export { CollectionProvider } from './MAIN_CONTEXT/CollectionContext/CollectionContext'; export { AppContextProvider } from './MISC_CONTEXT/AppContext/AppContextProvider'; export { UserProvider } from './MAIN_CONTEXT/UserContext/UserContext'; - export { ModalProvider } from './UTILITIES_CONTEXT/ModalContext/ModalContext'; - export { ColorModeProvider } from './UTILITIES_CONTEXT/ColorModeContext/ColorModeProvider'; export { SidebarProvider } from './UTILITIES_CONTEXT/SideBarContext/SideBarProvider'; -export { ChartProvider } from './MAIN_CONTEXT/ChartContext/ChartContext'; -export { StatisticsProvider } from './SECONDARY_CONTEXT/StatisticsContext/StatisticsContext'; export { FormProvider } from './UTILITIES_CONTEXT/FormContext/FormContext'; export { ConfiguratorProvider } from './UTILITIES_CONTEXT/ConfiguratorContext/ConfiguratorContext'; diff --git a/src/context/simplified_constants.jsx b/src/context/simplified_constants.jsx index fdafce9..34c00e5 100644 --- a/src/context/simplified_constants.jsx +++ b/src/context/simplified_constants.jsx @@ -4,6 +4,7 @@ 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 REACT_APP_SERVER = process.env.REACT_APP_SERVER; // const defaultUserData = user.data; // const defaultUser = defaultUserData[0]; @@ -12,6 +13,7 @@ const defaultCards = Array(5).fill(defaultCard); // More efficient way to create export const defaultValues = { defaultCard, defaultCards, + REACT_APP_SERVER, // defaultUser, // defaultUsers, }; diff --git a/src/data/collectionPortfolioData.jsx b/src/data/collectionPortfolioData.jsx index 1a28d2a..e0598e0 100644 --- a/src/data/collectionPortfolioData.jsx +++ b/src/data/collectionPortfolioData.jsx @@ -3,6 +3,7 @@ import Icon from '@mui/material/Icon'; import MDTypography from '../MDTYPOGRAPHY/MDTypography'; import React from 'react'; import LoadingIndicator from '../../../components/reusable/indicators/LoadingIndicator'; +import { roundToNearestTenth } from '../context/Helpers'; const Name = ({ name }) => ( ( ); export default function prepareTableData(selectedCards) { if (!selectedCards) return ; - - // Helper function to round total price to the nearest tenth - const roundToNearestTenth = (value) => Math.round(value * 10) / 10; - // Define column structure for react-table const columns = React.useMemo( () => [ diff --git a/src/data/collectionPortfolioHeaderItems.jsx b/src/data/collectionPortfolioHeaderItems.jsx new file mode 100644 index 0000000..77677f3 --- /dev/null +++ b/src/data/collectionPortfolioHeaderItems.jsx @@ -0,0 +1,34 @@ +export const collectionPortfolioHeaderItems = (collection) => [ + { + // icon: , + icon: 'collections', + label: 'Portfolio Selected', + value: collection?.name || 'Select a collection to view its statistics', + delay: 0, + }, + { + // icon: , + icon: 'attach_money', + label: 'Total Value', + value: + collection?.totalPrice || 'Select a collection to view its statistics', + delay: 200, + }, + { + // icon: , + icon: 'format_list_numbered', + label: 'Number of Unique Cards', + value: + collection?.cards?.length || 'Select a collection to view its statistics', + delay: 400, + }, + { + // icon: , + icon: 'trending_up', + label: "Today's Performance", + value: + collection?.statistics?.percentChange || + 'Select a collection to view its statistics', + delay: 600, + }, +]; diff --git a/src/data/iconData.jsx b/src/data/iconData.jsx index fddae08..431baa6 100644 --- a/src/data/iconData.jsx +++ b/src/data/iconData.jsx @@ -216,4 +216,10 @@ const iconData = [ }, ]; -export default iconData; +const getIconDataByComponent = (component) => { + const match = iconData.find((i) => i.component === component); + const returnIcon = match ? match.icon : null; + return returnIcon; +}; + +export default { iconData, getIconDataByComponent }; diff --git a/src/data/route-config.jsx b/src/data/route-config.jsx new file mode 100644 index 0000000..014cde4 --- /dev/null +++ b/src/data/route-config.jsx @@ -0,0 +1,16 @@ +export const ROUTE_CONFIG = [ + { path: '/', componentName: 'HomePage', isPrivate: false }, + { path: '/home', componentName: 'HomePage', isPrivate: false }, + { path: '/deckbuilder', componentName: 'DeckBuilderPage', isPrivate: false }, + { path: '/store', componentName: 'StorePage', isPrivate: false }, + { path: '/cart', componentName: 'CartPage', isPrivate: true }, + { path: '/collection', componentName: 'CollectionPage', isPrivate: true }, + { path: '/profile', componentName: 'ProfilePage', isPrivate: true }, + { path: '/login', componentName: 'LoginDialog', isPrivate: false }, + { path: '/signup', componentName: 'SignupPage', isPrivate: false }, + { path: '/about', componentName: 'AboutPage', isPrivate: false }, + { path: '/contact', componentName: 'ContactPage', isPrivate: false }, + { path: '/terms', componentName: 'TermsPage', isPrivate: false }, + { path: '/privacy', componentName: 'PrivacyPage', isPrivate: false }, + { path: '*', componentName: 'NotFoundPage', isPrivate: false }, +]; diff --git a/src/data/searchData.jsx b/src/data/searchData.jsx index 3f84dde..9bdefd8 100644 --- a/src/data/searchData.jsx +++ b/src/data/searchData.jsx @@ -6,6 +6,7 @@ import LoadingIndicator from '../../../components/reusable/indicators/LoadingInd import GenericCard from '../components/cards/GenericCard'; import { Container } from '@mui/system'; import { Box } from '@mui/material'; +import { roundToNearestTenth } from '../context/Helpers'; const SearchItem = memo(({ card, context, page, index }) => { return ( ; - // Helper function to round total price to the nearest tenth - const roundToNearestTenth = (value) => Math.round(value * 10) / 10; - - // Define column structure for react-table const columns = React.useMemo( () => [ { diff --git a/src/context/user.jsx b/src/data/user.jsx similarity index 100% rename from src/context/user.jsx rename to src/data/user.jsx diff --git a/src/index.js b/src/index.js index de806e2..9f6335a 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,7 @@ import * as serviceWorker from './serviceWorker'; // ==============================|| REACT DOM RENDER ||============================== // -import { AuthProvider, ColorModeProvider } from './context'; +import { AuthProvider, ColorModeProvider, ErrorBoundary } from './context'; import { loadStripe } from '@stripe/stripe-js'; import { Elements } from '@stripe/react-stripe-js'; import { Helmet } from 'react-helmet'; @@ -59,18 +59,20 @@ const AppWrapper = () => { ); return ( - - - - - - - - - - - - + + + + + + + + + + + + + + ); }; diff --git a/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx b/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx index deba981..d88f5f0 100644 --- a/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx +++ b/src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx @@ -18,7 +18,7 @@ const BoxHeader = ({ const { theme } = useMode(); return ( {title}
@@ -44,7 +46,7 @@ const BoxHeader = ({ {sideText} diff --git a/src/layout/REUSABLE_COMPONENTS/Configurator/ConfiguratorRoot.jsx b/src/layout/REUSABLE_COMPONENTS/Configurator/ConfiguratorRoot.jsx index d6d4aac..c58536e 100644 --- a/src/layout/REUSABLE_COMPONENTS/Configurator/ConfiguratorRoot.jsx +++ b/src/layout/REUSABLE_COMPONENTS/Configurator/ConfiguratorRoot.jsx @@ -1,8 +1,8 @@ import Drawer from '@mui/material/Drawer'; import { styled } from '@mui/material/styles'; import { useConfiguratorContext, useMode } from '../../../context'; -import { useCardStoreHook } from '../../../context/hooks/useCardStore'; import { useEffect } from 'react'; +import { useCardStoreHook } from '../../../context/MAIN_CONTEXT/CardContext/useCardStore'; export default styled(Drawer)(({ ownerState }) => { const { theme } = useMode(); diff --git a/src/context/ErrorBoundary.jsx b/src/layout/REUSABLE_COMPONENTS/ErrorBoundary.jsx similarity index 100% rename from src/context/ErrorBoundary.jsx rename to src/layout/REUSABLE_COMPONENTS/ErrorBoundary.jsx diff --git a/src/layout/ErrorIndicator.js b/src/layout/REUSABLE_COMPONENTS/ErrorIndicator.js similarity index 97% rename from src/layout/ErrorIndicator.js rename to src/layout/REUSABLE_COMPONENTS/ErrorIndicator.js index 7592742..0e30542 100644 --- a/src/layout/ErrorIndicator.js +++ b/src/layout/REUSABLE_COMPONENTS/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/layout/LoadingIndicator.js b/src/layout/REUSABLE_COMPONENTS/LoadingIndicator.js similarity index 100% rename from src/layout/LoadingIndicator.js rename to src/layout/REUSABLE_COMPONENTS/LoadingIndicator.js diff --git a/src/layout/LoadingOverlay.jsx b/src/layout/REUSABLE_COMPONENTS/LoadingOverlay.jsx similarity index 96% rename from src/layout/LoadingOverlay.jsx rename to src/layout/REUSABLE_COMPONENTS/LoadingOverlay.jsx index d7c6aa1..b6d3e2f 100644 --- a/src/layout/LoadingOverlay.jsx +++ b/src/layout/REUSABLE_COMPONENTS/LoadingOverlay.jsx @@ -2,7 +2,7 @@ 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'; +import { useMode } from '../../context'; const LoadingOverlay = () => { const { theme } = useMode(); diff --git a/src/layout/REUSABLE_COMPONENTS/MDBOX/MdBoxRoot.jsx b/src/layout/REUSABLE_COMPONENTS/MDBOX/MdBoxRoot.jsx index f15e09f..0f90c49 100644 --- a/src/layout/REUSABLE_COMPONENTS/MDBOX/MdBoxRoot.jsx +++ b/src/layout/REUSABLE_COMPONENTS/MDBOX/MdBoxRoot.jsx @@ -3,7 +3,6 @@ import { styled } from 'styled-components'; import { useMode, useSidebarContext } from '../../../context'; export default styled(Box)(({ ownerstate }) => { - // const { backgroundA, backgroundB, backgroundC, rarity } = theme; const { isSidebarOpen } = useSidebarContext(); const { theme } = useMode(); const { palette, functions, borders, boxShadows, transitions, breakpoints } = @@ -126,14 +125,6 @@ export default styled(Box)(({ ownerstate }) => { // Map background color const mapBgColor = () => { switch (bgColor) { - case 'backgroundA': - return variant === 'gradient' - ? theme.palette.backgroundA.dark - : theme.palette.backgroundA.default; - case 'backgroundB': - return theme.palette.backgroundB.default; - case 'backgroundC': - return theme.palette.backgroundC.default; default: return theme.palette[bgColor] ? theme.palette[bgColor].main : bgColor; } diff --git a/src/layout/REUSABLE_COMPONENTS/MDBUTTON/MDButtonRoot.jsx b/src/layout/REUSABLE_COMPONENTS/MDBUTTON/MDButtonRoot.jsx index 3137ab3..a64fb17 100644 --- a/src/layout/REUSABLE_COMPONENTS/MDBUTTON/MDButtonRoot.jsx +++ b/src/layout/REUSABLE_COMPONENTS/MDBUTTON/MDButtonRoot.jsx @@ -267,24 +267,18 @@ export default styled(Button)(({ ownerState }) => { }; const customContainedStyles = { - backgroundColor: palette.backgroundF.dark, - // background: - // backgroundColor: theme.palette.backgroundE.light, - // color: theme.palette.primary.main, - borderColor: palette.backgroundB.dark, - // color: theme.palette.primary.main, + backgroundColor: palette.greenAccent.greenBlue, + borderColor: palette.grey.simpleGrey, borderWidth: 2, flexGrow: 1, justifySelf: 'bottom', bottom: 0, '&:hover': { fontWeight: 'bold', - background: palette.backgroundF.dark, - border: `1px solid ${palette.backgroundF.darkest}`, - // border: `1px solid ${theme.palette.backgroundB.darkest}`, + background: palette.greenAccent.greenBlue, + border: `1px solid ${palette.greenAccent.ceruleanBlue}`, }, transition: 'opacity 0.3s ease, transform 0.3s ease', - // Add any other styles you want to apply specifically to this variant }; const customContained = () => { diff --git a/src/layout/REUSABLE_COMPONENTS/MDBUTTON/index.jsx b/src/layout/REUSABLE_COMPONENTS/MDBUTTON/index.jsx index c7a45f6..1785b2a 100644 --- a/src/layout/REUSABLE_COMPONENTS/MDBUTTON/index.jsx +++ b/src/layout/REUSABLE_COMPONENTS/MDBUTTON/index.jsx @@ -31,8 +31,6 @@ MDButton.defaultProps = { size: 'medium', variant: 'contained', color: 'white', - // backgroundColor: theme.palette.backgroundF.main, - // backgroundColor: 'primary', circular: false, iconOnly: false, }; @@ -48,6 +46,7 @@ MDButton.propTypes = { 'customContained', ]), color: PropTypes.oneOf([ + 'default', 'white', 'primary', 'secondary', @@ -57,12 +56,6 @@ MDButton.propTypes = { 'error', 'light', 'dark', - // 'backgroundA', - // 'backgroundB', - // 'backgroundC', - // 'backgroundD', - // 'backgroundE', - // 'backgroundE', ]), circular: PropTypes.bool, iconOnly: PropTypes.bool, diff --git a/src/layout/REUSABLE_COMPONENTS/RCBADGE/RCBadgeRoot.jsx b/src/layout/REUSABLE_COMPONENTS/RCBADGE/RCBadgeRoot.jsx new file mode 100644 index 0000000..7ccd52b --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBADGE/RCBadgeRoot.jsx @@ -0,0 +1,135 @@ +import { Badge } from '@mui/material'; +import styled from 'styled-components'; +import { useMode } from '../../../context/UTILITIES_CONTEXT/ColorModeContext/useMode'; + +export default styled(Badge)(({ ownerState }) => { + const { theme } = useMode(); + const { palette, typography, borders, functions } = theme; + const { + color, + circular, + border, + size, + indicator, + variant, + container, + children, + } = ownerState; + + const { white, dark, gradients, badgeColors } = palette; + const { size: fontSize, fontWeightBold } = typography; + const { borderRadius, borderWidth } = borders; + const { pxToRem, linearGradient } = functions; + + // padding values + const paddings = { + xs: '0.45em 0.775em', + sm: '0.55em 0.9em', + md: '0.65em 1em', + lg: '0.85em 1.375em', + }; + + // fontSize value + const fontSizeValue = size === 'xs' ? fontSize.xxs : fontSize.xs; + + // border value + const borderValue = border ? `${borderWidth[3]} solid ${white.main}` : 'none'; + + // borderRadius value + const borderRadiusValue = circular ? borderRadius.section : borderRadius.lg; + + // styles for the badge with indicator={true} + const indicatorStyles = (sizeProp) => { + let widthValue = pxToRem(20); + let heightValue = pxToRem(20); + + if (sizeProp === 'medium') { + widthValue = pxToRem(24); + heightValue = pxToRem(24); + } else if (sizeProp === 'large') { + widthValue = pxToRem(32); + heightValue = pxToRem(32); + } + + return { + width: widthValue, + height: heightValue, + display: 'grid', + placeItems: 'center', + textAlign: 'center', + borderRadius: '50%', + padding: 0, + border: borderValue, + }; + }; + + // styles for the badge with variant="gradient" + const gradientStyles = (colorProp) => { + const backgroundValue = gradients[colorProp] + ? linearGradient(gradients[colorProp].main, gradients[colorProp].state) + : linearGradient(gradients.info.main, gradients.info.state); + const colorValue = colorProp === 'light' ? dark.main : white.main; + + return { + background: backgroundValue, + color: colorValue, + }; + }; + + // styles for the badge with variant="contained" + const containedStyles = (colorProp) => { + let backgroundValue = badgeColors[colorProp] + ? badgeColors[colorProp].background + : badgeColors.info.background; + let colorValue = badgeColors[colorProp] + ? badgeColors[colorProp].text + : badgeColors.info.text; + + if (colorProp === 'light') { + colorValue = dark.main; + } else if (colorProp === 'white') { + backgroundValue = white.main; + colorValue = dark.main; + } + + return { + background: backgroundValue, + color: colorValue, + }; + }; + + // styles for the badge with no children and container={false} + const standAloneStyles = () => ({ + position: 'static', + marginLeft: pxToRem(8), + transform: 'none', + fontSize: pxToRem(9), + }); + + // styles for the badge with container={true} + const containerStyles = () => ({ + position: 'relative', + transform: 'none', + }); + + return { + '& .MuiBadge-badge': { + height: 'auto', + padding: paddings[size] || paddings.xs, + fontSize: fontSizeValue, + fontWeight: fontWeightBold, + textTransform: 'uppercase', + lineHeight: 1, + textAlign: 'center', + whiteSpace: 'nowrap', + verticalAlign: 'baseline', + border: borderValue, + borderRadius: borderRadiusValue, + ...(indicator && indicatorStyles(size)), + ...(variant === 'gradient' && gradientStyles(color)), + ...(variant === 'contained' && containedStyles(color)), + ...(!children && !container && standAloneStyles(color)), + ...(container && containerStyles(color)), + }, + }; +}); diff --git a/src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesGradient.jsx b/src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesGradient.jsx new file mode 100644 index 0000000..dd210cb --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesGradient.jsx @@ -0,0 +1,28 @@ +import Container from '@mui/material/Container'; +import Grid from '@mui/material/Grid'; +import Stack from '@mui/material/Stack'; +import RCBadge from '..'; +import { MDBox } from '../../MDBOX/index'; + +function BadgesGradient() { + return ( + + + + + + + + + + + + + + + + + ); +} + +export default BadgesGradient; diff --git a/src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesSimple.jsx b/src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesSimple.jsx new file mode 100644 index 0000000..bdb078c --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesSimple.jsx @@ -0,0 +1,68 @@ +import Container from '@mui/material/Container'; +import Grid from '@mui/material/Grid'; +import Stack from '@mui/material/Stack'; +import RCBadge from '..'; +import { MDBox } from '../../MDBOX/index'; + +function BadgesSimple() { + return ( + + + + + + + + + + + + + + + + + ); +} + +export default BadgesSimple; diff --git a/src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesSimpleRounded.jsx b/src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesSimpleRounded.jsx new file mode 100644 index 0000000..637c38d --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBADGE/examples/BadgesSimpleRounded.jsx @@ -0,0 +1,76 @@ +import Container from '@mui/material/Container'; +import Grid from '@mui/material/Grid'; +import Stack from '@mui/material/Stack'; +import RCBadge from '..'; +import { MDBox } from '../../MDBOX/index'; + +function BadgesSimpleRounded() { + return ( + + + + + + + + + + + + + + + + + ); +} + +export default BadgesSimpleRounded; diff --git a/src/layout/REUSABLE_COMPONENTS/RCBADGE/index.jsx b/src/layout/REUSABLE_COMPONENTS/RCBADGE/index.jsx new file mode 100644 index 0000000..1b19ee5 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBADGE/index.jsx @@ -0,0 +1,72 @@ +import { forwardRef } from 'react'; +import PropTypes from 'prop-types'; +import RCBadgeRoot from './RCBadgeRoot'; + +const RCBadge = forwardRef( + ( + { + color, + variant, + size, + circular, + indicator, + border, + container, + children, + ...rest + }, + ref + ) => ( + + {children} + + ) +); + +RCBadge.displayName = 'RCBadge'; +RCBadge.defaultProps = { + color: 'info', + variant: 'gradient', + size: 'sm', + circular: false, + indicator: false, + border: false, + children: false, + container: false, +}; +RCBadge.propTypes = { + color: PropTypes.oneOf([ + 'white', + 'primary', + 'secondary', + 'info', + 'success', + 'warning', + 'error', + 'light', + 'dark', + ]), + variant: PropTypes.oneOf(['gradient', 'contained']), + size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg']), + circular: PropTypes.bool, + indicator: PropTypes.bool, + border: PropTypes.bool, + children: PropTypes.node, + container: PropTypes.bool, +}; + +export default RCBadge; diff --git a/src/layout/REUSABLE_COMPONENTS/RCBUTTON/RCButtonRoot.jsx b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/RCButtonRoot.jsx new file mode 100644 index 0000000..5c0b47d --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/RCButtonRoot.jsx @@ -0,0 +1,458 @@ +import { Button } from '@mui/material'; +import styled from 'styled-components'; +import { useMode } from '../../../context'; +// const calculateStyles = (size) => { +// switch (size) { +// case 'small': +// return { +// width: '75px', +// // p: '1rem', +// p: '0.5rem 0.75rem', +// // m: '0.5rem', +// fontSize: '0.875rem', +// }; +// case 'medium': +// return { +// width: '100px', +// padding: '0.75rem 1rem', +// fontSize: '1rem', +// }; +// case 'large': +// return { +// width: '125px', +// padding: '1rem 1.5rem', +// fontSize: '1.25rem', +// }; +// default: +// return { +// width: '140px', +// padding: '1rem 1.25rem', +// fontSize: '1.125rem', +// }; +// } +// }; +export default styled(Button)(({ ownerState }) => { + const { theme } = useMode(); + const { palette, functions, borders, boxShadows } = theme; + const { color, variant, size, circular, iconOnly } = ownerState; + const { + white, + text, + transparent, + gradients, + dark, + greenAccent, + grey, + success, + error, + } = palette; + const { boxShadow, linearGradient, pxToRem, rgba } = functions; + const { borderRadius } = borders; + const { colored } = boxShadows; + const containedStyles = () => { + const backgroundValue = palette[color] ? palette[color].main : white.main; + const focusedBackgroundValue = palette[color] + ? palette[color].focus + : white.focus; + + // boxShadow value + const boxShadowValue = colored[color] + ? `${boxShadow([0, 3], [3, 0], palette[color].main, 0.15)}, ${boxShadow( + [0, 3], + [1, -2], + palette[color].main, + 0.2 + )}, ${boxShadow([0, 1], [5, 0], palette[color].main, 0.15)}` + : 'none'; + + // boxShadow value when button is hovered + const hoveredBoxShadowValue = colored[color] + ? `${boxShadow([0, 14], [26, -12], palette[color].main, 0.4)}, ${boxShadow( + [0, 4], + [23, 0], + palette[color].main, + 0.15 + )}, ${boxShadow([0, 8], [10, -5], palette[color].main, 0.2)}` + : 'none'; + + // color value + let colorValue = white.main; + + if (color === 'default' || !palette[color]) { + colorValue = text.main; + } else if (color === 'white' || color === 'light') { + colorValue = dark.main; + } + + // color value when button is focused + let focusedColorValue = white.main; + + if (color === 'default') { + focusedColorValue = text.main; + } else if (color === 'white') { + focusedColorValue = dark.main; + } else if (color === 'primary' || color === 'error' || color === 'dark') { + focusedColorValue = white.main; + } + + return { + background: backgroundValue, + color: colorValue, + boxShadow: boxShadowValue, + + '&:hover': { + backgroundColor: backgroundValue, + boxShadow: hoveredBoxShadowValue, + }, + + '&:focus:not(:hover)': { + backgroundColor: focusedBackgroundValue, + boxShadow: palette[color] + ? boxShadow([0, 0], [0, 3.2], palette[color].main, 0.5) + : boxShadow([0, 0], [0, 3.2], white.main, 0.5), + }, + + '&:disabled': { + backgroundColor: backgroundValue, + color: focusedColorValue, + }, + }; + }; + const outlinedStyles = () => { + // background color value + const backgroundValue = + color === 'white' ? rgba(white.main, 0.1) : transparent.main; + + // color value + const colorValue = palette[color] ? palette[color].main : white.main; + + // boxShadow value + const boxShadowValue = palette[color] + ? boxShadow([0, 0], [0, 3.2], palette[color].main, 0.5) + : boxShadow([0, 0], [0, 3.2], white.main, 0.5); + + // border color value + let borderColorValue = palette[color] + ? palette[color].main + : rgba(white.main, 0.75); + + if (color === 'white') { + borderColorValue = rgba(white.main, 0.75); + } + + return { + background: backgroundValue, + color: colorValue, + borderColor: borderColorValue, + + '&:hover': { + background: transparent.main, + borderColor: colorValue, + }, + + '&:focus:not(:hover)': { + background: transparent.main, + boxShadow: boxShadowValue, + }, + + '&:active:not(:hover)': { + backgroundColor: colorValue, + color: white.main, + opacity: 0.85, + }, + + '&:disabled': { + color: colorValue, + borderColor: colorValue, + }, + }; + }; + const gradientStyles = () => { + const backgroundValue = + color === 'white' || !gradients[color] + ? white.main + : linearGradient(gradients[color].main, gradients[color].state); + const boxShadowValue = colored[color] + ? `${boxShadow([0, 3], [3, 0], palette[color].main, 0.15)}, ${boxShadow( + [0, 3], + [1, -2], + palette[color].main, + 0.2 + )}, ${boxShadow([0, 1], [5, 0], palette[color].main, 0.15)}` + : 'none'; + const hoveredBoxShadowValue = colored[color] + ? `${boxShadow([0, 14], [26, -12], palette[color].main, 0.4)}, ${boxShadow( + [0, 4], + [23, 0], + palette[color].main, + 0.15 + )}, ${boxShadow([0, 8], [10, -5], palette[color].main, 0.2)}` + : 'none'; + let colorValue = white.main; + if (color === 'white') { + colorValue = text.main; + } else if (color === 'light') { + colorValue = gradients.dark.state; + } + + return { + background: backgroundValue, + color: colorValue, + boxShadow: boxShadowValue, + + '&:hover': { + backgroundColor: white.main, + boxShadow: hoveredBoxShadowValue, + }, + + '&:focus:not(:hover)': { + boxShadow: boxShadowValue, + }, + + '&:disabled': { + background: backgroundValue, + color: colorValue, + }, + }; + }; + const textStyles = () => { + const colorValue = palette[color] ? palette[color].main : white.main; + const focusedColorValue = palette[color] + ? palette[color].focus + : white.focus; + + return { + color: colorValue, + + '&:hover': { + color: focusedColorValue, + }, + + '&:focus:not(:hover)': { + color: focusedColorValue, + }, + }; + }; + const circularStyles = () => ({ + borderRadius: borderRadius.section, + }); + const iconOnlyStyles = () => { + let sizeValue = pxToRem(38); + if (size === 'small') { + sizeValue = pxToRem(25.4); + } else if (size === 'large') { + sizeValue = pxToRem(52); + } + + // padding value + let paddingValue = `${pxToRem(11)} ${pxToRem(11)} ${pxToRem(10)}`; + + if (size === 'small') { + paddingValue = pxToRem(4.5); + } else if (size === 'large') { + paddingValue = pxToRem(16); + } + + return { + width: sizeValue, + minWidth: sizeValue, + height: sizeValue, + minHeight: sizeValue, + padding: paddingValue, + + '& .material-icons': { + marginTop: 0, + }, + + '&:hover, &:focus, &:active': { + transform: 'none', + }, + }; + }; + const holoStyles = () => { + const backgroundValue = palette[color] + ? palette[color].main + : greenAccent.light; + const focusedBackgroundValue = palette[color] + ? palette[color].focus + : white.focus; + const hoveredBackgroundValue = palette[color] + ? rgba(palette[color].main, 0.15) + : 'rgba(0, 0, 0, 0.075)'; + const boxShadowValue = `0 0 0 4px ${rgba(palette[color].secondary || 'white', 0.4)}`; + const hoveredBoxShadowValue = `0 0 0 4px ${rgba(palette[color].secondary || 'white', 0.15)}`; + // const boxShadowValue = colored[color] + // ? `${boxShadow([0, 3], [3, 0], palette[color].main, 0.15)}, ${boxShadow( + // [0, 3], + // [1, -2], + // palette[color].main, + // 0.2 + // )}, ${boxShadow([0, 1], [5, 0], palette[color].main, 0.15)}` + // : 'none'; + // const hoveredBoxShadowValue = colored[color] + // ? `${boxShadow([0, 14], [26, -12], palette[color].main, 0.4)}, ${boxShadow( + // [0, 4], + // [23, 0], + // palette[color].main, + // 0.15 + // )}, ${boxShadow([0, 8], [10, -5], palette[color].main, 0.2)}` + // : 'none'; + let colorValue = white.main; + if (color === 'default' || !palette[color]) { + colorValue = text.main; + } else if (color === 'white' || color === 'light') { + colorValue = dark.main; + } else if ( + color === 'primary' || + color === 'error' || + color === 'dark' || + color === 'success' + ) { + colorValue = white.main; + } + let focusedColorValue = white.main; + + if (color === 'default') { + focusedColorValue = text.main; + } else if (color === 'white') { + focusedColorValue = dark.main; + } else if ( + color === 'primary' || + color === 'error' || + color === 'dark' || + color === 'secondary' || + color === 'success' || + color === 'info' || + color === 'warning' || + color === 'greenAccent' + ) { + focusedColorValue = white.main; + } + + return { + position: 'relative', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + borderRadius: theme.borderRadius, + transitionProperty: 'color, background, box-shadow', + transitionDuration: '0.35s', + fontSize: pxToRem(14), + background: `${backgroundValue} !important`, // Use !important to override any later conflicting styles + color: `${colorValue} !important`, // Use !important to override any later conflicting styles + + // color: colorValue, + boxShadow: boxShadowValue, + + '&:hover': { + backgroundColor: backgroundValue, + boxShadow: hoveredBoxShadowValue, + }, + + '&:focus:not(:hover)': { + backgroundColor: focusedBackgroundValue, + boxShadow: palette[color] + ? boxShadow([0, 0], [0, 3.2], palette[color].main, 0.5) + : boxShadow([0, 0], [0, 3.2], white.main, 0.5), + }, + + '&:disabled': { + backgroundColor: backgroundValue, + color: focusedColorValue, + }, + }; + }; + + // const baseStyle = { + // // position: 'relative', + // // display: 'flex', + // // alignItems: 'center', + // // justifyContent: 'center', + // // width: '100%', + // // minWidth: calculateStyles(size).width, + // // padding: calculateStyles(size).padding, + // // minWidth: customSize === 'md' ? 100 : customSize === 'sm' ? 75 : 140, + // // padding: `1.035rem ${theme.lenMd1}`, + // // borderRadius: theme.borderRadius, + // // fontSize: calculateStyles(size).fontSize, + // // transitionProperty: 'color, background, box-shadow', + // // transitionDuration: '0.35s', + // // background: !color ? greenAccent.light : palette[color].main, + // // color: !customTextColor ? grey.blueGrey : 'white', + // // boxShadow: isDefault + // // ? `0 0 0 4px ${rgba(theme.colorDefaultBackground || 'white', 0.74)}` + // // : 'none', + // // ...(isPrimary && { + // // background: theme.colorPrimary, + // // color: theme.colorPrimaryText, + // // boxShadow: `0 0 0 4px ${rgba(theme.colorPrimary || 'white', 0.4)}`, + // // }), + // // ...(isError && { + // // background: themeSettings.palette.error.main, + // // color: theme.colorPrimaryText, + // // boxShadow: `0 0 0 4px ${rgba(themeSettings.palette.error.main || 'white', 0.4)}`, + // // }), + // // ...(isAccent && { + // // background: theme.colorAccent, + // // color: theme.colorAccentText, + // // boxShadow: `0 0 0 4px ${rgba(theme.colorAccent || 'white', 0.4)}`, + // // }), + // // ...(isDisabled && { + // // background: theme.colorDisabledBackground, + // // color: theme.colorDisabledText, + // // cursor: 'not-allowed', + // // }), + // }; + // // const buttonHoverStyle = { + // // position: 'absolute', + // // zIndex: 1, + // // top: 0, + // // left: 0, + // // width: '100%', + // // height: '100%', + // // borderRadius: theme.borderRadius, + // // opacity: 0, + // // pointerEvents: 'none', + // // transition: 'opacity 0.35s', + // // background: customColor + // // ? rgba(customColor, 0.15) // Lighter shade of customColor if provided + // // : 'rgba(0, 0, 0, 0.075)', + + // // ...(isPrimary && { + // // background: rgba(theme.colorPrimary || 'white', 0.15), + // // }), + // // ...(isError && { + // // background: rgba(themeSettings.palette.error.main || 'white', 0.15), + // // }), + // // ...(isAccent && { + // // background: rgba(theme.colorAccent || 'white', 0.15), + // // }), + // // ...(isDisabled && { + // // background: rgba(theme.colorDisabledBackground || 'white', 0.15), + // // }), + // // }; + // // const buttonTextStyle = { + // // position: 'relative', + // // zIndex: 2, + // // }; + // // const getPrimaryStyle = (theme, isPrimary) => ({ + // // background: isPrimary ? theme.colorPrimary : undefined, + // // color: isPrimary ? theme.colorPrimaryText : undefined, + // // }); + + return { + ...(variant === 'contained' && containedStyles()), + ...(variant === 'outlined' && outlinedStyles()), + ...(variant === 'gradient' && gradientStyles()), + ...(variant === 'text' && textStyles()), + ...(circular && circularStyles()), + ...(iconOnly && iconOnlyStyles()), + ...(variant === 'holo' && holoStyles()), + // ...(variant === 'accent' && accentStyles(theme, true)), + // ...(variant === 'default' && defaultStyles(theme, true)), + // ...(variant === 'disabled' && disabledStyles(theme, true)), + // ...(variant === 'error' && errorStyles(theme, true)), + // ...(variant === 'success' && successStyles(theme, true)), + }; +}); diff --git a/src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonGradient.jsx b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonGradient.jsx new file mode 100644 index 0000000..54d0aae --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonGradient.jsx @@ -0,0 +1,47 @@ +import Container from '@mui/material/Container'; +import Grid from '@mui/material/Grid'; +import Stack from '@mui/material/Stack'; +import { MDBox } from '../../MDBOX/index'; +import RCButton from '..'; + +function ButtonsGradient() { + return ( + + + + + + primary + + + secondary + + + info + + + success + + + warning + + + error + + + light + + + dark + + + White + + + + + + ); +} + +export default ButtonsGradient; diff --git a/src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonIconLeft.jsx b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonIconLeft.jsx new file mode 100644 index 0000000..ef244c5 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonIconLeft.jsx @@ -0,0 +1,33 @@ +import Container from '@mui/material/Container'; +import Grid from '@mui/material/Grid'; +import Stack from '@mui/material/Stack'; +import Icon from '@mui/material/Icon'; +import { MDBox } from '../../MDBOX/index'; +import RCButton from '..'; + +function ButtonsIconLeft() { + return ( + + + + + + favorite + small + + + favorite + default + + + favorite + large + + + + + + ); +} + +export default ButtonsIconLeft; diff --git a/src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonIconRight.jsx b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonIconRight.jsx new file mode 100644 index 0000000..249eb51 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/examples/ButtonIconRight.jsx @@ -0,0 +1,32 @@ +import Container from '@mui/material/Container'; +import Grid from '@mui/material/Grid'; +import Stack from '@mui/material/Stack'; +import Icon from '@mui/material/Icon'; +import { MDBox } from '../../MDBOX/index'; +import RCButton from '..'; +function ButtonsIconRight() { + return ( + + + + + + small + favorite + + + default + favorite + + + large + favorite + + + + + + ); +} + +export default ButtonsIconRight; diff --git a/src/layout/REUSABLE_COMPONENTS/RCBUTTON/index.jsx b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/index.jsx new file mode 100644 index 0000000..52987b3 --- /dev/null +++ b/src/layout/REUSABLE_COMPONENTS/RCBUTTON/index.jsx @@ -0,0 +1,105 @@ +import { forwardRef } from 'react'; +import PropTypes from 'prop-types'; +import RCButtonRoot from './RCButtonRoot'; +import { Box } from '@mui/material'; +// Wrapper component for when you need a container around the button +const ButtonContainer = ({ children, withContainer }) => ( + + {children} + +); +const RCButton = forwardRef( + ( + { + color, + variant, + size, + circular, + iconOnly, + children, + withContainer, + // isPrimary, + // isAccent, + // isDefault, + // isDisabled, + // isError, + // customColor, + // customTextColor, + // customSize, + ...rest + }, + ref + ) => { + const ButtonContent = ( + + {children} + + ); + + if (withContainer) { + return ( + + {ButtonContent} + + ); + } + return ButtonContent; + } +); + +RCButton.displayName = 'RCButton'; +RCButton.defaultProps = { + size: 'medium', + variant: 'contained', + color: 'white', + circular: false, + iconOnly: false, + withContainer: false, // Default value for withContainer prop +}; +RCButton.propTypes = { + size: PropTypes.oneOf(['small', 'medium', 'large']), + variant: PropTypes.oneOf([ + 'text', + 'contained', + 'outlined', + 'gradient', + 'holo', + // 'primary', + // 'accent', + // 'default', + // 'disabled', + // 'error', + ]), + color: PropTypes.oneOf([ + 'default', + 'white', + 'greenAccent', + 'secondary', + 'info', + 'success', + 'warning', + 'error', + 'light', + 'dark', + ]), + circular: PropTypes.bool, + iconOnly: PropTypes.bool, + withContainer: PropTypes.bool, // Add withContainer to the prop types + children: PropTypes.node.isRequired, +}; + +export default RCButton; diff --git a/src/layout/REUSABLE_COMPONENTS/RCInput/RCInputRoot.jsx b/src/layout/REUSABLE_COMPONENTS/RCInput/RCInputRoot.jsx index 9762bcd..390ab54 100644 --- a/src/layout/REUSABLE_COMPONENTS/RCInput/RCInputRoot.jsx +++ b/src/layout/REUSABLE_COMPONENTS/RCInput/RCInputRoot.jsx @@ -58,7 +58,7 @@ export default styled(TextField)(({ ownerState }) => { return { // color: 'white', backgroundColor: disabled - ? `${theme.palette.backgroundA.default} !important` + ? `${theme.palette.greenAccent.lighter} !important` : transparent.main, pointerEvents: disabled ? 'none' : 'auto', ...(error && errorStyles()), diff --git a/src/layout/REUSABLE_COMPONENTS/StatBox.jsx b/src/layout/REUSABLE_COMPONENTS/StatBox.jsx index 6eeb582..1bca445 100644 --- a/src/layout/REUSABLE_COMPONENTS/StatBox.jsx +++ b/src/layout/REUSABLE_COMPONENTS/StatBox.jsx @@ -11,27 +11,13 @@ const StatBox = ({ title, subtitle, icon, progress, increase, wrapIcon }) => { const green = colors.greenAccent.default; const greenliht = colors.greenAccent.light; const grey = colors.grey.default; - const IconWrapper = ({ children }) => ( - - {children} - - ); return ( @@ -43,11 +29,9 @@ const StatBox = ({ title, subtitle, icon, progress, increase, wrapIcon }) => { borderRadius: theme.spacing(4), }} > - {/* */} - {/* */} {paths?.map((d, i) => ( diff --git a/src/layout/REUSABLE_COMPONENTS/unique/SimpleButton.jsx b/src/layout/REUSABLE_COMPONENTS/unique/SimpleButton.jsx index 4f5e542..181545d 100644 --- a/src/layout/REUSABLE_COMPONENTS/unique/SimpleButton.jsx +++ b/src/layout/REUSABLE_COMPONENTS/unique/SimpleButton.jsx @@ -28,7 +28,7 @@ const SimpleButton = ({ const { theme: themeSettings } = useMode(); const calculateStyles = (size) => { switch (size) { - case 'sm': + case 'small': return { width: '75px', // p: '1rem', @@ -36,12 +36,18 @@ const SimpleButton = ({ // m: '0.5rem', fontSize: '0.875rem', }; - case 'md': + case 'medium': return { width: '100px', padding: '0.75rem 1rem', fontSize: '1rem', }; + case 'large': + return { + width: '125px', + padding: '1rem 1.5rem', + fontSize: '1.25rem', + }; default: return { width: '140px', @@ -91,7 +97,6 @@ const SimpleButton = ({ cursor: 'not-allowed', }), }; - const buttonHoverStyle = { position: 'absolute', zIndex: 1, @@ -119,7 +124,6 @@ const SimpleButton = ({ background: rgba(theme.colorDisabledBackground || 'white', 0.15), }), }; - const buttonTextStyle = { position: 'relative', zIndex: 2, diff --git a/src/layout/REUSABLE_COMPONENTS/unique/SimpleSectionHeader.jsx b/src/layout/REUSABLE_COMPONENTS/unique/SimpleSectionHeader.jsx index e6d83ba..1eacaa5 100644 --- a/src/layout/REUSABLE_COMPONENTS/unique/SimpleSectionHeader.jsx +++ b/src/layout/REUSABLE_COMPONENTS/unique/SimpleSectionHeader.jsx @@ -75,7 +75,7 @@ const SimpleSectionHeader = ({ ({ marginTop: theme.spacing(2), padding: theme.spacing(3), borderRadius: theme.shape.borderRadius, - background: theme.palette.backgroundB.dark, + background: theme.palette.grey.simpleGrey, boxShadow: theme.shadows[10], marginBottom: theme.spacing(4), transition: 'all 0.3s ease-in-out', // smooth all transitions @@ -38,7 +38,7 @@ export const StyledContainerBoxSecondary = styled(Box)(({ theme }) => ({ marginTop: theme.spacing(2), padding: theme.spacing(3), borderRadius: theme.shape.borderRadius, - background: theme.palette.backgroundE.light, + background: theme.palette.greenAccent.lighterSeaGreen, boxShadow: theme.shadows[10], marginBottom: theme.spacing(4), transition: 'all 0.3s ease-in-out', // smooth all transitions @@ -50,7 +50,7 @@ export const StyledContainerBox = styled(Box)(({ theme }) => ({ marginTop: theme.spacing(2), padding: theme.spacing(3), borderRadius: theme.shape.borderRadius, - background: theme.palette.backgroundD.dark, + background: theme.palette.greenAccent.crystalGreen, boxShadow: theme.shadows[10], marginBottom: theme.spacing(4), transition: 'all 0.3s ease-in-out', // smooth all transitions @@ -60,7 +60,7 @@ export const StyledPaperPrimary = styled(Paper)(({ theme }) => ({ padding: theme.spacing(2), borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[4], - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, color: theme.palette.text.primary, display: 'flex', flexDirection: 'column', @@ -73,30 +73,12 @@ export const StyledPaper = styled(Box)(({ theme }) => ({ justifyContent: 'center', mx: 'auto', padding: '1rem', - background: theme.palette.backgroundC.dark, + background: theme.palette.grey.clearGrey, // maxWidth: '1200px', borderRadius: '8px', boxShadow: '0 4px 8px 0 rgba(0,0,0,0.2)', transition: 'all 0.3s ease-in-out', // smooth all transitions })); -// BUTTON: #4cceac - transparent -// export const StyledButton = styled(MDButton)(({ theme }) => ({ -// // background: theme.palette.backgroundE.darker, -// // borderColor: theme.palette.backgroundB.darkest, -// borderWidth: 2, -// flexGrow: 1, -// justifySelf: 'bottom', -// bottom: 0, -// mx: 1, -// width: '70%', -// '&: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}`, -// }, -// })); // ! DIALOG STYLES export const StyledDialog = styled(Dialog)(({ theme }) => ({ @@ -109,7 +91,7 @@ export const StyledDialog = styled(Dialog)(({ theme }) => ({ // width: 240, width: '100%', padding: 0, - background: theme.palette.backgroundC.dark, + background: theme.palette.grey.clearGrey, boxShadow: '0 4px 8px 0 rgba(0,0,0,0.2)', transition: 'all 0.3s ease-in-out', // smooth all transitions // mx: 'auto', @@ -144,7 +126,6 @@ export const StyledDialog = styled(Dialog)(({ theme }) => ({ justifyContent: 'flex-end', }, })); - export const DialogPaper = styled(Paper)(({ theme }) => ({ display: 'flex', flexDirection: 'column', @@ -169,7 +150,7 @@ export const DialogContentsBox = styled(Box)(({ theme }) => ({ width: '100%', // minWidth: '500px', // borderRadius: theme.shape.borderRadius, - background: theme.palette.backgroundE.lighter, + background: theme.palette.greenAccent.evenLighter, boxShadow: theme.shadows[10], transition: 'all 0.3s ease-in-out', // smooth all transitions '@media (max-width:600px)': { @@ -183,7 +164,7 @@ export const StyledDialogContent = styled(DialogContent)(({ theme }) => ({ width: '100%', gap: theme.spacing(2), padding: theme.spacing(3), - // backgroundColor: theme.palette.backgroundA.lightest, + // backgroundColor: theme.palette.greenAccent.contrastText, })); export const StyledDialogActions = styled(DialogActions)(({ theme }) => ({ display: 'flex', @@ -192,7 +173,7 @@ export const StyledDialogActions = styled(DialogActions)(({ theme }) => ({ width: '100%', gap: theme.spacing(2), padding: theme.spacing(3), - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, })); export const FormBox = styled(Box)(({ theme }) => ({ display: 'flex', @@ -203,7 +184,7 @@ export const FormBox = styled(Box)(({ theme }) => ({ mx: 'auto', padding: '1rem', background: 'rgba(255, 255, 255, 0.2)', // Adjust for desired translucency - backdropFilter: 'blur(20px)', + // backdropFilter: 'blur(20px)', boxShadow: '0 4px 8px 0 rgba(0,0,0,0.2)', transition: 'all 0.3s ease-in-out', borderRadius: '20px', @@ -219,20 +200,8 @@ export const FormPaper = styled(Paper)(({ theme }) => ({ flexGrow: 1, width: '100%', height: '100%', - background: theme.palette.backgroundE.lighter, + background: theme.palette.greenAccent.evenLighter, borderRadius: '16px', - // display: 'flex', - // flexDirection: 'column', - // alignItems: 'center', - // justifyContent: 'center', - // width: '100%', - // mx: 'auto', - // padding: '1rem', - // background: theme.palette.backgroundC.dark, - // // maxWidth: '1200px', - // borderRadius: '8px', - // boxShadow: '0 4px 8px 0 rgba(0,0,0,0.2)', - // transition: 'all 0.3s ease-in-out', // smooth all transitions })); export const FormFieldBox = styled(Box)(({ theme }) => ({ m: theme.spacing(1), @@ -252,18 +221,18 @@ export const StyledTextField = styled(TextField)(({ theme }) => ({ borderColor: theme.palette.transparent.main, }, '&:hover .MuiOutlinedInput-notchedOutline': { - color: theme.palette.backgroundA.darker, - borderColor: theme.palette.backgroundA.darker, + color: theme.palette.greenAccent.default, + borderColor: theme.palette.greenAccent.default, }, '&.Mui-focused .MuiOutlinedInput-notchedOutline': { - borderColor: theme.palette.backgroundA.darker, + borderColor: theme.palette.greenAccent.default, borderWidth: '2px', // or other width as you like }, }, borderRadius: theme.shape.borderRadius, - color: theme.palette.backgroundA.darkest, + color: theme.palette.greenAccent.dark, width: '100%', - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, boxShadow: `0px 2px 4px -1px ${theme.palette.grey[400]}`, marginBottom: theme.spacing(2), })); @@ -292,8 +261,8 @@ export const CardIconWrapper = styled('div')(({ theme }) => ({ height: theme.spacing(4), width: theme.spacing(4), borderRadius: '50%', - backgroundColor: theme.palette.backgroundA.lighter, - color: theme.palette.backgroundA.contrastTextD, + backgroundColor: theme.palette.greenAccent.evenLighter, + color: theme.palette.grey.black, '& svg': { fontSize: theme.typography.pxToRem(20), }, @@ -302,7 +271,7 @@ export const CardDetailContainer = styled(Box)(({ theme }) => ({ padding: theme.spacing(2), borderRadius: theme.shape.borderRadius, boxShadow: `0 4px 8px 0 ${theme.palette.shadow}`, - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, display: 'flex', flexDirection: 'column', gap: theme.spacing(1), @@ -323,7 +292,7 @@ export const StyledCardContent = styled(CardContent)(({ theme }) => ({ flexGrow: 1, textAlign: 'left', minHeight: '50px', // Adjust based on the size of the text - border: `1px solid ${theme.palette.backgroundB.lighter}`, + border: `1px solid ${theme.palette.grey.lighterSimpleGrey}`, borderRadius: theme.shape.borderRadius, // Media queries for padding padding: theme.spacing(1), // default padding @@ -348,7 +317,7 @@ export const StyledCard = styled(Card)(({ theme }) => ({ maxHeight: '100%', // Adjusted for better height management // height: 'auto', flexGrow: 1, - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[5], justifyContent: 'center', diff --git a/src/layout/cart/CartContent.js b/src/layout/cart/CartContent.js index a5d0752..7da104f 100644 --- a/src/layout/cart/CartContent.js +++ b/src/layout/cart/CartContent.js @@ -1,19 +1,19 @@ import React from 'react'; import { Typography, Skeleton, Box, Grid, Container } from '@mui/material'; -import { useCartStore } from '../../context/MAIN_CONTEXT/CartContext/CartContext'; -import { useMode, useUserContext } from '../../context'; +import { useMode } from '../../context'; import GenericCard from '../../components/cards/GenericCard'; +import { useCartManager } from '../../context/MAIN_CONTEXT/CartContext/useCartManager'; const CartContent = () => { const { theme } = useMode(); - const { cartData, isLoading } = useCartStore(); + const { cart } = useCartManager(); return ( { }} > - {cartData?.cart?.map((card, index) => ( + {cart?.items?.map((card, index) => ( {console.log(card)} { setActiveStep(activeStep + 1); }; diff --git a/src/layout/cart/cartPageContainers/Review.jsx b/src/layout/cart/cartPageContainers/Review.jsx index 0350975..454813b 100644 --- a/src/layout/cart/cartPageContainers/Review.jsx +++ b/src/layout/cart/cartPageContainers/Review.jsx @@ -4,32 +4,7 @@ import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; import Grid from '@mui/material/Grid'; -import { useCartStore } from '../../../context'; - -// const products = [ -// { -// name: 'Product 1', -// desc: 'A nice thing', -// price: '$9.99', -// }, -// { -// name: 'Product 2', -// desc: 'Another thing', -// price: '$3.45', -// }, -// { -// name: 'Product 3', -// desc: 'Something else', -// price: '$6.51', -// }, -// { -// name: 'Product 4', -// desc: 'Best thing of all', -// price: '$14.11', -// }, -// { name: 'Shipping', desc: '', price: 'Free' }, -// ]; - +import { useCartManager } from '../../../context/MAIN_CONTEXT/CartContext/useCartManager'; const addresses = ['1 MUI Drive', 'Reactville', 'Anytown', '99999', 'USA']; const payments = [ { name: 'Card type', detail: 'Visa' }, @@ -39,7 +14,7 @@ const payments = [ ]; export default function Review() { - const { cartData } = useCartStore(); + const { cart } = useCartManager(); return ( @@ -47,7 +22,7 @@ export default function Review() { Order summary - {cartData?.cart?.map((product, index) => ( + {cart?.items?.map((product, index) => ( {product.price} diff --git a/src/layout/collection/collectionGrids/ChartGridLayout.jsx b/src/layout/collection/collectionGrids/ChartGridLayout.jsx index 867007b..ebabf9c 100644 --- a/src/layout/collection/collectionGrids/ChartGridLayout.jsx +++ b/src/layout/collection/collectionGrids/ChartGridLayout.jsx @@ -21,11 +21,12 @@ import useTimeRange from '../../../components/forms/selectors/useTimeRange'; import useSkeletonLoader from '../../REUSABLE_COMPONENTS/useSkeletonLoader'; import { ChartConfiguration } from './cards-chart/ChartConfigs'; import { TopCardsDisplayRow } from '../sub-components/TopCardsDisplayRow'; -import LoadingOverlay from '../../LoadingOverlay'; +import LoadingOverlay from '../../REUSABLE_COMPONENTS/LoadingOverlay'; import RCWrappedIcon from '../../REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIcon'; import { ResponsiveContainer } from 'recharts'; import PricedDataTable from './cards-datatable/PricedDataTable'; import preparePortfolioTableData from '../data/portfolioData'; +import { calculateChangePercentage } from '../../../context/Helpers'; const renderCardContainer = (content) => { return ( @@ -75,6 +76,7 @@ const ChartGridLayout = ({ selectedCards, removeCard }) => { defaultValue: 5, entries: [5, 10, 15, 20], }; + return ( @@ -92,21 +94,20 @@ const ChartGridLayout = ({ selectedCards, removeCard }) => { > show_chart } - sideText="+4%" + sideText={`Change: ${calculateChangePercentage(selectedCollection)}`} /> {renderCardContainer( @@ -180,7 +181,7 @@ const ChartGridLayout = ({ selectedCards, removeCard }) => { // color="success" color="white" sx={{ - background: greenAccent, + background: theme.palette.success.main, }} > list diff --git a/src/layout/collection/collectionGrids/cards-chart/ChartConfigs.jsx b/src/layout/collection/collectionGrids/cards-chart/ChartConfigs.jsx index 7cef160..b66986d 100644 --- a/src/layout/collection/collectionGrids/cards-chart/ChartConfigs.jsx +++ b/src/layout/collection/collectionGrids/cards-chart/ChartConfigs.jsx @@ -1,14 +1,11 @@ import { ResponsiveLine } from '@nivo/line'; -import { useChartContext, useMode } from '../../../../context'; -import { - CustomTooltipLayer, - useEventHandlers, -} from '../../../../context/MAIN_CONTEXT/ChartContext/helpers'; +import { useMode } from '../../../../context'; import { useEffect, useMemo } from 'react'; import NivoContainer from '../../../REUSABLE_COMPONENTS/NivoContainer'; import PropTypes from 'prop-types'; import ChartErrorBoundary from './ChartErrorBoundary'; import { BasicTooltip } from '@nivo/tooltip'; +import { useEventHandlers } from '../../../../context/hooks/useEventHandlers'; const formatDateBasedOnRange = (range) => { const formatMap = { '24hr': { format: '%H:%M', ticks: 'every hour' }, diff --git a/src/layout/collection/collectionGrids/cards-datatable/PricedDataTable.jsx b/src/layout/collection/collectionGrids/cards-datatable/PricedDataTable.jsx index 963d8b2..bb2575d 100644 --- a/src/layout/collection/collectionGrids/cards-datatable/PricedDataTable.jsx +++ b/src/layout/collection/collectionGrids/cards-datatable/PricedDataTable.jsx @@ -3,9 +3,6 @@ import PropTypes from 'prop-types'; import { DataGrid, GridToolbar } from '@mui/x-data-grid'; import { Box } from '@mui/material'; import { useMode } from '../../../../context'; -import OptionsComponent from '../../../../components/forms/OptionsComponent'; -import GenericActionButtons from '../../../../components/buttons/actionButtons/GenericActionButtons'; -import { enqueueSnackbar } from 'notistack'; function PricedDataTable({ entriesPerPage, canSearch, table }) { const { theme } = useMode(); diff --git a/src/layout/collection/collectionGrids/collections-list/CollectionListItem.jsx b/src/layout/collection/collectionGrids/collections-list/CollectionListItem.jsx index e76be12..2cdadbf 100644 --- a/src/layout/collection/collectionGrids/collections-list/CollectionListItem.jsx +++ b/src/layout/collection/collectionGrids/collections-list/CollectionListItem.jsx @@ -4,6 +4,7 @@ import { Card, CardActionArea, CardContent, + Collapse, Grid, Tooltip, useMediaQuery, @@ -11,150 +12,204 @@ import { import PropTypes from 'prop-types'; import MDBox from '../../../../layout/REUSABLE_COMPONENTS/MDBOX'; import useCollectionManager from '../../../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; -import useSelectedCollection from '../../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; import useDialogState from '../../../../context/hooks/useDialogState'; import { useMode } from '../../../../context'; import CollectionDialog from '../../../../components/dialogs/CollectionDialog'; -import LongMenu from '../../../../layout/navigation/LongMenu'; import RCChange from '../../../REUSABLE_COMPONENTS/RC/RCChange'; import RCInfoItem from '../../../REUSABLE_COMPONENTS/RCInfoItem'; import { roundToNearestTenth } from '../../../../context/Helpers'; -import SimpleButton from '../../../REUSABLE_COMPONENTS/unique/SimpleButton'; -import uniqueTheme from '../../../REUSABLE_COMPONENTS/unique/uniqueTheme'; - -const CollectionListItem = memo(({ collection }) => { - const { theme } = useMode(); - const isMobile = useMediaQuery(theme.breakpoints.down('sm')); - const { deleteCollection } = useCollectionManager(); - const { handleSelectCollection, selectedCollection } = - useSelectedCollection(); - const { dialogState, openDialog, closeDialog } = useDialogState(); +import LoadingOverlay from '../../../REUSABLE_COMPONENTS/LoadingOverlay'; +import useSelectedCollection from '../../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +import RCButton from '../../../REUSABLE_COMPONENTS/RCBUTTON'; +import { Divider } from '@mui/joy'; - const percentageChange = - collection?.collectionStatistics?.percentageChange || 0; +const CollectionListItem = memo( + ({ collection, handleSelectAndShowCollection }) => { + const { theme } = useMode(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + const { handleSelectCollection } = useSelectedCollection(); + const { deleteCollection } = useCollectionManager(); + const { dialogState, openDialog, closeDialog } = useDialogState(); + if (!collection) { + // Handle the scenario where collectionData is null + // Maybe set some default state or perform error handling + return ; + } + const percentageChange = + collection?.collectionStatistics?.percentageChange || 0; - const handleDelete = useCallback( - async (event) => { - event.stopPropagation(); // Prevent triggering the selection when clicking delete - await deleteCollection(collection?._id); - }, - [deleteCollection, collection?._id] - ); + const handleDelete = useCallback( + async (event) => { + event.stopPropagation(); // Prevent triggering the selection when clicking delete + await deleteCollection(collection?._id); + }, + [deleteCollection, collection?._id] + ); - const handleEdit = useCallback( - (event) => { - event.stopPropagation(); // Prevent triggering the selection when clicking edit - // handleSelectCollection(collection); - openDialog('isEditCollectionDialogOpen'); - }, - [openDialog] - ); + const handleEdit = useCallback( + (event) => { + event.stopPropagation(); // Prevent triggering the selection when clicking edit + // handleSelectCollection(collection); + openDialog('isEditCollectionDialogOpen'); + }, + [openDialog] + ); - const handleSelection = useCallback(() => { - handleSelectCollection(collection); - }, [handleSelectCollection, collection]); - return ( - - - - - - - - - 0} - change={percentageChange} - rangeLevel="24hr" // Default value; replace with actual data if available - /> - - - - { + handleSelectCollection(collection); + handleSelectAndShowCollection(collection); + }, [handleSelectCollection, collection]); + return ( + + { + // handleSelectAndShowCollection(collection); + // }} sx={{ - width: '20%', - maxWidth: 200, - flexGrow: 1, ...(isMobile && { - flexDirection: 'column', // Adjust layout for mobile + boxShadow: 'none', // Remove shadow + '&:hover': { + boxShadow: 'none', + }, }), }} > - - + { + handleSelectAndShowCollection(collection); + }} + sx={{ + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + p: 'none !!important', + flexGrow: 1, + ...(isMobile && { + flexDirection: 'column', // Adjust layout for mobile + }), + }} > - Delete - - + + + + + 0} + change={percentageChange} + rangeLevel="24hr" // Default value; replace with actual data if available + /> + + + + - Edit - + + + Delete + + + + Edit + + {/* + Delete + + + Edit + */} + + - - - closeDialog('isEditCollectionDialogOpen')} - collectionData={{ - _id: collection?._id, - name: collection?.name, - description: collection?.description, - }} - isNew={false} - /> - - ); -}); + closeDialog('isEditCollectionDialogOpen')} + collectionData={{ + _id: collection?._id, + name: collection?.name, + description: collection?.description, + }} + isNew={false} + /> +
+ + ); + } +); CollectionListItem.displayName = 'CollectionListItem'; diff --git a/src/layout/collection/collectionGrids/collections-list/SelectCollectionHeader.jsx b/src/layout/collection/collectionGrids/collections-list/SelectCollectionHeader.jsx index 9b30a9c..2d7882c 100644 --- a/src/layout/collection/collectionGrids/collections-list/SelectCollectionHeader.jsx +++ b/src/layout/collection/collectionGrids/collections-list/SelectCollectionHeader.jsx @@ -1,17 +1,13 @@ import React, { useEffect } from 'react'; import { Grid, Button, Box, useMediaQuery } from '@mui/material'; -import { - useAuthContext, - useFormContext, - useMode, - useUserContext, -} from '../../../../context'; +import { useFormContext } from '../../../../context'; 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 SimpleSectionHeader from '../../../REUSABLE_COMPONENTS/unique/SimpleSectionHeader'; import { PageHeaderSkeleton } from '../../../REUSABLE_COMPONENTS/SkeletonVariants'; +import RCButton from '../../../REUSABLE_COMPONENTS/RCBUTTON'; +import useUserData from '../../../../context/MAIN_CONTEXT/UserContext/useUserData'; const FlexContainer = styled(Box)` display: flex; @@ -21,22 +17,9 @@ const FlexContainer = styled(Box)` padding: ${({ theme }) => theme.spacing(1, 2)}; `; -const HeaderContainer = styled(Box)` - flex: 1; - max-width: 50%; -`; - -// const ButtonContainer = styled(Box)` -// flex: 1; -// display: flex; -// justify-content: flex-end; -// max-width: 50%; -// `; - const SelectCollectionHeader = ({ openNewDialog }) => { - const { theme } = useMode(); const { setCurrentForm } = useFormContext(); - const { user } = useUserContext(); + const { user } = useUserData(); if (!user) { return ; } @@ -59,7 +42,7 @@ const SelectCollectionHeader = ({ openNewDialog }) => { day: 'numeric', })} /> - { @@ -68,7 +51,19 @@ const SelectCollectionHeader = ({ openNewDialog }) => { }} > Add New Collection - + */} + { + setCurrentForm('addCollectionForm'); + openNewDialog(); + }} + > + Add New Collection + ); diff --git a/src/layout/collection/collectionGrids/collections-list/SelectCollectionList.jsx b/src/layout/collection/collectionGrids/collections-list/SelectCollectionList.jsx index 01f9bc9..a31b44b 100644 --- a/src/layout/collection/collectionGrids/collections-list/SelectCollectionList.jsx +++ b/src/layout/collection/collectionGrids/collections-list/SelectCollectionList.jsx @@ -17,20 +17,33 @@ import SimpleCard from '../../../REUSABLE_COMPONENTS/unique/SimpleCard'; import uniqueTheme from '../../../REUSABLE_COMPONENTS/unique/uniqueTheme'; import { CollectionListItemSkeleton } from '../../../REUSABLE_COMPONENTS/SkeletonVariants'; import { useMode } from '../../../../context'; +import LoadingOverlay from '../../../REUSABLE_COMPONENTS/LoadingOverlay'; const SelectCollectionList = ({ handleSelectAndShowCollection }) => { const { theme } = useMode(); const isMobile = useMediaQuery(theme.breakpoints.down('sm')); // Detect mobile screen + const collectionData = useSelectedCollection(); + + if (!collectionData) { + // Handle the scenario where collectionData is null + // Maybe set some default state or perform error handling + return ; + } const { + selectedCollection, allCollections, allIds, - handleSelectCollection, - toggleShowCollections, - } = useSelectedCollection(); + refreshCollections, + // other returned values + } = collectionData; const [collectionList, setCollectionList] = useState([]); const numCollections = allIds?.length || 0; const nonSkeletonCount = useRef(0); + // useEffect(() => { + // // This effect will run whenever `allCollections` changes, including when a collection is deleted. + // refreshCollections(); // Fetch the latest collections whenever the component mounts or updates. + // }, [refreshCollections]); // Dependency on `allCollections.length` to trigger re-fetching. useEffect(() => { const minItems = 5; @@ -38,6 +51,15 @@ const SelectCollectionList = ({ handleSelectAndShowCollection }) => { minItems - numCollections > 0 ? minItems - numCollections : 0; nonSkeletonCount.current = numCollections; + // const allSkeletonCollections = [...Array(numRequired).keys()].map( + // (index) => ( + // + // ) + // ); const allSkeletonCollections = [...Array(numRequired).keys()].map( (index) => ( { ) ); const combinedCollections = allCollections - .map((collection, index) => ( - - { - handleSelectAndShowCollection(collection); - }} - sx={{ - ...(isMobile && { - boxShadow: 'none', // Remove shadow - '&:hover': { - boxShadow: 'none', - }, - }), - }} - > - - - + ?.map((collection, index) => ( + )) .concat(allSkeletonCollections); @@ -89,13 +97,14 @@ const SelectCollectionList = ({ handleSelectAndShowCollection }) => { > - {collectionList?.map((item, index) => + {collectionList?.map((item, index) => item)} + {/* {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 14ed242..0eea2a6 100644 --- a/src/layout/collection/collectionGrids/collections-list/StatBoard.jsx +++ b/src/layout/collection/collectionGrids/collections-list/StatBoard.jsx @@ -1,9 +1,6 @@ /* eslint-disable react/jsx-key */ import { Box, Grid, Typography, Skeleton, useMediaQuery } from '@mui/material'; -// import PieChart from './statItems/PieChart'; 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 { useAppContext, useMode } from '../../../../context'; @@ -12,6 +9,8 @@ import PricedCardList from './statItems/PricedCardList'; import MDBox from '../../../REUSABLE_COMPONENTS/MDBOX'; import TotalCardsCollectedStatBox from './statItems/TotalCardsCollectedStatBox'; import FlexBetween from '../../../REUSABLE_COMPONENTS/FlexBetween'; +import useSelectedCollection from '../../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +import { Divider } from '@mui/joy'; const SkeletonPieChart = ({ theme }) => ( ( const StatBoxes = () => { const { theme } = useMode(); return ( - + <> + - + ); }; const DistCircle = () => { const { theme } = useMode(); const colors = theme.palette.chartTheme; - const { allCollections } = useAppContext(); + const { allCollections } = useSelectedCollection(); if (!allCollections || allCollections.length === 0) { - return ( - - - - ); + return ; } - return ( - - - - ); + return ; }; const PriceList = () => { @@ -74,11 +64,7 @@ const PriceList = () => { const colors = theme.palette.chartTheme; const isMobile = useMediaQuery(theme.breakpoints.down('sm')); - return ( - - - - ); + return ; }; const StatBoard = () => { const { theme } = useMode(); @@ -122,7 +108,17 @@ const StatBoard = () => { sx={{ width: '100%', minHeight: '100%', maxHeight: 270 }} > - {component} + + {component} + ) diff --git a/src/layout/collection/collectionGrids/collections-list/statItems/PricedCardList.jsx b/src/layout/collection/collectionGrids/collections-list/statItems/PricedCardList.jsx index a6dc623..a32cd39 100644 --- a/src/layout/collection/collectionGrids/collections-list/statItems/PricedCardList.jsx +++ b/src/layout/collection/collectionGrids/collections-list/statItems/PricedCardList.jsx @@ -6,6 +6,8 @@ import { DataGrid } from '@mui/x-data-grid'; import prepareTableData from '../../../data/topCards'; import styled from 'styled-components'; import { useMemo } from 'react'; +import { useCompileCardData } from '../../../../../context/MISC_CONTEXT/AppContext/useCompileCardData'; +import { Divider } from '@mui/joy'; const PricedCardList = () => { const { theme } = useMode(); @@ -14,59 +16,59 @@ const PricedCardList = () => { 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 topFiveCards = useMemo(() => { - const uniqueCards = new Map(); - cardsWithQuantities.forEach((card) => { - if (!uniqueCards.has(card.id)) { - uniqueCards.set(card.id, card); - } - }); - - const uniqueCardsArray = Array.from(uniqueCards.values()); - - return uniqueCardsArray.sort((a, b) => b.price - a.price).slice(0, 5); - }, [cardsWithQuantities]); + const { collectionMetaData } = useCompileCardData(); const { data, columns } = useMemo( - () => prepareTableData(topFiveCards), - [topFiveCards] + () => prepareTableData(collectionMetaData?.topFiveCards), + [collectionMetaData?.topFiveCards] ); return ( - + - + acc + card.price, 0)}`} + sideText={`$${collectionMetaData?.topFiveCards?.reduce((acc, card) => acc + card.price, 0)}`} colorVariant={greenAccent} useSX={true} titleVariant="h5" paddingVariant={theme.spacing(2)} sx={{ color: greenAccent, + borderRadius: theme.shape.borderRadius, }} /> + { display: 'flex', justifyContent: 'center', alignItems: 'center', - borderRadius: theme.spacing(4), + borderRadius: theme.shape.borderRadius, height: '100%', minHeight: '135px', }} diff --git a/src/layout/collection/collectionGrids/collections-list/statItems/TotalPriceStatBox.jsx b/src/layout/collection/collectionGrids/collections-list/statItems/TotalPriceStatBox.jsx index d2ea418..1c5d144 100644 --- a/src/layout/collection/collectionGrids/collections-list/statItems/TotalPriceStatBox.jsx +++ b/src/layout/collection/collectionGrids/collections-list/statItems/TotalPriceStatBox.jsx @@ -1,19 +1,15 @@ 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'; - +import { roundToNearestTenth } from '../../../../../context/Helpers'; const TotalPriceStatBox = () => { 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), + borderRadius: theme.shape.borderRadius, height: '100%', minHeight: '135px', - // p: '5px', }} > { +const ValuDistributionCircle = () => { const { theme } = useMode(); + const { collectionMetaData } = useCompileCardData(); const colors = theme.palette.chartTheme; - const grey = colors.grey.darkest; - const lightGrey = colors.grey.lightest; - const primary = colors.primary.dark; - const greenAccent = colors.greenAccent.light; - - const collectionMetaData = collections?.reduce( - (meta, collection) => { - meta.totalValue += collection?.totalPrice; - meta.tooltips.push( - `${collection?.name}: $${collection?.totalPrice.toFixed(2)}` - ); - return meta; - }, - { totalValue: 0, tooltips: [] } - ); - const data = collections.map((collection) => ({ - name: collection.name, - value: collection.totalPrice, - })); + const { lightest, darkest, light, dark } = colors.greenAccent; + const greyDark = colors.grey.dark; + const greyDarkest = colors.grey.darkest; + const contrastText = colors.grey.contrastText; + const primaryDark = colors.primary.dark; const COLORS = [ - theme.palette.chartTheme.blueAccent.default, - theme.palette.chartTheme.greenAccent.light, - '#FFBB28', - '#FF8042', + // theme.palette.chartTheme.blueAccent.lightest, + // theme.palette.chartTheme.blueAccent.light, + // theme.palette.chartTheme.blueAccent.default, + // theme.palette.chartTheme.blueAccent.dark, + // theme.palette.chartTheme.blueAccent.darkest, + colors.greenAccent.default, + lightest, + light, + dark, + darkest, ]; + const renderCustomLabel = ({ + cx, + cy, + midAngle, + innerRadius, + outerRadius, + percent, + name, + }) => { + const RADIAN = Math.PI / 180; + const radius = innerRadius + (outerRadius - innerRadius) * 0.5; + const x = cx + radius * Math.cos(-midAngle * RADIAN); + const y = cy + radius * Math.sin(-midAngle * RADIAN); + return ( + cx ? 'start' : 'end'} + dominantBaseline="central" + style={{ fontSize: '0.875rem' }} + > + {`${name}: ${(percent * 100).toFixed(0)}%`} + + ); + }; return ( { flexGrow: 1, // p: 2, maxHeight: 270, + border: 'none', }} > { maxHeight: 270, }} > - + + + show_chart + + + } subtitle="none" sideText="" - colorVariant={greenAccent} + colorVariant={lightest} useSX={true} titleVariant="h5" paddingVariant={theme.spacing(2)} sx={{ - color: greenAccent, + color: lightest, + borderRadius: theme.shape.borderRadius, }} /> @@ -87,26 +137,37 @@ const ValuDistributionCircle = ({ collections }) => { - `${name}: ${(percent * 100).toFixed(0)}%` - } + // textAnchor="middle" + color={contrastText} + innerRadius={18} + outerRadius="80%" + // paddingAngle={2} + label={renderCustomLabel} // Using the custom label function > - {data.map((entry, index) => ( + {collectionMetaData?.pieChartData?.map((entry, index) => ( ))} - + + diff --git a/src/layout/collection/data/portfolioData.jsx b/src/layout/collection/data/portfolioData.jsx index 7bd2585..930fe4c 100644 --- a/src/layout/collection/data/portfolioData.jsx +++ b/src/layout/collection/data/portfolioData.jsx @@ -2,6 +2,7 @@ import React from 'react'; import MDTypography from '../../REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; import GenericActionButtons from '../../../components/buttons/actionButtons/GenericActionButtons'; import { useSnackbar } from 'notistack'; +import { roundToNearestTenth } from '../../../context/Helpers'; // Note: No changes needed for these components const Name = ({ name }) => ( @@ -53,9 +54,6 @@ const Quantity = ({ quantity }) => ( ); export default function preparePortfolioTableData(selectedCards) { - const roundToNearestTenth = (value) => Math.round(value * 10) / 10; - // const { enqueueSnackbar } = useSnackbar(); - const columns = [ { field: 'name', @@ -83,62 +81,17 @@ export default function preparePortfolioTableData(selectedCards) { flex: 1, renderCell: (params) => , }, - // { - // field: 'action', - // headerName: 'Action', - // flex: 1, - // renderCell: (params) => ( - // console.log('clicked')} - // onSuccess={() => - // enqueueSnackbar('Action successful', { - // variant: 'success', - // }) - // } - // onFailure={(error) => - // enqueueSnackbar('Action failed', { - // variant: 'error', - // }) - // } - // page={'Collection'} - // cardSize={'small'} - // /> - // ), - // }, { field: 'action', headerName: 'Action', renderCell: (params) => ( console.log('clicked')} onSuccess={() => console.log('success')} onFailure={() => console.log('failure')} - // onSuccess={() => - // enqueueSnackbar( - // { - // title: 'Action successful', - // message: `Card added to ${params?.name || ''} successfully.`, - // }, - // 'success', - // null - // ) - // } - // onFailure={(error) => - // enqueueSnackbar( - // { - // title: 'Action failed', - // message: `Failed to add card to ${params?.name || ''}.`, - // }, - // 'error', - // error - // ) - // } page={'Collection'} cardSize={'small'} variant="data-table" diff --git a/src/layout/collection/sub-components/CollectionPortfolioHeader.jsx b/src/layout/collection/sub-components/CollectionPortfolioHeader.jsx index 45b1920..27c7e9e 100644 --- a/src/layout/collection/sub-components/CollectionPortfolioHeader.jsx +++ b/src/layout/collection/sub-components/CollectionPortfolioHeader.jsx @@ -12,7 +12,7 @@ import uniqueTheme from '../../REUSABLE_COMPONENTS/unique/uniqueTheme'; import IconStatWrapper from '../../REUSABLE_COMPONENTS/unique/IconStatWrapper'; import DashboardBox from '../../REUSABLE_COMPONENTS/DashboardBox'; import { PageHeaderSkeleton } from '../../REUSABLE_COMPONENTS/SkeletonVariants'; -import RCWrappedIcon from '../../REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIcon'; +import { collectionPortfolioHeaderItems } from '../../../data/collectionPortfolioHeaderItems'; const HeaderItem = ({ icon, label, value, delay }) => { return ( @@ -55,42 +55,8 @@ const CollectionPortfolioHeader = ({ onBack, collection, allCollections }) => { ) { return ; } - const headerItems = [ - { - // icon: , - icon: 'collections', - label: 'Portfolio Selected', - value: collection?.name || 'Select a collection to view its statistics', - delay: 0, - }, - { - // icon: , - icon: 'attach_money', - label: 'Total Value', - value: - collection?.totalPrice || 'Select a collection to view its statistics', - delay: 200, - }, - { - // icon: , - icon: 'format_list_numbered', - label: 'Number of Unique Cards', - value: - collection?.cards?.length || - 'Select a collection to view its statistics', - delay: 400, - }, - { - // icon: , - icon: 'trending_up', - label: "Today's Performance", - value: - collection?.statistics?.percentChange || - 'Select a collection to view its statistics', - delay: 600, - }, - ]; + const items = collectionPortfolioHeaderItems(collection); return ( { - {headerItems?.map((item, index) => ( + {items?.map((item, index) => ( { const { theme } = useMode(); const isMobileView = useMediaQuery(theme.breakpoints.down('sm')); - const { selectedCollection } = useSelectedCollection(); const [activeCardIndex, setActiveCardIndex] = useState(0); - + const { collectionMetaData } = useCompileCardData(); const handleSlideChange = (swiper) => setActiveCardIndex(swiper.realIndex); - - const topFiveCards = useMemo( - () => - selectedCollection?.cards?.sort((a, b) => b.price - a.price).slice(0, 5), - [selectedCollection?.cards] - ); + // const topFiveCards = useMemo( + // () => + // selectedCollection?.cards?.sort((a, b) => b.price - a.price).slice(0, 5), + // [selectedCollection?.cards] + // ); return ( { spacing={2} sx={{ padding: theme.spacing(2), - background: theme.palette.backgroundB.darker, + background: theme.palette.grey.darker, borderRadius: theme.shape.borderRadius, flexDirection: isMobileView ? 'column' : 'row', // Stack items vertically on mobile }} @@ -62,7 +61,7 @@ export const TopCardsDisplayRow = () => { width: '100%', }} > - {topFiveCards?.map((card, index) => ( + {collectionMetaData?.topFiveCards?.map((card, index) => ( { > { component="img" alt={`Image for ${card.name || 'the card'}`} image={card?.image || placeHolder} // || placeHolder - // image={placeHolder} - // src={placeHolder} loading="lazy" style={{ borderRadius: 0, width: 'auto', - // minWidth: 280, - // height: '20%', // Ensure image covers the height on mobile }} /> { const [checked, setChecked] = useState(false); @@ -99,12 +97,13 @@ const DeckListItem = ({ - + + {/* */} @@ -155,14 +154,15 @@ const DeckListItem = ({ {cards && - cards.length > 0 && - cards.map((card) => ( + cards?.length > 0 && + cards?.map((card, index) => ( {/* Adjust breakpoints as needed for responsive design */} - + ))} diff --git a/src/layout/deck/DeckPageHeader.jsx b/src/layout/deck/DeckPageHeader.jsx index 19ae521..859b435 100644 --- a/src/layout/deck/DeckPageHeader.jsx +++ b/src/layout/deck/DeckPageHeader.jsx @@ -1,12 +1,13 @@ import React from 'react'; import styled from 'styled-components'; -import { Box, Card, Grid } from '@mui/material'; -import { useFormContext, useMode, useUserContext } from '../../context'; +import { Box } from '@mui/material'; +import { useFormContext, useMode } from '../../context'; import SimpleCard from '../REUSABLE_COMPONENTS/unique/SimpleCard'; import SimpleSectionHeader from '../REUSABLE_COMPONENTS/unique/SimpleSectionHeader'; -import SimpleButton from '../REUSABLE_COMPONENTS/unique/SimpleButton'; import uniqueTheme from '../REUSABLE_COMPONENTS/unique/uniqueTheme'; import { PageHeaderSkeleton } from '../REUSABLE_COMPONENTS/SkeletonVariants'; +import RCButton from '../REUSABLE_COMPONENTS/RCBUTTON'; +import useUserData from '../../context/MAIN_CONTEXT/UserContext/useUserData'; const FlexContainer = styled(Box)(({ theme }) => ({ display: 'flex', @@ -20,31 +21,10 @@ const FlexContainer = styled(Box)(({ theme }) => ({ }, })); -// Adjusting Header container for mobile view -const HeaderContainer = styled(Box)(({ theme }) => ({ - flex: 1, - maxWidth: '100%', // Allows the header to utilize full width on small screens - [theme.breakpoints.down('sm')]: { - marginBottom: theme.spacing(2), - }, -})); - -// Button container adjusted for mobile view -const ButtonContainer = styled(Box)(({ theme }) => ({ - flex: 1, - display: 'flex', - justifyContent: 'flex-end', - maxWidth: '100%', - [theme.breakpoints.down('sm')]: { - justifyContent: 'center', - width: '100%', - }, -})); - const DeckPageHeader = ({ openAddDeckDialog }) => { const { theme } = useMode(); const { setCurrentForm } = useFormContext(); - const { user } = useUserContext(); + const { user } = useUserData(); if (!user) { return ; } @@ -61,7 +41,6 @@ const DeckPageHeader = ({ openAddDeckDialog }) => { }} > - {/* */} { day: 'numeric', })} /> - {/* */} - { setCurrentForm('addDeckForm'); openAddDeckDialog(); @@ -84,7 +61,20 @@ const DeckPageHeader = ({ openAddDeckDialog }) => { }} > Add New Deck - + */} + { + setCurrentForm('addDeckForm'); + openAddDeckDialog(); + console.log('openAddDeckDialog'); + }} + > + Add New Deck + ); diff --git a/src/layout/deck/index.jsx b/src/layout/deck/index.jsx index 551d799..3a5be6d 100644 --- a/src/layout/deck/index.jsx +++ b/src/layout/deck/index.jsx @@ -10,8 +10,6 @@ import useDialogState from '../../context/hooks/useDialogState'; import useSelectedDeck from '../../context/MAIN_CONTEXT/DeckContext/useSelectedDeck'; import DeckListItem from './DeckListItem'; import DashboardBox from '../REUSABLE_COMPONENTS/DashboardBox'; -import SimpleCard from '../REUSABLE_COMPONENTS/unique/SimpleCard'; -import uniqueTheme from '../REUSABLE_COMPONENTS/unique/uniqueTheme'; const DeckBuilder = () => { const { theme } = useMode(); @@ -68,9 +66,14 @@ const DeckBuilder = () => { item xs={6} // lg={7} - sx={{ display: 'flex', flexDirection: 'column' }} + sx={{ + display: 'flex', + flexDirection: 'column', + px: theme.spacing(2), + py: theme.spacing(2), + }} > - \{/* */} + {/* */} {allDecks?.map((deck, index) => ( { const { theme } = useMode(); const navigate = useNavigate(); - const { totalQuantity } = useCartStore(); + const { cartCardQuantity } = useCartManager(); const iOS = typeof navigator !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent); const [isOpen, setIsOpen] = useState(false); // Manage open state locally - const isMobileView = useMediaQuery(theme.breakpoints.down('sm')); - const iconColor = isMobileView ? theme.palette.primary.main : 'white'; - const [cookies] = useCookies('authUser'); - const username = cookies?.authUser?.username; - const menuItems = baseMenuItems({ cartCardQuantity: totalQuantity }); + const isMedView = useMediaQuery(theme.breakpoints.down('md')); + const { addCookies, getCookie, deleteCookies } = useManageCookies(); + const { authUser } = getCookie(['authUser']); + const username = authUser?.username; + const menuItems = baseMenuItems({ cartCardQuantity: cartCardQuantity }); const toggleSidebar = useCallback(() => setIsOpen(!isOpen), [isOpen]); const [springs] = useSprings(menuItems.length, (index) => ({ from: { opacity: 0, transform: 'translateY(-20px)' }, @@ -99,7 +100,7 @@ const Navigation = ({ isLoggedIn }) => { maxHeight: 64, maxWidth: '100%', '&:hover': { - backgroundColor: rgba(theme.palette.backgroundE.light), + backgroundColor: rgba(theme.palette.greenAccent.lighterSeaGreen), }, }} > @@ -158,7 +159,7 @@ const Navigation = ({ isLoggedIn }) => { /> - {!isMobileView && renderMenuItems('top')} + {!isMedView && renderMenuItems('top')} { flexDirection: 'row', }} > - + {username} @@ -191,14 +199,9 @@ const Navigation = ({ isLoggedIn }) => { }, }} sx={{ - // borderRadius: 'md', - // p: 2, display: 'flex', flexDirection: 'column', justifyContent: 'flex-start', - // gap: 2, - // height: '100%', - // overflow: 'auto', }} > { - const { isLoggedIn, user } = useAuthContext(); + const { user } = useUserData(); const navigate = useNavigate(); useEffect(() => { if (user === null) { diff --git a/src/layout/profile/NotificationSection.js/NotificationList.jsx b/src/layout/profile/NotificationSection.js/NotificationList.jsx index 1abf89d..2ae7e9a 100644 --- a/src/layout/profile/NotificationSection.js/NotificationList.jsx +++ b/src/layout/profile/NotificationSection.js/NotificationList.jsx @@ -16,23 +16,13 @@ import { Stack, Typography, } from '@mui/material'; -import { useAuthContext, useMode } from '../../../context'; - -// assets -// import { -// IconBrandTelegram, -// IconBuildingStore, -// IconMailbox, -// IconPhoto, -// } from '../../../../../node_modules/@tabler/icons-react'; -// import User1 from 'assets/images/users/user-round.svg'; - -// styles +import { useMode } from '../../../context'; +import useUserData from '../../../context/MAIN_CONTEXT/UserContext/useUserData'; const ListItemWrapper = styled('div')(({ theme }) => ({ cursor: 'pointer', padding: 16, '&:hover': { - background: theme.palette.primary?.light, + background: theme.palette.grey.blueGrey, }, '& .MuiListItem-root': { padding: 0, @@ -43,7 +33,7 @@ const ListItemWrapper = styled('div')(({ theme }) => ({ const NotificationList = () => { const { theme } = useMode(); - const { user } = useAuthContext(); + const { user } = useUserData(); const chipSX = { height: 24, padding: '0 6px', @@ -54,13 +44,11 @@ const NotificationList = () => { backgroundColor: theme.palette.rariry?.ultra, marginRight: '5px', }; - const chipWarningSX = { ...chipSX, color: theme.palette.warning?.dark, backgroundColor: theme.palette.warning?.light, }; - const chipSuccessSX = { ...chipSX, color: theme.palette.success?.dark, @@ -135,7 +123,7 @@ const NotificationList = () => { borderColor: theme.palette.success.main, }} > - + {/* */} { - + {/* */} { disableElevation endIcon={ <> - + {/* */} } > @@ -230,7 +218,7 @@ const NotificationList = () => { - + {/* */} John Doe} @@ -259,14 +247,14 @@ const NotificationList = () => { - + {/* */} demo.jpg @@ -282,7 +270,7 @@ const NotificationList = () => { - + {/* */} John Doe} diff --git a/src/layout/profile/NotificationSection.js/index.js b/src/layout/profile/NotificationSection.js/index.js index b54bc8f..7edeb6c 100644 --- a/src/layout/profile/NotificationSection.js/index.js +++ b/src/layout/profile/NotificationSection.js/index.js @@ -106,11 +106,11 @@ const NotificationSection = () => { ...theme.typography.commonAvatar, ...theme.typography.mediumAvatar, transition: 'all .2s ease-in-out', - background: theme.palette.secondary.light, - color: theme.palette.secondary.dark, + background: theme.palette.grey.evenLighter, + color: theme.palette.grey.dark, '&[aria-controls="menu-list-grow"],&:hover': { - background: theme.palette.secondary.dark, - color: theme.palette.secondary.light, + background: theme.palette.grey.dark, + color: theme.palette.grey.evenLighter, }, }} ref={anchorRef} diff --git a/src/layout/profile/ProfileSection.js b/src/layout/profile/ProfileSection.js index 1727666..a01b0e0 100644 --- a/src/layout/profile/ProfileSection.js +++ b/src/layout/profile/ProfileSection.js @@ -22,15 +22,16 @@ import { } from '@mui/material'; import PerfectScrollbar from 'react-perfect-scrollbar'; import { useNavigate } from 'react-router-dom'; -import { useAuthContext, useMode } from '../../context'; +import { useMode } from '../../context'; import Transitions from '../Transitions'; import MainCard from '../MainCard'; +import useUserData from '../../context/MAIN_CONTEXT/UserContext/useUserData'; // ==============================|| PROFILE MENU ||============================== // const ProfileSection = () => { const { theme } = useMode(); - const { user } = useAuthContext(); + const { user } = useUserData(); const navigate = useNavigate(); const [sdm, setSdm] = useState(true); const [value, setValue] = useState(''); @@ -81,14 +82,14 @@ const ProfileSection = () => { alignItems: 'center', borderRadius: '27px', transition: 'all .2s ease-in-out', - borderColor: theme.palette.backgroundA.light, - backgroundColor: theme.palette.backgroundA.light, + borderColor: theme.palette.greenAccent.lightest, + backgroundColor: theme.palette.greenAccent.lightest, '&[aria-controls="menu-list-grow"], &:hover': { - borderColor: theme.palette.backgroundA.dark, - background: `${theme.palette.backgroundA.dark}!important`, - color: theme.palette.backgroundA.light, + borderColor: theme.palette.greenAccent.light, + background: `${theme.palette.greenAccent.light}!important`, + color: theme.palette.greenAccent.lightest, '& svg': { - stroke: theme.palette.backgroundA.light, + stroke: theme.palette.greenAccent.lightest, }, }, '& .MuiChip-label': { @@ -113,7 +114,7 @@ const ProfileSection = () => { // // } variant="outlined" @@ -200,7 +201,7 @@ const ProfileSection = () => { @@ -263,7 +264,7 @@ const ProfileSection = () => { width: '100%', maxWidth: 350, minWidth: 300, - backgroundColor: theme.palette.backgroundA.dark, + backgroundColor: theme.palette.greenAccent.light, borderRadius: '10px', [theme.breakpoints.down('md')]: { minWidth: '100%', @@ -323,7 +324,7 @@ const ProfileSection = () => { size="small" sx={{ bgcolor: theme.palette.warning.dark, - color: theme.palette.backgroundA.light, + color: theme.palette.greenAccent.lightest, }} /> diff --git a/src/layout/profile/UserStats.jsx b/src/layout/profile/UserStats.jsx index da3eb2a..6b34583 100644 --- a/src/layout/profile/UserStats.jsx +++ b/src/layout/profile/UserStats.jsx @@ -1,13 +1,13 @@ import React from 'react'; import { Box, Typography } from '@mui/material'; -import { useDeckStore } from '../../context/MAIN_CONTEXT/DeckContext/DeckContext'; -import { useCartStore } from '../../context/MAIN_CONTEXT/CartContext/CartContext'; -import { useCollectionStore } from '../../context/MAIN_CONTEXT/CollectionContext/CollectionContext'; +import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +import useSelectedDeck from '../../context/MAIN_CONTEXT/DeckContext/useSelectedDeck'; +import { useCartManager } from '../../context/MAIN_CONTEXT/CartContext/useCartManager'; const UserStats = () => { - const { allDecks } = useDeckStore(); - const { allCollections } = useCollectionStore(); - const { cartData } = useCartStore(); + const { allDecks } = useSelectedDeck(); + const { allCollections } = useSelectedCollection(); + const { cart } = useCartManager(); // console.log('allDecks', allDecks); return ( @@ -19,7 +19,7 @@ const UserStats = () => { Number of Collections: {allCollections?.length} - Number of Cards in Cart: {cartData?.cart?.length} + Number of Cards in Cart: {cart?.items?.length} {/* Add other statistics as needed */} diff --git a/src/layout/sections/AnimatedFeatureCard.jsx b/src/layout/sections/AnimatedFeatureCard.jsx index 432a3c1..198801f 100644 --- a/src/layout/sections/AnimatedFeatureCard.jsx +++ b/src/layout/sections/AnimatedFeatureCard.jsx @@ -10,6 +10,7 @@ import { import { useMode } from '../../context'; import SimpleButton from '../REUSABLE_COMPONENTS/unique/SimpleButton'; import uniqueTheme from '../REUSABLE_COMPONENTS/unique/uniqueTheme'; +import RCButton from '../REUSABLE_COMPONENTS/RCBUTTON'; const AnimatedBox = animated(Box); @@ -55,7 +56,7 @@ export const AnimatedFeatureCard = ({ tier, onOpenModal }) => { titleTypographyProps={{ align: 'center' }} subheaderTypographyProps={{ align: 'center' }} sx={{ - backgroundColor: theme.palette.backgroundA.dark, + backgroundColor: theme.palette.greenAccent.light, height: '20%', }} /> @@ -79,9 +80,18 @@ export const AnimatedFeatureCard = ({ tier, onOpenModal }) => { justifyContent: 'flex-end', // Align button to the end }} > - onOpenModal(tier.title)} + > + Manage Collections + + {/* { }} > Manage {tier.title} - + */} diff --git a/src/layout/sections/HeroSection.jsx b/src/layout/sections/HeroSection.jsx index 6ec9d71..832e396 100644 --- a/src/layout/sections/HeroSection.jsx +++ b/src/layout/sections/HeroSection.jsx @@ -2,18 +2,6 @@ import React, { useEffect, useMemo, useState } from 'react'; import { ErrorBoundary, useMode, useStatisticsStore } from '../../context'; import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; import placeHolder from '../../assets/images/placeholder.jpeg'; -import { Swiper, SwiperSlide } from 'swiper/react'; -import 'swiper/css'; -import 'swiper/css/effect-coverflow'; -import 'swiper/css/pagination'; -import 'swiper/css/navigation'; -import { - EffectCoverflow, - Pagination, - Navigation, - Autoplay, -} from 'swiper/modules'; -import MDTypography from '../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; import { HeroSectionSkeleton } from '../../layout/REUSABLE_COMPONENTS/SkeletonVariants'; import { Box, @@ -23,17 +11,11 @@ import { Zoom, useMediaQuery, } from '@mui/material'; -import pages from '../../data/pages.json'; import HeroTextSection from './HeroTextSection'; import HeroIconSection from './HeroIconSection'; import HeroSwiper from './HeroSwiper'; import FlexBetween from '../REUSABLE_COMPONENTS/FlexBetween'; -import RCHeader from '../REUSABLE_COMPONENTS/RCHeader'; -import styled from 'styled-components'; -import { useCardStoreHook } from '../../context/hooks/useCardStore'; import useLocalStorage from '../../context/hooks/useLocalStorage'; -import { useLoading } from '../../context/hooks/useLoading'; -import useSkeletonLoader from '../REUSABLE_COMPONENTS/useSkeletonLoader'; import { ResponsiveContainer, CartesianGrid, @@ -50,18 +32,15 @@ import { } from 'recharts'; import DashboardBox from '../REUSABLE_COMPONENTS/DashboardBox'; import BoxHeader from '../REUSABLE_COMPONENTS/BoxHeader'; +import { useCardStoreHook } from '../../context/MAIN_CONTEXT/CardContext/useCardStore'; const HeroSection = () => { const { theme } = useMode(); const { breakpoints } = theme; - const { introText } = pages; const { fetchRandomCardsAndSet } = useCardStoreHook(); const [randomCards, setRandomCards] = useLocalStorage('randomCards', []); const isMobileView = useMediaQuery(breakpoints.down('sm')); const isMidView = useMediaQuery(breakpoints.down('md')); - const isFullView = useMediaQuery(breakpoints.up('lg')); - const { isLoading } = useLoading(); - const { SkeletonLoader } = useSkeletonLoader(); const [activeCardIndex, setActiveCardIndex] = useState(0); const [shouldShow, setShouldShow] = useState(false); const defaultCards = new Array(45).fill({}).map((_, index) => ({ diff --git a/src/layout/sections/HeroTextSection.jsx b/src/layout/sections/HeroTextSection.jsx index cccd26e..b968ea6 100644 --- a/src/layout/sections/HeroTextSection.jsx +++ b/src/layout/sections/HeroTextSection.jsx @@ -38,7 +38,7 @@ const HeroTextSection = ({ shouldShow }) => { variant={isMobileView ? 'h3' : 'h2'} sx={{ fontWeight: 'bold', - color: theme.palette.primary.main, + color: theme.palette.success.secondary, my: isMobileView ? '1rem' : 'auto', }} > diff --git a/src/layout/sections/MainContentSection.jsx b/src/layout/sections/MainContentSection.jsx index ecdb4fa..184b803 100644 --- a/src/layout/sections/MainContentSection.jsx +++ b/src/layout/sections/MainContentSection.jsx @@ -10,12 +10,7 @@ import { Container, useMediaQuery, } from '@mui/material'; -import { - useAuthContext, - useCollectionStore, - useMode, - useUserContext, -} from '../../context'; +import { useMode } from '../../context'; import MDBox from '../../layout/REUSABLE_COMPONENTS/MDBOX'; import MDTypography from '../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; import MDAvatar from '../../layout/REUSABLE_COMPONENTS/MDAVATAR'; @@ -31,13 +26,19 @@ import placeHolder from '../../assets/images/placeholder.jpeg'; import { DEFAULT_COLLECTION } from '../../context/constants'; import useCollectionManager from '../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -import SimpleButton from '../../layout/REUSABLE_COMPONENTS/unique/SimpleButton'; -import uniqueTheme from '../../layout/REUSABLE_COMPONENTS/unique/uniqueTheme'; +import useManageCookies from '../../context/hooks/useManageCookies'; +import RCButton from '../REUSABLE_COMPONENTS/RCBUTTON'; +import useUserData from '../../context/MAIN_CONTEXT/UserContext/useUserData'; const MainContentSection = () => { const { theme } = useMode(); - const { isLoggedIn } = useAuthContext(); - const { user } = useUserContext(); + const { addCookie, getCookie, deleteCookie } = useManageCookies(); + const { isLoggedIn, authUser, userId } = getCookie([ + 'isLoggedIn', + 'authUser', + 'userId', + ]); + const { user } = useUserData(); const { hasFetchedCollections } = useCollectionManager(); const { allCollections, selectedCollection } = useSelectedCollection(); const isMdUp = useMediaQuery(theme.breakpoints.up('md')); @@ -115,12 +116,16 @@ const MainContentSection = () => { - { }} > Manage Collections - - {/* */} + { + console.log('clicked'); }} > Manage Collections - */} + View Purchase History diff --git a/src/pages/CartPage.js b/src/pages/CartPage.js index b9e0458..e8ebdfe 100644 --- a/src/pages/CartPage.js +++ b/src/pages/CartPage.js @@ -1,105 +1,54 @@ import React, { useEffect } from 'react'; -import { useCookies } from 'react-cookie'; import { Box, Card, CardContent, Grid, useTheme } from '@mui/material'; import CartContent from '../layout/cart/CartContent'; -import { useCartStore, useMode } from '../context'; +import { useMode } from '../context'; import Checkout from '../layout/cart/cartPageContainers/Checkout'; import PageLayout from '../layout/REUSABLE_COMPONENTS/PageLayout'; import { useLoading } from '../context/hooks/useLoading'; import CartSummary from '../layout/cart/CartSummary'; +import { useCartManager } from '../context/MAIN_CONTEXT/CartContext/useCartManager'; +import LoadingOverlay from '../layout/REUSABLE_COMPONENTS/LoadingOverlay'; const CartPage = () => { const { theme } = useMode(); const { - cartData, - addOneToCart, - removeOneFromCart, - fetchCartForUser, - getTotalCost, + cart, + addCardsToCart, + removeCardsFromCart, + fetchUserCart, cartCardQuantity, totalCost, - } = useCartStore(); + } = useCartManager(); const { startLoading, stopLoading, setError, isPageLoading } = useLoading(); - const calculateTotalPrice = getTotalCost(); - - useEffect(() => { - const fetchData = async () => { - startLoading('isPageLoading'); - try { - await fetchCartForUser(); // Assuming fetchUserCart updates cartData - } catch (error) { - console.error('Error fetching cart data:', error); - setError(error.message || 'Failed to fetch cart data'); - } finally { - stopLoading('isPageLoading'); - } - }; - if (!cartData) { - fetchData(); - } - }, [cartData, fetchCartForUser]); + // useEffect(() => { + // const fetchData = async () => { + // try { + // await fetchUserCart(); // Assuming fetchUserCart updates cartData + // } catch (error) { + // console.error('Error fetching cart data:', error); + // setError(error.message || 'Failed to fetch cart data'); + // } + // }; + // if (!cart) { + // fetchData(); + // } + // }, [fetchUserCart]); // Modify this function based on how your cart store manages items - const handleModifyItemInCart = async (cardId, operation) => { - try { - operation === 'add' ? addOneToCart(cardId) : removeOneFromCart(cardId); - } catch (e) { - console.error('Failed to adjust quantity in cart:', e); - } - }; + // const handleModifyItemInCart = async (cardId, operation) => { + // // convert card to be that card but in an array + // const card = [cardId]; + // try { + // operation === 'add' + // ? addCardsToCart(card, cart) + // : removeCardsFromCart(card); + // } catch (e) { + // console.error('Failed to adjust quantity in cart:', e); + // } + // }; // Function to render the checkout and summary section - const renderCheckoutAndSummary = () => ( - - - - - - - ); - // Function to render the overall cart layout - const renderCartLayout = () => ( - - - - - - {' '} - {renderCheckoutAndSummary()} - - - - ); return ( - {/* */} - {/* */} - {isPageLoading &&
Loading...
} - {/* {loadingStatus?.isLoading && returnDisplay()} */} + {isPageLoading && } { m: 'auto', width: '100%', height: '100%', - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, }} > - {renderCartLayout()} + + + + + + {' '} + + + + + + {' '} + + + {' '} - {/*
*/} - {/*
*/}
); }; diff --git a/src/pages/CollectionPage.js b/src/pages/CollectionPage.js index 45b7237..8c9ed81 100644 --- a/src/pages/CollectionPage.js +++ b/src/pages/CollectionPage.js @@ -2,12 +2,10 @@ import React, { useEffect } from 'react'; import { Box, Grid } from '@mui/material'; import CollectionPortfolio from '../layout/collection'; import GenericCardDialog from '../components/dialogs/GenericCardDialog'; -import { useCollectionStore, useMode } from '../context'; import useLoadingAndModal from './pageStyles/useLoadingAndModal'; import HeroBanner from './pageStyles/HeroBanner'; import PageLayout from '../layout/REUSABLE_COMPONENTS/PageLayout'; import useSelectedCollection from '../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -import { useIsFirstRender } from '../context/hooks/useIsFirstRender'; import useCollectionManager from '../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; import { useLoading } from '../context/hooks/useLoading'; @@ -19,7 +17,6 @@ const CollectionPage = () => { const { returnDisplay, isModalOpen, modalContent, closeModal } = useLoadingAndModal(); const { isPageLoading } = useLoading(); - const isFirstRender = useIsFirstRender(); useEffect(() => { if (!hasFetchedCollections) { @@ -38,7 +35,6 @@ const CollectionPage = () => { } } > - {isPageLoading && returnDisplay()} {!selectedCollection && ( { - if (!isLoggedIn) { - openDialog(dialogName); - } - }, [isLoggedIn, openDialog]); - // EFFECT: Set the current form to 'loginForm' when the component mounts - useEffect(() => { - setFormSchema('loginForm'); - }, [setFormSchema]); - // HANDLE: Logout the user and close the login dialog - const handleLogout = () => { - logout(); - closeDialog(dialogName); - }; + // const { formMethods, onSubmit, setFormSchema, currentSchemaKey } = + // useFormContext(); // HANDLE: Open the register dialog and close the login dialog - const handleToggle = () => { - setFormSchema( - currentSchemaKey === 'loginForm' ? 'signupForm' : 'loginForm' - ); - setChecked(!checked); - }; + // const handleToggle = () => { + // setFormSchema( + // currentSchemaKey === 'loginForm' ? 'signupForm' : 'loginForm' + // ); + // setChecked(!checked); + // }; const formTitle = currentSchemaKey === 'loginForm' ? 'Login' : 'Sign Up'; - const signupMode = currentSchemaKey === 'signupForm'; - const formLabel = () => { - - {currentSchemaKey === 'loginForm' ? 'Sign Up' : 'Login'} - ; - }; return ( closeDialog(dialogName)} tbeme={theme} aria-labelledby="responsive-dialog-title" @@ -109,7 +94,7 @@ function LoginDialog() { @@ -118,12 +103,12 @@ function LoginDialog() { {formTitle}
- {/* */} toggleActiveForm('loginForm', 'signupForm')} + // onChange={handleToggle} labelLeft="Login" labelRight="Sign Up" iconLeft={} @@ -131,29 +116,17 @@ function LoginDialog() { /> - - - {/* {currentSchemaKey === 'loginForm' ? ( - - ) : ( - - )} */} + + {/* */} } label="Remember me" /> - {isLoggedIn && ( - - )} {'Copyright © '} diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx index ee595b7..3f10fc3 100644 --- a/src/pages/LoginPage.jsx +++ b/src/pages/LoginPage.jsx @@ -9,9 +9,9 @@ import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; import Typography from '@mui/material/Typography'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import LoginForm from '../assets/currentlyUnused/LoginForm'; -import { useAuthContext, useMode } from '../context'; +import { useMode } from '../context'; import { Navigate, useNavigate } from 'react-router-dom'; -import { useCookies } from 'react-cookie'; +import useManageCookies from '../context/hooks/useManageCookies'; function Copyright(props) { return ( @@ -37,24 +37,24 @@ export default function LoginPage() { const navigate = useNavigate(); const { theme } = useMode(); - const { isLoggedIn } = useAuthContext(); - const [cookies, setCookies, removeCookies] = useCookies(['authUser']); + const { addCookies, getCookie, deleteCookies } = useManageCookies(); + const { authUser, isLoggedIn } = getCookie(['authUser', 'isLoggedIn']); const previousIsLoggedIn = React.useRef(isLoggedIn); - React.useEffect(() => { - // Check if isLoggedIn status has changed - if (isLoggedIn && !previousIsLoggedIn.current) { - // User just logged in - navigate('/'); - } else if (!isLoggedIn && previousIsLoggedIn.current) { - // User just logged out - navigate('/login'); - } - // Update the ref to the current isLoggedIn status - previousIsLoggedIn.current = isLoggedIn; - }, [isLoggedIn, navigate]); + // React.useEffect(() => { + // // Check if isLoggedIn status has changed + // if (isLoggedIn && !previousIsLoggedIn.current) { + // // User just logged in + // navigate('/'); + // } else if (!isLoggedIn && previousIsLoggedIn.current) { + // // User just logged out + // navigate('/login'); + // } + // // Update the ref to the current isLoggedIn status + // previousIsLoggedIn.current = isLoggedIn; + // }, [isLoggedIn, navigate]); - if (isLoggedIn) return null; // Don't render anything if already logged in + // if (isLoggedIn) return null; // Don't render anything if already logged in return ( @@ -87,7 +87,7 @@ export default function LoginPage() { alignItems: 'center', }} > - + diff --git a/src/pages/pageStyles/CarouselImage.jsx b/src/pages/pageStyles/CarouselImage.jsx index db0984d..4cf9356 100644 --- a/src/pages/pageStyles/CarouselImage.jsx +++ b/src/pages/pageStyles/CarouselImage.jsx @@ -12,9 +12,7 @@ export const CarouselImage = ({ image, caption }) => { sx={{ position: 'absolute', bottom: 0, - // backgroundColor: (theme) => theme.palette.backgroundA.lighter, - color: (theme) => - theme.palette.backgroundA.contrastTextD || 'common.white', + color: (theme) => theme.palette.grey.black || 'common.white', width: '100%', padding: 2, textAlign: 'center', diff --git a/src/pages/pageStyles/HeroCenter.jsx b/src/pages/pageStyles/HeroCenter.jsx index 77de3ef..02c854f 100644 --- a/src/pages/pageStyles/HeroCenter.jsx +++ b/src/pages/pageStyles/HeroCenter.jsx @@ -16,7 +16,7 @@ const HeroCenter = ({ decorative, title, subtitle }) => { width: '100%', height: '100%', textAlign: 'center', - background: theme.palette.backgroundE.light, + background: theme.palette.greenAccent.lighterSeaGreen, padding: theme2.spacing(2), // Adjust padding as needed }} > @@ -24,7 +24,7 @@ const HeroCenter = ({ decorative, title, subtitle }) => { ({ // alignItems: 'center', // width: '100%', // Do not explicitly set the height here to allow Toolbar's default styling to take effect - // backgroundColor: theme.palette.backgroundA.darker, + // backgroundColor: theme.palette.greenAccent.default, // borderRadius: '30px', flexGrow: 1, @@ -93,7 +93,7 @@ export const StyledToolbar = styled(Toolbar)(({ theme }) => ({ // alignItems: 'center', // width: '100%', // // Do not explicitly set the height here to allow Toolbar's default styling to take effect - // backgroundColor: theme.palette.backgroundA.darker, + // backgroundColor: theme.palette.greenAccent.default, mr: 2, display: { xs: 'none', md: 'flex' }, fontFamily: 'monospace', @@ -110,12 +110,7 @@ export const StyledMenuItem = styled(MenuItem)(({ theme }) => ({ px: '2', py: '1', borderRadius: '30px', - // height: '100%', - // width: '100%', height: '100%', - // '&:hover': { - // backgroundColor: theme.palette.backgroundA.onHover, - // }, '&:hover': { backgroundColor: 'linear-gradient( 90deg, rgba(78, 78, 246, 0.647) 0%, rgba(247, 90, 216, 0.696) 100% );', @@ -166,14 +161,9 @@ export const StyledSwipeableDrawer = styled(SwipeableDrawer)(({ theme }) => ({ display: 'flex', flexDirection: 'column', height: '100%', - backgroundColor: theme.palette.backgroundA.default, - // top: 64, - // width: 240, - // position: 'absolute', + backgroundColor: theme.palette.greenAccent.lighter, flexGrow: 1, justifyContent: 'space-between', - // backgroundColor: theme.palette.backgroundA.lightest, - // !----- }, })); export const DrawerHeader = styled('div')(({ theme }) => ({ @@ -184,14 +174,6 @@ export const DrawerHeader = styled('div')(({ theme }) => ({ ...theme.mixins.toolbar, justifyContent: 'flex-end', })); -// export const StyledListItem = styled(ListItem)(({ theme }) => ({ -// cursor: 'pointer', -// height: '100%', -// flexGrow: 1, -// '&:hover': { -// backgroundColor: theme.palette.backgroundA.onHover, -// }, -// })); export const StyledListItemButton = styled(ListItem)(({ theme }) => ({ cursor: 'pointer', '&:hover': { @@ -232,7 +214,7 @@ export const MainContainer2 = styled(Box)(({ theme }) => ({ export const CardDetails = styled(Box)(({ theme }) => ({ padding: theme.spacing(2), width: '100%', - border: `1px solid ${theme.palette.backgroundB.lighter}`, + border: `1px solid ${theme.palette.grey.lighterSimpleGrey}`, borderRadius: theme.shape.borderRadius, })); export const ChartContainer = styled(Box)(({ theme, height, width }) => ({ @@ -247,7 +229,7 @@ export const ChartContainer = styled(Box)(({ theme, height, width }) => ({ minWidth: '390px', position: 'relative', padding: theme.spacing(2), - background: theme.palette.backgroundB.darker, + background: theme.palette.grey.blueGrey, })); export const LinearChartContainer = styled(Box)(({ theme }) => ({ chartContainer: { @@ -281,7 +263,7 @@ export const DeckBuilderBanner = styled(Box)(({ theme }) => ({ flexDirection: 'column', alignItems: 'center', flexGrow: 1, - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, width: '100%', maxWidth: '1600px', // margin: 'auto', @@ -317,7 +299,7 @@ export const RootGrid = styled(Grid)(({ theme }) => ({ alignItems: 'center', flexWrap: 'wrap', // Ensure wrapping on smaller screens overflow: 'auto', - backgroundColor: theme.palette.backgroundB.default, + backgroundColor: theme.palette.grey.simpleGrey, borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[5], })); @@ -330,7 +312,7 @@ export const GridContainer = styled(Grid)(({ theme }) => ({ // ! DECK DISPLAY PAGE export const DeckDisplayBox = styled(Box)(({ theme }) => ({ padding: theme.spacing(3), - backgroundColor: theme.palette.backgroundB.lightest, + backgroundColor: theme.palette.grey.lighterSimpleGrey, border: `1px solid ${theme.palette.divider}`, borderRadius: theme.shape.borderRadius, margin: 'auto', @@ -341,7 +323,7 @@ export const DeckDisplayPaper = styled(Paper)(({ theme }) => ({ padding: theme.spacing(2), borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[4], - backgroundColor: theme.palette.backgroundA.light, + backgroundColor: theme.palette.greenAccent.lightest, color: theme.palette.text.primary, display: 'flex', flexDirection: 'column', @@ -355,10 +337,10 @@ export const DeckDisplayTitleTypography = styled(Typography)(({ theme }) => ({ export const DeckStyledButton = styled(Button)(({ theme }) => ({ margin: theme.spacing(1), - backgroundColor: theme.palette.backgroundA.dark, - color: theme.palette.backgroundA.contrastTextA, + backgroundColor: theme.palette.greenAccent.light, + color: theme.palette.grey.white, '&:hover': { - backgroundColor: theme.palette.backgroundA.darker, + backgroundColor: theme.palette.greenAccent.default, }, display: 'flex', alignItems: 'center', @@ -388,7 +370,7 @@ export const NoCardsTypography = styled(Typography)(({ theme }) => ({ // ! HOME PAGE STYLED COMPONENTS export const HomePageBox = styled(Box)(({ theme }) => ({ - background: theme.palette.backgroundA.lighter, + background: theme.palette.greenAccent.evenLighter, padding: theme.spacing(2, 4, 8), margin: theme.spacing(1, 2, 4), borderRadius: theme.shape.borderRadius, @@ -406,7 +388,7 @@ export const HomePageBox = styled(Box)(({ theme }) => ({ // }, // })); export const FeatureCard = styled(Card)(({ theme }) => ({ - background: theme.palette.backgroundA.light, + background: theme.palette.greenAccent.lightest, boxShadow: theme.shadows[5], transition: 'box-shadow 0.3s ease-in-out', // smooth transition for shadow '&:hover': { @@ -415,16 +397,16 @@ export const FeatureCard = styled(Card)(({ theme }) => ({ }, })); export const ActionButton = styled(Button)(({ theme }) => ({ - color: theme.palette.backgroundA.dark, - background: theme.palette.backgroundA.dark, + color: theme.palette.greenAccent.light, + background: theme.palette.greenAccent.light, '&:hover': { - background: theme.palette.backgroundA.light, // Darken button on hover for feedback + background: theme.palette.greenAccent.lightest, // Darken button on hover for feedback }, })); export const MainContentContainer = styled(Paper)(({ theme }) => ({ padding: theme.spacing(2, 4, 6), borderRadius: theme.shape.borderRadius, - backgroundColor: theme.palette.backgroundD.dark, + backgroundColor: theme.palette.greenAccent.crystalGreen, boxShadow: theme.shadows[10], marginTop: theme.spacing(2), margin: theme.spacing(4, 0), // added vertical spacing @@ -436,7 +418,7 @@ export const SecondaryContentContainer = styled(Paper)(({ theme }) => ({ alignItems: 'center', width: '100%', padding: theme.spacing(2), - background: theme.palette.backgroundD.dark, + background: theme.palette.greenAccent.crystalGreen, borderRadius: theme.shape.borderRadius, transition: 'background-color 0.3s', })); @@ -444,7 +426,7 @@ export const TertiaryContentContainer = styled(Box)(({ theme }) => ({ marginTop: theme.spacing(2), padding: theme.spacing(3), borderRadius: theme.shape.borderRadius, - background: theme.palette.backgroundD.dark, + background: theme.palette.greenAccent.crystalGreen, boxShadow: theme.shadows[10], marginBottom: theme.spacing(4), transition: 'all 0.3s ease-in-out', // smooth all transitions @@ -476,7 +458,7 @@ export const ChartArea = styled(Container)(({ theme }) => ({ justifyContent: 'center', border: `1px solid ${theme.palette.divider}`, borderRadius: theme.shape.borderRadius, - background: theme.palette.backgroundB.contrastText, + background: theme.palette.grey.contrastText, })); export const SquareChartContainer = styled(Box)(({ theme }) => ({ position: 'relative', @@ -494,7 +476,7 @@ export const SquareChartContainer = styled(Box)(({ theme }) => ({ export const ChartPaper = styled(Paper)(({ theme }) => ({ borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[5], - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, color: theme.palette.text.secondary, padding: theme.spacing(2), display: 'flex', @@ -510,7 +492,7 @@ export const ChartPaper = styled(Paper)(({ theme }) => ({ export const ResponsiveSquare = styled(Box)(({ theme }) => ({ width: '100%', paddingTop: '100%', - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[5], display: 'flex', @@ -535,7 +517,7 @@ export const StyledSkeletonCard = styled(Card)(({ theme }) => ({ flexGrow: 1, // width: 'auto', maxHeight: '14vh', - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[5], transition: 'transform 0.3s ease-in-out', @@ -592,7 +574,7 @@ export const StyledStatisticTypography = styled(Typography)(({ theme }) => ({ // ! FORMS / INPUTS export const StyledFormControl = styled(FormControl)(({ theme }) => ({ margin: theme.spacing(1, 0), - backgroundColor: theme.palette.backgroundA.lightest, // Adjusted for a slight contrast + backgroundColor: theme.palette.greenAccent.contrastText, // Adjusted for a slight contrast borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[1], // Subtle shadow for depth @@ -602,14 +584,14 @@ export const StyledFormControl = styled(FormControl)(({ theme }) => ({ backgroundColor: theme.palette.action.hover, }, '&.Mui-focused': { - backgroundColor: theme.palette.backgroundA.lightest, - borderColor: theme.palette.backgroundA.dark, + backgroundColor: theme.palette.greenAccent.contrastText, + borderColor: theme.palette.greenAccent.light, }, }, })); export const StyledInputLabel = styled(InputLabel)(({ theme }) => ({ fontWeight: 'bold', // Making label text bold - color: theme.palette.backgroundB.dark, + color: theme.palette.grey.simpleGrey, })); // FORM STYLES // export const FormWrapper = styled('form')(({ theme }) => ({ @@ -620,7 +602,7 @@ export const StyledInputLabel = styled(InputLabel)(({ theme }) => ({ // padding: theme.spacing(3), // margin: 'auto', // alignItems: 'center', -// backgroundColor: theme.palette.backgroundA.light, +// backgroundColor: theme.palette.greenAccent.lightest, // borderRadius: theme.shape.borderRadius, // })); export const StyledFormPaper = styled(Paper)(({ theme }) => ({ @@ -635,16 +617,6 @@ export const StyledFormPaper = styled(Paper)(({ theme }) => ({ boxShadow: theme.shadows[9], }, })); -// export const StyledButton = styled(Button)(({ theme }) => ({ -// background: theme.palette.backgroundA.default, -// color: theme.palette.backgroundA.contrastTextB, -// padding: theme.spacing(1.5), -// marginTop: theme.spacing(2), -// '&:hover': { -// backgroundColor: theme.palette.backgroundA.darkest, -// color: theme.palette.backgroundA.contrastTextA, -// }, -// })); export const StyledTextField = styled(TextField)(({ theme }) => ({ '& .MuiOutlinedInput-root': { position: 'relative', @@ -653,18 +625,18 @@ export const StyledTextField = styled(TextField)(({ theme }) => ({ borderColor: theme.palette.transparent.main, }, '&:hover .MuiOutlinedInput-notchedOutline': { - color: theme.palette.backgroundA.darker, - borderColor: theme.palette.backgroundA.darker, + color: theme.palette.greenAccent.default, + borderColor: theme.palette.greenAccent.default, }, '&.Mui-focused .MuiOutlinedInput-notchedOutline': { - borderColor: theme.palette.backgroundA.darker, + borderColor: theme.palette.greenAccent.default, borderWidth: '2px', // or other width as you like }, }, borderRadius: theme.shape.borderRadius, - color: theme.palette.backgroundA.darkest, + color: theme.palette.greenAccent.dark, width: '100%', - backgroundColor: theme.palette.backgroundA.lightest, + backgroundColor: theme.palette.greenAccent.contrastText, boxShadow: `0px 2px 4px -1px ${theme.palette.grey[400]}`, marginBottom: theme.spacing(2), })); @@ -675,13 +647,13 @@ export const StyledFormBox = styled(Box)(({ theme }) => ({ alignItems: 'center', padding: theme.spacing(3), minHeight: '100%', - // backgroundColor: theme.palette.backgroundA.lightest, + // backgroundColor: theme.palette.greenAccent.contrastText, borderRadius: theme.shape.borderRadius, color: theme.palette.text.primary, })); export const StyledDialogTitle = styled(DialogTitle)(({ theme }) => ({ - background: theme.palette.backgroundA.dark, // Or any other appropriate color - color: theme.palette.backgroundA.contrastTextA, + background: theme.palette.greenAccent.light, // Or any other appropriate color + color: theme.palette.grey.white, padding: theme.spacing(2), display: 'flex', justifyContent: 'space-between', @@ -691,7 +663,7 @@ export const StyledDialogTitle = styled(DialogTitle)(({ theme }) => ({ fontWeight: 'bold', }, '& .MuiIconButton-root': { - color: theme.palette.backgroundA.contrastTextA, // Ensure it stands out or matches + color: theme.palette.grey.white, // Ensure it stands out or matches }, })); @@ -707,7 +679,7 @@ export const PortfolioTablePaper = styled(Paper)(({ theme }) => ({ maxWidth: 'lg', margin: 'auto', overflowX: 'auto', // Ensures table doesn't overflow the paper - background: theme.palette.backgroundB.default, + background: theme.palette.grey.simpleGrey, padding: theme.spacing(2), [theme.breakpoints.up('xs')]: { padding: theme.spacing(1), // Smaller padding for xs @@ -730,34 +702,26 @@ export const PortfolioTable = styled(Table)(({ theme }) => ({ minHeight: 300, width: '100%', height: '100%', - background: theme.palette.backgroundA.lightest, + background: theme.palette.greenAccent.contrastText, })); // TABLE HEADER export const PortfolioTableHeader = styled(TableHead)(({ theme }) => ({ - background: theme.palette.backgroundA.lighter, - color: theme.palette.backgroundA.contrastTextA, + background: theme.palette.greenAccent.evenLighter, + color: theme.palette.grey.white, [theme.breakpoints.up('md')]: { fontSize: '1rem', }, })); // TABLE BODY export const PortfolioTableBody = styled(TableBody)(({ theme }) => ({ - background: theme.palette.backgroundA.lightest, + background: theme.palette.greenAccent.contrastText, 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, + // background: theme.palette.greenAccent.lightest, [theme.breakpoints.down('sm')]: { padding: theme.spacing(1), @@ -770,7 +734,7 @@ export const PortfolioTableCell = styled(TableCell)(({ theme }) => ({ })); // TABLE FOOTER export const PortfolioTableFooter = styled(TableFooter)(({ theme }) => ({ - backgroundColor: theme.palette.backgroundA.lighter, + backgroundColor: theme.palette.greenAccent.evenLighter, borderTop: `1px solid ${theme.palette.divider}`, '& .MuiTableCell-root': { padding: theme.spacing(2), @@ -779,7 +743,7 @@ export const PortfolioTableFooter = styled(TableFooter)(({ theme }) => ({ export const PortfolioPaginationActionsTableRow = styled(TableRow)( ({ theme }) => ({ // Example styling, you can customize as needed - backgroundColor: theme.palette.backgroundA.lighter, + backgroundColor: theme.palette.greenAccent.evenLighter, '&:hover': { backgroundColor: theme.palette.action.hover, }, @@ -794,11 +758,11 @@ export const PortfolioPaginationActionsTableCell = styled(TableCell)( // Example styling, you can customize as needed fontSize: '0.9rem', fontWeight: 'bold', - color: theme.palette.backgroundA.contrastTextA, - backgroundColor: theme.palette.backgroundA.lighter, + color: theme.palette.grey.white, + backgroundColor: theme.palette.greenAccent.evenLighter, '&:hover': { - color: theme.palette.backgroundA.contrastTextA, + color: theme.palette.grey.white, }, '& .MuiTableCell-root': { textAlign: 'right', @@ -828,7 +792,7 @@ export const PortfolioTablePriceBox = styled(Box)(({ theme }) => ({ alignItems: 'center', width: '100%', padding: theme.spacing(2), - background: theme.palette.backgroundB.lighter, + background: theme.palette.grey.lighterSimpleGrey, })); // ! CHART AND DATATABLES export const StyledChartBox = styled(Box)(({ theme }) => ({ @@ -886,20 +850,12 @@ export const PortfolioBox = styled(Box)(({ theme }) => ({ height: '100%', margin: theme.spacing(0, 'auto'), padding: theme.spacing(1, 2, 3), - // backgroundColor: theme.palette.backgroundE.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], - // backgroundColor: theme.palette.backgroundE.lightestBlue, - [theme.breakpoints.down('sm')]: { - // padding: theme.spacing(2), - }, + [theme.breakpoints.down('sm')]: {}, })); export const PortfolioBoxB = styled(Box)(({ theme }) => ({ display: 'flex', @@ -907,7 +863,7 @@ export const PortfolioBoxB = styled(Box)(({ theme }) => ({ gap: theme.spacing(4), borderRadius: theme.shape.borderRadius, flexGrow: 1, - background: theme.palette.backgroundD.dark, + background: theme.palette.greenAccent.crystalGreen, padding: theme.spacing(4), width: '100%', height: '100%', diff --git a/src/utils/FormFactory.jsx b/src/utils/FormFactory.jsx new file mode 100644 index 0000000..069a52f --- /dev/null +++ b/src/utils/FormFactory.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Grid } from '@mui/material'; +import FormInputText from './FormInputText'; +import FormInputDate from './FormInputDate'; + +export const FormFactory = ({ field, control }) => { + const { componentType, grid, ...fieldProps } = field; + + const renderFieldComponent = () => { + switch (componentType) { + case 'text': + return ; + case 'date': + return ; + default: + return null; // Default case if the type is unknown + } + }; + + return ( + + {renderFieldComponent()} + + ); +}; From 88557b8ac18449f3199b31fc94dfd78c97f15bb2 Mon Sep 17 00:00:00 2001 From: Reed Vogt Date: Tue, 9 Apr 2024 13:33:53 -0700 Subject: [PATCH 3/5] fixed time range chart update --- .vscode/settings.json | 4 +- package.json | 2 + public/index.html | 25 +- src/App.js | 42 +- src/Main.jsx | 12 +- src/assets/css/card.css | 97 +++ src/assets/css/index.css | 17 + src/assets/css/page.css | 17 + src/assets/currentlyUnused/AuthForm.jsx | 66 ++ .../currentlyUnused}/CarouselImage.jsx | 0 .../currentlyUnused}/CartPage.css | 0 src/assets/currentlyUnused/CartSummary.js | 29 + .../currentlyUnused/ChartErrorBoundary.jsx | 49 ++ src/assets/currentlyUnused/CollectionForm.jsx | 67 ++ .../CollectionStatisticsSelector.jsx | 54 ++ src/assets/currentlyUnused/DeckForm.jsx | 147 ++++ src/assets/currentlyUnused/Header.jsx | 28 + .../currentlyUnused}/ImageWithFallback.jsx | 0 .../currentlyUnused}/LongMenu.jsx | 0 src/assets/currentlyUnused/RCZodForm.jsx | 196 +++++ src/assets/currentlyUnused/SearchForm.jsx | 93 +++ src/assets/currentlyUnused/Select.jsx | 59 ++ src/assets/currentlyUnused/ThemeSelector.jsx | 51 ++ .../currentlyUnused/TimeRangeSelector.jsx | 42 ++ .../useSnackbarManager.jsx | 0 src/assets/currentlyUnused/useTimeRange.jsx | 91 +++ .../currentlyUnused}/utils/FormFactory.jsx | 0 src/assets/themes/base/colors.jsx | 1 + .../themes/base/customColorPalettes.jsx | 156 ++-- src/assets/themes/components/buttons/holo.jsx | 6 +- src/assets/themes/themeSettings.jsx | 1 + .../actionButtons/GenericActionButtons.jsx | 5 +- src/components/cards/GenericCard.jsx | 72 +- src/components/dialogs/CollectionDialog.jsx | 23 +- src/components/dialogs/DeckDialog.jsx | 16 +- src/components/dialogs/GenericCardDialog.jsx | 8 +- .../dialogs/SelectionErrorDialog.jsx | 90 --- src/components/forms/AuthForm.jsx | 66 -- src/components/forms/CollectionForm.jsx | 69 -- src/components/forms/DeckForm.jsx | 147 ---- .../forms/{reusable => Factory}/FormField.jsx | 5 - .../forms/Factory/RCDynamicForm.jsx | 249 +++++-- src/components/forms/Factory/RCInput.jsx | 580 +++++++++------ .../forms/{reusable => Factory}/RCSwitch.jsx | 4 - src/components/forms/Factory/RCTags.jsx | 54 ++ src/components/forms/Factory/RCTagsInput.jsx | 107 +++ .../forms/Factory/useRCFormHook.jsx | 13 - src/components/forms/SearchForm.jsx | 38 - src/components/forms/SearchSettingsForm.jsx | 50 -- src/components/forms/formsConfig.jsx | 395 +++++++--- .../forms/hooks/useFormManagement.jsx | 3 +- src/components/forms/hooks/useFormSelect.jsx | 84 +++ .../forms/hooks/useFormSubmission.jsx | 87 +-- .../forms/hooks/useInitialFormData.jsx | 31 + src/components/forms/hooks/useRCFormHook.jsx | 45 ++ src/components/forms/index.jsx | 20 - src/components/forms/reusable/RCZodForm.jsx | 196 ----- src/components/forms/reusable/Select.jsx | 54 -- .../forms/search/SearchComponent.jsx | 27 +- src/components/forms/search/SearchResults.jsx | 2 +- .../CollectionStatisticsSelector.jsx | 54 -- .../forms/selectors/ThemeSelector.jsx | 52 -- .../forms/selectors/TimeRangeSelector.jsx | 42 -- .../forms/selectors/useTimeRange.jsx | 45 -- src/config.json | 1 + src/context/DataContextProvider.jsx | 69 ++ src/context/Helpers.jsx | 13 + .../MAIN_CONTEXT/CardContext/useCardStore.jsx | 30 +- .../CollectionContext/CollectionContext.jsx | 26 - .../useCollectionManager.jsx | 15 +- .../useSelectedCollection.jsx | 89 ++- .../DeckContext/useSelectedDeck.jsx | 11 +- .../AppContext/AppContextProvider.jsx | 8 + .../AppContext/useCompileCardData.jsx | 31 +- .../FormContext/FormContext.jsx | 678 +++++++++--------- src/context/constants.jsx | 2 - src/context/hooks/oldhooks/useVisibility.jsx | 1 - src/context/hooks/useBreakPoint.jsx | 41 ++ src/context/hooks/useCardValues.jsx | 0 src/context/hooks/useDialogState.jsx | 1 - src/context/hooks/useGridItems.jsx | 1 + src/context/hooks/usePopover.jsx | 14 +- src/context/hooks/useValidateInnerData.jsx | 15 + src/context/index.js | 8 +- .../FormContext => data}/search.json | 0 src/data/searchData.jsx | 8 +- src/index.js | 23 - src/layout/REUSABLE_COMPONENTS/BoxHeader.jsx | 4 +- .../Configurator/index.jsx | 57 +- src/layout/REUSABLE_COMPONENTS/PageLayout.jsx | 1 + .../REUSABLE_COMPONENTS/RC/RCChange.jsx | 8 +- .../RCBUTTON/RCButtonRoot.jsx | 2 +- .../RCWRAPPEDICON/RCWrappedIcon.jsx | 13 +- .../REUSABLE_COMPONENTS/SkeletonVariants.jsx | 29 + .../{ => system-utils}/ErrorBoundary.jsx | 0 .../{ => system-utils}/ErrorIndicator.js | 2 +- .../{ => system-utils}/LoadingIndicator.js | 0 .../{ => system-utils}/LoadingOverlay.jsx | 9 +- .../REUSABLE_COMPONENTS/unique/SimpleCard.jsx | 12 +- .../ReusableStyledComponents.jsx | 9 +- src/layout/cart/CartContent.js | 78 +- src/layout/cart/CartSummary.js | 29 - src/layout/cart/Info.jsx | 38 + .../cart/cartPageContainers/Checkout.jsx | 15 +- .../CollectionPortfolioHeader.jsx | 29 +- .../SelectCollectionHeader.jsx | 28 +- src/layout/collection/TopCardsDisplayRow.jsx | 121 ++++ .../collectionGrids/ChartGridLayout.jsx | 289 ++++---- .../{cards-datatable => }/PricedDataTable.jsx | 2 +- .../cards-chart/ChartConfigs.jsx | 242 +++---- .../cards-chart/ChartErrorBoundary.jsx | 49 -- .../cards-chart/MyPortfolioLineChart.jsx | 123 ++++ .../cards-chart/UpdaterAndStatisticsRow.jsx | 39 - .../collections-list/CollectionListItem.jsx | 52 +- .../collections-list/SelectCollectionList.jsx | 151 ++-- .../collections-list/StatBoard.jsx | 44 +- .../collections-list/statItems/Header.jsx | 28 - .../statItems/ValuDistributionCircle.jsx | 18 +- src/layout/collection/index.jsx | 4 +- .../sub-components/TopCardsDisplayRow.jsx | 137 ---- src/layout/deck/DeckListItem.jsx | 89 +-- src/layout/deck/DeckPageHeader.jsx | 19 +- src/layout/deck/index.jsx | 25 +- src/layout/navigation/Navigation.jsx | 9 +- src/layout/sections/AnimatedFeatureCard.jsx | 23 - src/layout/sections/HeroChartSection.jsx | 128 ++++ src/layout/sections/HeroIconSection.jsx | 39 +- src/layout/sections/HeroSection.jsx | 156 +--- src/layout/sections/HeroSwiper.jsx | 9 +- src/layout/sections/HeroTextSection.jsx | 1 + src/layout/sections/MainContentSection.jsx | 19 - src/layout/store/StoreSearch.jsx | 1 - src/pages/CartPage.js | 113 +-- src/pages/HomePage.js | 8 +- src/pages/LoginDialog.jsx | 33 +- src/pages/LoginPage.jsx | 19 - src/pages/StorePage.js | 1 - 137 files changed, 4250 insertions(+), 3274 deletions(-) create mode 100644 src/assets/css/card.css create mode 100644 src/assets/css/page.css create mode 100644 src/assets/currentlyUnused/AuthForm.jsx rename src/{pages/pageStyles => assets/currentlyUnused}/CarouselImage.jsx (100%) rename src/{pages/pageStyles => assets/currentlyUnused}/CartPage.css (100%) create mode 100644 src/assets/currentlyUnused/CartSummary.js create mode 100644 src/assets/currentlyUnused/ChartErrorBoundary.jsx create mode 100644 src/assets/currentlyUnused/CollectionForm.jsx create mode 100644 src/assets/currentlyUnused/CollectionStatisticsSelector.jsx create mode 100644 src/assets/currentlyUnused/DeckForm.jsx create mode 100644 src/assets/currentlyUnused/Header.jsx rename src/{pages/pageStyles => assets/currentlyUnused}/ImageWithFallback.jsx (100%) rename src/{layout/navigation => assets/currentlyUnused}/LongMenu.jsx (100%) create mode 100644 src/assets/currentlyUnused/RCZodForm.jsx create mode 100644 src/assets/currentlyUnused/SearchForm.jsx create mode 100644 src/assets/currentlyUnused/Select.jsx create mode 100644 src/assets/currentlyUnused/ThemeSelector.jsx create mode 100644 src/assets/currentlyUnused/TimeRangeSelector.jsx rename src/assets/{ => currentlyUnused}/useSnackbarManager.jsx (100%) create mode 100644 src/assets/currentlyUnused/useTimeRange.jsx rename src/{ => assets/currentlyUnused}/utils/FormFactory.jsx (100%) delete mode 100644 src/components/dialogs/SelectionErrorDialog.jsx delete mode 100644 src/components/forms/AuthForm.jsx delete mode 100644 src/components/forms/CollectionForm.jsx delete mode 100644 src/components/forms/DeckForm.jsx rename src/components/forms/{reusable => Factory}/FormField.jsx (87%) rename src/components/forms/{reusable => Factory}/RCSwitch.jsx (93%) create mode 100644 src/components/forms/Factory/RCTags.jsx create mode 100644 src/components/forms/Factory/RCTagsInput.jsx delete mode 100644 src/components/forms/Factory/useRCFormHook.jsx delete mode 100644 src/components/forms/SearchForm.jsx delete mode 100644 src/components/forms/SearchSettingsForm.jsx create mode 100644 src/components/forms/hooks/useFormSelect.jsx create mode 100644 src/components/forms/hooks/useInitialFormData.jsx create mode 100644 src/components/forms/hooks/useRCFormHook.jsx delete mode 100644 src/components/forms/index.jsx delete mode 100644 src/components/forms/reusable/RCZodForm.jsx delete mode 100644 src/components/forms/reusable/Select.jsx delete mode 100644 src/components/forms/selectors/CollectionStatisticsSelector.jsx delete mode 100644 src/components/forms/selectors/ThemeSelector.jsx delete mode 100644 src/components/forms/selectors/TimeRangeSelector.jsx delete mode 100644 src/components/forms/selectors/useTimeRange.jsx create mode 100644 src/context/DataContextProvider.jsx create mode 100644 src/context/hooks/useBreakPoint.jsx create mode 100644 src/context/hooks/useCardValues.jsx create mode 100644 src/context/hooks/useValidateInnerData.jsx rename src/{context/UTILITIES_CONTEXT/FormContext => data}/search.json (100%) rename src/layout/REUSABLE_COMPONENTS/{ => system-utils}/ErrorBoundary.jsx (100%) rename src/layout/REUSABLE_COMPONENTS/{ => system-utils}/ErrorIndicator.js (97%) rename src/layout/REUSABLE_COMPONENTS/{ => system-utils}/LoadingIndicator.js (100%) rename src/layout/REUSABLE_COMPONENTS/{ => system-utils}/LoadingOverlay.jsx (75%) delete mode 100644 src/layout/cart/CartSummary.js create mode 100644 src/layout/cart/Info.jsx rename src/layout/collection/{sub-components => }/CollectionPortfolioHeader.jsx (76%) rename src/layout/collection/{collectionGrids/collections-list => }/SelectCollectionHeader.jsx (59%) create mode 100644 src/layout/collection/TopCardsDisplayRow.jsx rename src/layout/collection/collectionGrids/{cards-datatable => }/PricedDataTable.jsx (97%) delete mode 100644 src/layout/collection/collectionGrids/cards-chart/ChartErrorBoundary.jsx create mode 100644 src/layout/collection/collectionGrids/cards-chart/MyPortfolioLineChart.jsx delete mode 100644 src/layout/collection/collectionGrids/cards-chart/UpdaterAndStatisticsRow.jsx delete mode 100644 src/layout/collection/collectionGrids/collections-list/statItems/Header.jsx delete mode 100644 src/layout/collection/sub-components/TopCardsDisplayRow.jsx create mode 100644 src/layout/sections/HeroChartSection.jsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 0967ef4..6b665aa 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1 +1,3 @@ -{} +{ + "liveServer.settings.port": 5501 +} diff --git a/package.json b/package.json index 045fe72..f36462a 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,12 @@ "chroma-js": "^2.4.2", "date-fns": "^2.30.0", "dayjs": "^1.11.9", + "downshift": "^9.0.1", "image-downloader": "^4.3.0", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", "material-ui-image": "^3.3.2", + "mathjs": "^12.4.1", "moment": "^2.29.4", "notistack": "^3.0.1", "polished": "^4.3.1", diff --git a/public/index.html b/public/index.html index daf2c83..7ea0714 100644 --- a/public/index.html +++ b/public/index.html @@ -2,24 +2,27 @@ - - - + + + - + + + - +