From 7ad875cea5734e88daffc7e2d00773c638989f5d Mon Sep 17 00:00:00 2001 From: Reed Vogt Date: Mon, 22 Apr 2024 23:58:42 -0700 Subject: [PATCH] Fixes for styles to adjust for upload --- .../ActionButton.jsx | 14 +- cleanup/ChartConfigs.jsx | 76 ++++++ src/App.js | 5 +- src/Main.jsx | 2 +- src/components/cards/CardDetailsContainer.jsx | 40 +-- src/components/cards/GenericCard.jsx | 2 +- src/components/dialogs/GenericCardDialog.jsx | 4 +- src/components/forms/formsConfig.jsx | 6 +- src/context/DataContextProvider.jsx | 69 ------ src/context/Helpers.jsx | 81 ++----- .../AuthContext/useAuthManager.jsx | 7 +- .../useSelectedCollection.jsx | 89 +++---- .../DeckContext/useDeckManager.jsx | 6 +- .../DeckContext/useSelectedDeck.jsx | 17 +- src/context/MAIN_CONTEXT/apiUtils.jsx | 0 src/context/MAIN_CONTEXT/crud-utils.jsx | 31 +++ src/context/MAIN_CONTEXT/utility-hooks.jsx | 96 ++++++++ .../AppContext/useCompileCardData.jsx | 9 +- src/context/hooks/useLocalStorageManager.jsx | 19 ++ src/context/index.js | 2 - src/context/simplified_constants.jsx | 7 +- src/data/apiRoutes.json | 27 +++ .../GenericActionButtons.jsx | 97 ++++++-- src/layout/REUSABLE_COMPONENTS/Icons.jsx | 10 - .../REUSABLE_COMPONENTS/RC/RCChange.jsx | 2 +- .../RCLOGOSECTION/RCLogoSection.jsx | 2 +- .../{ => layout-utils}/BoxHeader.jsx | 22 +- .../{ => layout-utils}/DashBoardLayout.jsx | 4 +- .../{ => layout-utils}/DashboardBox.jsx | 0 .../{ => layout-utils}/FlexBetween.jsx | 0 .../{ => layout-utils}/NivoContainer.jsx | 0 .../{ => layout-utils}/PageHeader.jsx | 16 +- .../{ => layout-utils}/PageLayout.jsx | 4 +- .../{ => layout-utils}/StatBox.jsx | 6 +- .../{ => system-utils}/ProgressCircle.jsx | 6 +- .../system-utils/SkeletonVariants.jsx | 3 +- .../REUSABLE_COMPONENTS/unique/SimpleCard.jsx | 2 +- src/layout/collection/ChartConfigs.jsx | 195 --------------- src/layout/collection/ChartGridLayout.jsx | 102 ++++++-- src/layout/collection/CollectionListItem.jsx | 15 +- .../collection/CollectionPortfolioHeader.jsx | 2 +- .../collection/SelectCollectionList.jsx | 104 +++++--- src/layout/collection/StatBoard.jsx | 9 +- src/layout/collection/TopCardsDisplayRow.jsx | 92 ++++++- src/layout/collection/data/portfolioData.jsx | 3 +- src/layout/collection/index.jsx | 19 +- .../collection/statItems/PricedCardList.jsx | 2 +- .../statItems/TotalCardsCollectedStatBox.jsx | 2 +- .../statItems/TotalPriceStatBox.jsx | 2 +- .../statItems/ValuDistributionCircle.jsx | 5 +- src/layout/deck/DeckListItem.jsx | 4 +- src/layout/deck/index.jsx | 229 ++++++++++-------- src/layout/home/HeroChartSection.jsx | 6 +- src/layout/profile/index.jsx | 2 +- src/layout/store/index.jsx | 2 +- src/pages/CartPage.js | 2 +- src/pages/CollectionPage.js | 22 +- src/pages/DeckBuilderPage.js | 10 +- src/pages/HomePage.js | 2 +- src/pages/StorePage.js | 2 +- 60 files changed, 892 insertions(+), 724 deletions(-) rename {src/components/buttons/actionButtons => cleanup}/ActionButton.jsx (86%) create mode 100644 cleanup/ChartConfigs.jsx delete mode 100644 src/context/DataContextProvider.jsx create mode 100644 src/context/MAIN_CONTEXT/apiUtils.jsx create mode 100644 src/context/MAIN_CONTEXT/crud-utils.jsx create mode 100644 src/context/MAIN_CONTEXT/utility-hooks.jsx create mode 100644 src/context/hooks/useLocalStorageManager.jsx create mode 100644 src/data/apiRoutes.json rename src/{components/buttons/actionButtons => layout/REUSABLE_COMPONENTS}/GenericActionButtons.jsx (65%) delete mode 100644 src/layout/REUSABLE_COMPONENTS/Icons.jsx rename src/layout/REUSABLE_COMPONENTS/{ => layout-utils}/BoxHeader.jsx (83%) rename src/layout/REUSABLE_COMPONENTS/{ => layout-utils}/DashBoardLayout.jsx (91%) rename src/layout/REUSABLE_COMPONENTS/{ => layout-utils}/DashboardBox.jsx (100%) rename src/layout/REUSABLE_COMPONENTS/{ => layout-utils}/FlexBetween.jsx (100%) rename src/layout/REUSABLE_COMPONENTS/{ => layout-utils}/NivoContainer.jsx (100%) rename src/layout/REUSABLE_COMPONENTS/{ => layout-utils}/PageHeader.jsx (79%) rename src/layout/REUSABLE_COMPONENTS/{ => layout-utils}/PageLayout.jsx (93%) rename src/layout/REUSABLE_COMPONENTS/{ => layout-utils}/StatBox.jsx (94%) rename src/layout/REUSABLE_COMPONENTS/{ => system-utils}/ProgressCircle.jsx (94%) delete mode 100644 src/layout/collection/ChartConfigs.jsx diff --git a/src/components/buttons/actionButtons/ActionButton.jsx b/cleanup/ActionButton.jsx similarity index 86% rename from src/components/buttons/actionButtons/ActionButton.jsx rename to cleanup/ActionButton.jsx index 22b6054..618fe64 100644 --- a/src/components/buttons/actionButtons/ActionButton.jsx +++ b/cleanup/ActionButton.jsx @@ -1,7 +1,7 @@ import { LoadingButton } from '@mui/lab'; -import { useMode } from '../../../context'; -import MDTypography from '../../../layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; -import { useLoading } from '../../../context/hooks/useLoading'; +import { useMode } from '../src/context'; +import MDTypography from '../src/layout/REUSABLE_COMPONENTS/MDTYPOGRAPHY/MDTypography'; +import { useLoading } from '../src/context/hooks/useLoading'; import AddCircleOutlineOutlined from '@mui/icons-material/AddCircleOutlineOutlined'; import RemoveCircleOutlineOutlined from '@mui/icons-material/RemoveCircleOutlineOutlined'; @@ -38,14 +38,6 @@ const ActionButton = ({ const { theme } = useMode(); const { isLoading } = useLoading(); const adjustedButtonSize = variant === 'data-table' ? 'small' : buttonSize; - - const { buttonLabel, buttonVariant } = getLabelAndVariant( - adjustedButtonSize, - labelValue, - actionType - ); - - // Select icon based on action type const actionIcon = actionType === 'add' ? ( diff --git a/cleanup/ChartConfigs.jsx b/cleanup/ChartConfigs.jsx new file mode 100644 index 0000000..717cd67 --- /dev/null +++ b/cleanup/ChartConfigs.jsx @@ -0,0 +1,76 @@ +// /* eslint-disable max-len */ +// import { useAppContext, useMode } from '../../context'; +// import { useEffect, useMemo, useState } from 'react'; +// import NivoContainer from '../REUSABLE_COMPONENTS/layout-utils/NivoContainer'; +// import { BasicTooltip } from '@nivo/tooltip'; +// import { useEventHandlers } from '../../context/hooks/useEventHandlers'; +// import MyResponsiveLine from './MyPortfolioLineChart'; +// import { ChartArea } from '../../pages/pageStyles/StyledComponents'; +// import { useForm, useWatch } from 'react-hook-form'; +// import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +// import useRCFormHook from '../../components/forms/hooks/useRCFormHook'; +// import { useCompileCardData } from '../../context/MISC_CONTEXT/AppContext/useCompileCardData'; +// import LoadingOverlay from '../REUSABLE_COMPONENTS/system-utils/LoadingOverlay'; +// import { i } from 'mathjs'; +// const formatDateBasedOnRange = (range) => { +// const formatMap = { +// '24hr': { format: '%H:%M', ticks: 'every hour' }, +// '7d': { format: '%b %d', ticks: 'every day' }, +// '30d': { format: '%b %d', ticks: 'every day' }, +// '90d': { format: '%b %d', ticks: 'every 3 days' }, +// '180d': { format: '%b %d', ticks: 'every 6 days' }, +// '270d': { format: '%b %d', ticks: 'every 9 days' }, +// '365d': { format: '%b %d', ticks: 'every 12 days' }, +// default: { format: '%b %d', ticks: 'every day' }, +// }; + +// return formatMap[range] || formatMap.default; +// }; +// export const ChartConfiguration = () => { +// const { theme } = useMode(); +// const { greenAccent, redAccent, grey } = theme.palette.chartTheme; +// const { selectedTimeRange = '24hr' } = useCompileCardData(); +// const { selectedCollection, createMarkers } = useSelectedCollection(); +// if (!selectedCollection) { +// return ; +// } +// const memoChartData = useMemo(() => { +// const { selectedChartData, averagedChartData, selectedChartDataKey } = +// selectedCollection; +// return !selectedChartData.data.length +// ? averagedChartData[selectedChartDataKey] +// : selectedChartData; +// }, [selectedCollection]); +// const validMarkers = useMemo(() => { +// const markers = createMarkers(selectedCollection); +// return markers.filter((marker) => marker.id && marker.value !== undefined); +// }, [createMarkers, selectedCollection]); +// const tickValues = useMemo(() => { +// const { ticks } = formatDateBasedOnRange(selectedTimeRange); +// return ticks.split(' ').map((tick) => new Date(tick).getTime()); +// }, [selectedTimeRange]); +// useEffect(() => { +// console.log('Chart data:', memoChartData); +// }, [memoChartData]); +// if (!memoChartData) { +// return ; +// } + +// return ( +// +// +// +// +// +// ); +// }; diff --git a/src/App.js b/src/App.js index 1ffa375..9a2f43d 100644 --- a/src/App.js +++ b/src/App.js @@ -15,7 +15,6 @@ import { SidebarProvider, AppContextProvider, ConfiguratorProvider, - DataProvider, } from './context'; import { ThemeProvider } from 'styled-components'; import { CssBaseline, GlobalStyles } from '@mui/material'; @@ -39,9 +38,7 @@ const App = () => { - -
- +
diff --git a/src/Main.jsx b/src/Main.jsx index eb53cc8..5b25a7d 100644 --- a/src/Main.jsx +++ b/src/Main.jsx @@ -3,7 +3,7 @@ import { Route, Routes } from 'react-router-dom'; 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 PageLayout from './layout/REUSABLE_COMPONENTS/layout-utils/PageLayout.jsx'; import Navigation from './layout/navigation/Navigation.jsx'; import Configurator from './layout/REUSABLE_COMPONENTS/Configurator/index.jsx'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; diff --git a/src/components/cards/CardDetailsContainer.jsx b/src/components/cards/CardDetailsContainer.jsx index 7293b53..79c35c7 100644 --- a/src/components/cards/CardDetailsContainer.jsx +++ b/src/components/cards/CardDetailsContainer.jsx @@ -94,7 +94,7 @@ const CardDetailPrice = ({ value }) => ( const CardDetailRarity = ({ values, onRarityClick }) => { const { theme } = useMode(); - return values?.map((rarity, index) => ( + return values?.map((rarity, index) => { { margin: '5px', }} variant="outlined" - /> - )); + />; + }); }; const CardDetailSet = ({ values }) => { const { theme } = useMode(); @@ -148,12 +148,15 @@ const RenderDetailsSection = ({ details, card, className, handleAction }) => { return ( {details?.map((detail, index) => ( - - - - {detail.title}: - {detail.key === 'title' && } - + + + {detail.title}:} + /> + + {/* */} + {/* {detail.title}: */} + {/* {detail.key === 'title' && } */} {detail.key === 'desc' && ( @@ -162,13 +165,17 @@ const RenderDetailsSection = ({ details, card, className, handleAction }) => { )} {detail.key === 'rarities' && ( - + + + )} {detail.key === 'card_sets' && ( - + + + )} {/* {detail.icon && ( { } /> )} */} - + {/* */} + ))} @@ -221,7 +229,7 @@ const CardDetailsContainer = ({ return ( { console.log('Update Collection Form Data:', formData, additionalData); updateCollection(formData, additionalData); }, - updateDeckForm: (formData, additionalData) => { - console.log('Update Deck Form Data:', formData, additionalData); - updateDeckDetails(formData, additionalData); + updateDeckForm: (formData) => { + console.log('Update Deck Form Data:', formData); + updateDeckDetails(formData); }, addDeckForm: (formData, additionalData) => { console.log('Add Deck Form Data:', formData, additionalData); diff --git a/src/context/DataContextProvider.jsx b/src/context/DataContextProvider.jsx deleted file mode 100644 index 12340e7..0000000 --- a/src/context/DataContextProvider.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { - createContext, - useContext, - useRef, - useEffect, - useState, -} from 'react'; - -const StoreContext = createContext(null); - -const useData = () => { - const store = useRef({}); - const subscribers = useRef(new Set()); - - const set = (value) => { - store.current = { ...store.current, ...value }; - subscribers.current.forEach((subscriber) => { - const { callback, selector } = subscriber; - const selectedValue = selector(store.current); - callback(selectedValue); - }); - }; - - const subscribe = (callback, selector) => { - const subscriber = { callback, selector }; - const selectedValue = selector(store.current); - subscribers.current.add(subscriber); - callback(selectedValue); // Initialize the subscriber with the current selected value - - return () => subscribers.current.delete(subscriber); - }; - - return { set, subscribe }; -}; - -export const DataProvider = ({ children }) => { - const store = useData(); - - return ( - {children} - ); -}; - -export const useDataContext = () => { - return useContext(StoreContext); -}; -export const useStore = (selector) => { - const { subscribe, set } = useContext(StoreContext); - if (!subscribe || !set) { - throw new Error('useStore must be used within a StoreProvider'); - } - - // Initial state is now fetched from the subscribe function inside a useEffect hook - // to ensure it uses the most up-to-date store reference - const [selectedState, setSelectedState] = useState(); - - useEffect(() => { - const callback = (newSelectedState) => { - setSelectedState(newSelectedState); - }; - // Subscribe and get unsubscribe function for cleanup - const unsubscribe = subscribe(callback, selector); - - // Cleanup function to unsubscribe - return unsubscribe; - }, [subscribe, selector]); - - return [selectedState, set]; -}; diff --git a/src/context/Helpers.jsx b/src/context/Helpers.jsx index ac48260..6d8fad4 100644 --- a/src/context/Helpers.jsx +++ b/src/context/Helpers.jsx @@ -1,20 +1,11 @@ -/** - * The base URL for all API calls. - * @type {String} - */ +const roundDecimalToWholeNumber = (value) => { + return Math.round(value * 100) / 100; +}; +export const roundToNearestTenth = (value) => { + return Math.round(value * 10) / 10; +}; 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}`; -/** - * Calculates the total price of a collection based on the card prices and quantities. - * - * @param {Object} collection - The collection object with card information. - * @returns {number} The total price of the collection. - */ export const calculateAndUpdateTotalPrice = (collection) => { let totalPrice = 0; if (collection && collection.cards) { @@ -24,26 +15,6 @@ 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); -}; -/** - * Access the values of an object by its keys. - * @param {Object} obj - The object to access. - * @param {Array} keys - The keys to access. - * @returns {Array} The values of the keys. - * * @example accessValues({ a: 1, b: 2, c: 3 }, ['a', 'c']) => [1, 3] - **/ -export const accessMapValues = (schema) => { - return Object.keys(schema.shape).reduce((acc, key) => { - acc[key] = ''; - return acc; - }, {}); -}; export const getCardQuantity = (collectionId, allCollections) => { // Assuming allCollections is an array of collection objects const collection = allCollections.find((coll) => coll._id === collectionId); @@ -60,38 +31,12 @@ export const getCartCardQuantity = (cart, cardId) => { }); 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 ( - [Object, Array].includes((obj || {}).constructor) && - !Object.entries(obj || {}).length - ); -}; -/** - * Creates a new price entry object. - * @param {number} price - The price to be added to the price entry. - * @returns {object} - The new price entry object. - * */ export const createNewPriceEntry = (price) => { return { num: price, timestamp: new Date(), }; }; -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'; @@ -106,3 +51,17 @@ export const calculateChangePercentage = (collectionData) => { const percentageChange = ((newValue - oldValue) / oldValue) * 100; return percentageChange?.toFixed(2) + '%'; // Format to 2 decimal places }; +export const formatDateBasedOnRange = (range) => { + const formatMap = { + '24hr': { format: '%H:%M', ticks: 'every hour' }, + '7d': { format: '%b %d', ticks: 'every day' }, + '30d': { format: '%b %d', ticks: 'every day' }, + '90d': { format: '%b %d', ticks: 'every 3 days' }, + '180d': { format: '%b %d', ticks: 'every 6 days' }, + '270d': { format: '%b %d', ticks: 'every 9 days' }, + '365d': { format: '%b %d', ticks: 'every 12 days' }, + default: { format: '%b %d', ticks: 'every day' }, + }; + + return formatMap[range] || formatMap.default; +}; diff --git a/src/context/MAIN_CONTEXT/AuthContext/useAuthManager.jsx b/src/context/MAIN_CONTEXT/AuthContext/useAuthManager.jsx index 2392f87..727f89d 100644 --- a/src/context/MAIN_CONTEXT/AuthContext/useAuthManager.jsx +++ b/src/context/MAIN_CONTEXT/AuthContext/useAuthManager.jsx @@ -63,7 +63,12 @@ function useAuthManager() { `${endpoint}` ); if (!responseData?.data) throw new Error('Invalid response structure'); - console.log(responseData); + if (endpoint === 'signout') { + console.log(responseData?.message); + } else { + console.log(responseData?.data); + setAuthCookies(responseData.data); + } setAuthCookies(responseData.data); } catch (error) { console.error('Auth action error:', error); diff --git a/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx b/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx index c55149e..216d855 100644 --- a/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx +++ b/src/context/MAIN_CONTEXT/CollectionContext/useSelectedCollection.jsx @@ -1,79 +1,60 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { DEFAULT_COLLECTION, SELECTED_COLLECTION_ID } from '../../constants'; +import { SELECTED_COLLECTION_ID } from '../../constants'; import useLocalStorage from '../../hooks/useLocalStorage'; import { defaultValues } from '../../simplified_constants'; - import _set from 'lodash/set'; function useSelectedCollection() { - const [collections, setCollections] = useLocalStorage('collections', { - allIds: [], - byId: { - [SELECTED_COLLECTION_ID]: DEFAULT_COLLECTION, - }, - selectedId: SELECTED_COLLECTION_ID, - prevSelectedId: null, - showCollections: true, - }); - useEffect(() => { - if (!collections || collections?.allIds?.length === 0) { - setCollections(defaultValues.defaultCollections); - } - }, []); + const [collections, setCollections] = useLocalStorage( + 'collections', + defaultValues.defaultCollections + ); useEffect(() => { - if (!collections || collections?.allIds?.length === 0) { + // Check if the collections need initialization or if the default collection should be filtered out + if ( + !collections || + collections.allIds.length === 0 || + collections.allIds.includes(SELECTED_COLLECTION_ID) + ) { const updatedById = { ...defaultValues.defaultCollections.byId }; Object.keys(updatedById).forEach((id) => { const collection = updatedById[id]; + // Check and assign chart data if available if ( collection.averagedChartData && collection.averagedChartData['24hr'] ) { updatedById[id].selectedChartData = collection.averagedChartData['24hr']; - console.log( - 'UPDATED SELECTED CHART DATA', - collection.averagedChartData['24hr'] - ); } }); + + // Remove the default selected ID if it's not supposed to be part of the initial setup + if (updatedById[SELECTED_COLLECTION_ID]) { + delete updatedById[SELECTED_COLLECTION_ID]; + } + + // Filter out the SELECTED_COLLECTION_ID from allIds if it's currently included + const updatedAllIds = collections.allIds.filter( + (id) => id !== SELECTED_COLLECTION_ID + ); + setCollections({ ...defaultValues.defaultCollections, byId: updatedById, + allIds: updatedAllIds, + // Ensure the selectedId is updated appropriately, default to the first valid ID or null + selectedId: updatedAllIds.length > 0 ? updatedAllIds[0] : null, }); } }, []); - 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 [selectedStat, setSelectedStat] = useState(''); - useEffect(() => { - prevSelectedCollectionIdRef.current = selectedCollectionId; - }, [selectedCollectionId]); + const [collectionUpdated, setCollectionUpdated] = useState(false); + // useEffect(() => { + // prevSelectedCollectionIdRef.current = selectedCollectionId; + // }, [selectedCollectionId]); const getSelectedCollection = useMemo(() => { const selectedCollection = collections.byId[collections.selectedId]; if (!selectedCollection) { @@ -178,6 +159,9 @@ function useSelectedCollection() { }, [setCollections] ); + const refreshCollections2 = useCallback(() => { + setCollectionUpdated((prevState) => !prevState); // Toggle to trigger a re-render + }, []); const updateCollectionsData = useCallback( (newCollections, action, deletedCollectionId) => { console.log('updateCollectionsData', newCollections, action); @@ -402,11 +386,13 @@ function useSelectedCollection() { collections.selectedId || prevSelectedCollectionIdRef.current, selectedCollection: getSelectedCollection || {}, allIds: collections?.allIds || [], - allCollections: Object.values(collections?.byId), + allCollections: Object.values(collections?.byId).filter( + (collection) => collection?._id !== '' + ), showCollections: !!collections.showCollections, byId: collections?.byId || {}, removeCardFromCollection, - + collections, selectedStat, setSelectedStat, createMarkers, @@ -419,6 +405,7 @@ function useSelectedCollection() { handleRemoveCard, updateCollectionsData, customError, + refreshCollections2, refreshCollections, addNewCollection, removeCollection, diff --git a/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx b/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx index 9330aa1..b74c345 100644 --- a/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx +++ b/src/context/MAIN_CONTEXT/DeckContext/useDeckManager.jsx @@ -37,7 +37,7 @@ const useDeckManager = () => { setError('User is not logged in or request is in loading state.'); return; } - + console.log('DECK DATA', deckData); const url = createApiUrl(urlSuffix); try { const response = await fetchWrapper(url, method, deckData, actionName); @@ -85,9 +85,9 @@ const useDeckManager = () => { const createNewDeck = (deckData) => performFetchAndUpdate('create', 'POST', deckData, 'createNewDeck'); - const updateDeckDetails = (deckId, deckData) => + const updateDeckDetails = (deckData) => performFetchAndUpdate( - `${deckId}/deckDetails`, + `${deckData._id}/deckDetails`, 'PUT', deckData, 'updateDeckDetails' diff --git a/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx b/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx index 289c0a0..c0f59d8 100644 --- a/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx +++ b/src/context/MAIN_CONTEXT/DeckContext/useSelectedDeck.jsx @@ -12,7 +12,6 @@ import useManageCookies from '../../hooks/useManageCookies'; // showDecks: true, // }; const useSelectedDeck = () => { - const { addCookies } = useManageCookies(); const [decks, setDecks] = useLocalStorage( 'decks', defaultValues.defaultDecks @@ -50,7 +49,7 @@ const useSelectedDeck = () => { modifyDeck((currentDecks) => ({ ...currentDecks, - selectedId: deck._id, + selectedId: deck?._id, prevSelectedId: currentDecks.selectedId, // Preserve the previously selected ID showDecks: currentDecks.showDecks, })); @@ -69,11 +68,11 @@ const useSelectedDeck = () => { const updatedAllIds = [...currentDecks.allIds]; const updatedById = { ...currentDecks.byId }; - if (deck._id !== SELECTED_DECK_ID) { - updatedById[deck._id] = deck; + if (deck?._id !== SELECTED_DECK_ID) { + updatedById[deck?._id] = deck; - if (!updatedAllIds.includes(deck._id)) { - updatedAllIds.push(deck._id); + if (!updatedAllIds.includes(deck?._id)) { + updatedAllIds.push(deck?._id); } } // Delete the default deck data from `byId`, if it exists @@ -114,9 +113,9 @@ const useSelectedDeck = () => { // (id) => id !== SELECTED_DECK_ID // ); allDecksFromServer.forEach((deck) => { - updatedById[deck._id] = deck; - if (!updatedAllIds.includes(deck._id)) { - updatedAllIds.push(deck._id); + updatedById[deck?._id] = deck; + if (!updatedAllIds.includes(deck?._id)) { + updatedAllIds.push(deck?._id); } }); return { diff --git a/src/context/MAIN_CONTEXT/apiUtils.jsx b/src/context/MAIN_CONTEXT/apiUtils.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/context/MAIN_CONTEXT/crud-utils.jsx b/src/context/MAIN_CONTEXT/crud-utils.jsx new file mode 100644 index 0000000..38ed4bd --- /dev/null +++ b/src/context/MAIN_CONTEXT/crud-utils.jsx @@ -0,0 +1,31 @@ +// // utilities.js +// export const updateField = (setFunction, path, value) => { +// setFunction((currentData) => { +// const newData = { ...currentData }; +// _.set(newData, path, value); +// return newData; +// }); +// }; + +// export const addItem = (setFunction, item, idField = '_id') => { +// setFunction((prevData) => { +// const newData = { ...prevData }; +// newData.byId[item[idField]] = item; +// if (!newData.allIds.includes(item[idField])) { +// newData.allIds.push(item[idField]); +// } +// return newData; +// }); +// }; + +// export const removeItem = (setFunction, itemId) => { +// setFunction((prevData) => { +// const { [itemId]: _, ...remainingById } = prevData.byId; +// const remainingAllIds = prevData.allIds.filter((id) => id !== itemId); +// return { +// ...prevData, +// byId: remainingById, +// allIds: remainingAllIds, +// }; +// }); +// }; diff --git a/src/context/MAIN_CONTEXT/utility-hooks.jsx b/src/context/MAIN_CONTEXT/utility-hooks.jsx new file mode 100644 index 0000000..63cc25e --- /dev/null +++ b/src/context/MAIN_CONTEXT/utility-hooks.jsx @@ -0,0 +1,96 @@ +import { useCallback } from 'react'; +import { defaultValues } from '../simplified_constants'; +import { + useLocalStorageManager, + updateField, + addItem, + removeItem, +} from './utilities'; + +function useSelectedCollection() { + const [collections, modifyCollections] = useLocalStorageManager( + 'collections', + defaultValues.defaultCollections + ); + + // Example usage of utility functions + const addCollection = useCallback( + (collection) => { + addItem(modifyCollections, collection); + }, + [modifyCollections] + ); + + const updateCollectionField = useCallback( + (collectionId, fieldPath, value) => { + updateField( + modifyCollections, + `byId.${collectionId}.${fieldPath}`, + value + ); + }, + [modifyCollections] + ); + + const removeCollection = useCallback( + (collectionId) => { + removeItem(modifyCollections, collectionId); + }, + [modifyCollections] + ); + + return { + collections, + addCollection, + updateCollectionField, + removeCollection, + }; +} + +function useSelectedDeck() { + const [decks, modifyDecks] = useLocalStorageManager( + 'decks', + defaultValues.defaultDecks + ); + + const addDeck = useCallback( + (deck) => { + addItem(modifyDecks, deck); + }, + [modifyDecks] + ); + + const updateDeckField = useCallback( + (deckId, fieldPath, value) => { + updateField(modifyDecks, `byId.${deckId}.${fieldPath}`, value); + }, + [modifyDecks] + ); + + const removeDeck = useCallback( + (deckId) => { + removeItem(modifyDecks, deckId); + }, + [modifyDecks] + ); + + // Optionally, implement any deck-specific logic if necessary + const selectDeck = useCallback( + (deckId) => { + modifyDecks((prevDecks) => ({ + ...prevDecks, + selectedId: deckId, + prevSelectedId: prevDecks.selectedId, // Tracking previous selection for UX purposes + })); + }, + [modifyDecks] + ); + + return { + decks, + addDeck, + updateDeckField, + removeDeck, + selectDeck, + }; +} diff --git a/src/context/MISC_CONTEXT/AppContext/useCompileCardData.jsx b/src/context/MISC_CONTEXT/AppContext/useCompileCardData.jsx index e2c270e..db11b7a 100644 --- a/src/context/MISC_CONTEXT/AppContext/useCompileCardData.jsx +++ b/src/context/MISC_CONTEXT/AppContext/useCompileCardData.jsx @@ -82,15 +82,7 @@ export const useCompileCardData = () => { allCollections.forEach((collection) => { const collectionPrice = parseFloat(collection?.totalPrice); if (isNaN(collectionPrice)) return; - // collectionCards.push({ - // id: collection._id, - // name: collection.name, - // price: collectionPrice, - // }); totalValue += collectionPrice; - }); - allCollections.forEach((collection) => { - if (!collection.cards) return; collectionCards.push(...collection.cards); }); const uniqueCards = new Map(); @@ -104,6 +96,7 @@ export const useCompileCardData = () => { const topFiveCardsInCollection = uniqueCardsArray .sort((a, b) => b.price - a.price) .slice(0, 5); + console.log('TOP FIVE CARDS', topFiveCardsInCollection); const metaData = { totalValue: totalValue, numCardsCollected: collectionCards?.reduce((total, card) => { diff --git a/src/context/hooks/useLocalStorageManager.jsx b/src/context/hooks/useLocalStorageManager.jsx new file mode 100644 index 0000000..55ac9a8 --- /dev/null +++ b/src/context/hooks/useLocalStorageManager.jsx @@ -0,0 +1,19 @@ +// useLocalStorageManager.js +import { useCallback } from 'react'; +import useLocalStorage from './useLocalStorage'; + +export const useLocalStorageManager = (key, defaultValue) => { + const [data, setData] = useLocalStorage(key, defaultValue); + + const modifyData = useCallback( + (modifyFn) => { + setData((prevData) => { + const updatedData = modifyFn({ ...prevData }); + return updatedData; + }); + }, + [setData] + ); + + return [data, modifyData]; +}; diff --git a/src/context/index.js b/src/context/index.js index 283fd8f..c811b8f 100644 --- a/src/context/index.js +++ b/src/context/index.js @@ -10,7 +10,6 @@ export { useAppContext } from './MISC_CONTEXT/AppContext/AppContextProvider'; export { useAuthContext } from './MAIN_CONTEXT/AuthContext/authContext'; export { useMode } from './UTILITIES_CONTEXT/ColorModeContext/useMode'; export { useConfiguratorContext } from './UTILITIES_CONTEXT/ConfiguratorContext/ConfiguratorContext'; -export { useDataContext } from './DataContextProvider'; // Contexts export { default as ErrorBoundary } from '../layout/REUSABLE_COMPONENTS/system-utils/ErrorBoundary'; @@ -25,4 +24,3 @@ export { ModalProvider } from './UTILITIES_CONTEXT/ModalContext/ModalContext'; export { ColorModeProvider } from './UTILITIES_CONTEXT/ColorModeContext/ColorModeProvider'; export { SidebarProvider } from './UTILITIES_CONTEXT/SideBarContext/SideBarProvider'; export { ConfiguratorProvider } from './UTILITIES_CONTEXT/ConfiguratorContext/ConfiguratorContext'; -export { DataProvider } from './DataContextProvider'; diff --git a/src/context/simplified_constants.jsx b/src/context/simplified_constants.jsx index eb3cd8d..bbca98c 100644 --- a/src/context/simplified_constants.jsx +++ b/src/context/simplified_constants.jsx @@ -11,9 +11,9 @@ 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 defaultDecks = { - allIds: [SELECTED_DECK_ID], - byId: { [SELECTED_DECK_ID]: DEFAULT_DECK }, - selectedId: SELECTED_DECK_ID, + allIds: [], + byId: {}, + selectedId: null, lastUpdated: new Date(), showDecks: true, }; @@ -23,6 +23,7 @@ const defaultCollections = { [SELECTED_COLLECTION_ID]: DEFAULT_COLLECTION, }, selectedId: SELECTED_COLLECTION_ID, + // lastUpdated: new Date(), prevSelectedId: SELECTED_COLLECTION_ID, showCollections: false, }; diff --git a/src/data/apiRoutes.json b/src/data/apiRoutes.json new file mode 100644 index 0000000..03aa185 --- /dev/null +++ b/src/data/apiRoutes.json @@ -0,0 +1,27 @@ +{ + "collections": { + "create": "/collections/create", + "update": "/collections/update", + "delete": "/collections/delete", + "addCard": "/collections/addCard", + "incrementCard": "/collections/incrementCard", + "decrementCard": "/collections/decrementCard", + "deleteCard": "/collections/deleteCard" + }, + "decks": { + "create": "/decks/create", + "update": "/decks/update", + "delete": "/decks/delete", + "addCard": "/decks/addCard", + "incrementCard": "/decks/incrementCard", + "decrementCard": "/decks/decrementCard", + "deleteCard": "/decks/deleteCard" + }, + "cart": { + "update": "/cart/update", + "addItem": "/cart/addItem", + "incrementItem": "/cart/incrementItem", + "decrementItem": "/cart/decrementItem", + "deleteItem": "/cart/deleteItem" + } +} diff --git a/src/components/buttons/actionButtons/GenericActionButtons.jsx b/src/layout/REUSABLE_COMPONENTS/GenericActionButtons.jsx similarity index 65% rename from src/components/buttons/actionButtons/GenericActionButtons.jsx rename to src/layout/REUSABLE_COMPONENTS/GenericActionButtons.jsx index 6493d83..7d31424 100644 --- a/src/components/buttons/actionButtons/GenericActionButtons.jsx +++ b/src/layout/REUSABLE_COMPONENTS/GenericActionButtons.jsx @@ -1,17 +1,21 @@ 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 useCollectionManager from '../../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; -import ActionButton from './ActionButton'; +import MDTypography from './MDTYPOGRAPHY/MDTypography'; +import { getContextIcon } from './icons/index'; +import useCollectionManager from '../../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; 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/system-utils/LoadingOverlay'; -import useSelectedCollection from '../../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -import useSelectedDeck from '../../../context/MAIN_CONTEXT/DeckContext/useSelectedDeck'; +import GlassyIcon from './icons/GlassyIcon'; +import MDBox from './MDBOX'; +import useDeckManager from '../../context/MAIN_CONTEXT/DeckContext/useDeckManager'; +import { useCartManager } from '../../context/MAIN_CONTEXT/CartContext/useCartManager'; +import LoadingOverlay from './system-utils/LoadingOverlay'; +import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; +import useSelectedDeck from '../../context/MAIN_CONTEXT/DeckContext/useSelectedDeck'; +import AddCircleOutlineOutlined from '@mui/icons-material/AddCircleOutlineOutlined'; +import RemoveCircleOutlineOutlined from '@mui/icons-material/RemoveCircleOutlineOutlined'; +import { useMode } from '../../context'; +import { useLoading } from '../../context/hooks/useLoading'; +import { LoadingButton } from '@mui/lab'; const buttonSizeMap = { xs: 'extraSmall', @@ -19,7 +23,63 @@ const buttonSizeMap = { md: 'medium', lg: 'large', }; +const ActionButton = ({ + buttonSize, + handleCardAction, + labelValue, + actionType, + variant, +}) => { + const { theme } = useMode(); + const { isLoading } = useLoading(); + const adjustedButtonSize = variant === 'data-table' ? 'small' : buttonSize; + const actionIcon = + actionType === 'add' ? ( + + ) : ( + + ); + const loadingKey = + actionType === 'add' ? 'addCardsToCollection' : 'removeCardsFromCollection'; + return ( + + + {String(labelValue)} {/* Force conversion to string */} + + + ); +}; const GenericActionButtons = ({ card, // selectedEnt, @@ -31,15 +91,17 @@ const GenericActionButtons = ({ cardSize = 'md', datatable = false, }) => { + const { theme } = useMode(); const { enqueueSnackbar } = useSnackbar(); // Add this line to use Notistack const memoizedReturnValues = useCollectionManager(); // Add this line to use useCollectionManager const memoizedSelectionVals1 = useSelectedCollection(); // Add this line to use useCollectionManager const memoizedSelectionVals2 = useSelectedDeck(); // Add this line to use useCollectionManager const memoizedSelectionVals3 = useCartManager(); // Add this line to use useCollectionManager + const memoizedReturnValues4 = useDeckManager(); if (!memoizedReturnValues) return ; // Add this line to use useCollectionManager const { addOneToCollection, removeOneFromCollection } = memoizedReturnValues; // Modify this line to use useCollectionManager - const { addOneToDeck, removeOneFromDeck } = useDeckManager(); - const { addOneToCart, removeOneFromCart } = useCartManager(); + const { addOneToDeck, removeOneFromDeck } = memoizedReturnValues4; + const { addOneToCart, removeOneFromCart } = memoizedSelectionVals3; const [buttonSize, setButtonSize] = useState( buttonSizeMap[cardSize] || 'medium' ); @@ -54,9 +116,9 @@ const GenericActionButtons = ({ setSelectedEntity(memoizedSelectionVals3.cart); } }, [ - memoizedSelectionVals1.selectedCollection, + memoizedSelectionVals1.selectedCollectionId, memoizedSelectionVals2.selectedDeckId, - memoizedSelectionVals2.allDecks, + // memoizedSelectionVals2.allDecks, memoizedSelectionVals3.cart, context, ]); @@ -107,7 +169,7 @@ const GenericActionButtons = ({ height: '100%', }} > - {datatable !== 'data-table' && ( + {datatable === 'data-table' && ( { return ( - - {sideText} - + {sideText !== 'none' && ( + + {sideText} + + )} ); }; diff --git a/src/layout/REUSABLE_COMPONENTS/DashBoardLayout.jsx b/src/layout/REUSABLE_COMPONENTS/layout-utils/DashBoardLayout.jsx similarity index 91% rename from src/layout/REUSABLE_COMPONENTS/DashBoardLayout.jsx rename to src/layout/REUSABLE_COMPONENTS/layout-utils/DashBoardLayout.jsx index ecfc110..51ea089 100644 --- a/src/layout/REUSABLE_COMPONENTS/DashBoardLayout.jsx +++ b/src/layout/REUSABLE_COMPONENTS/layout-utils/DashBoardLayout.jsx @@ -1,7 +1,7 @@ -import { useMode, useSidebarContext } from '../../context'; +import { useMode, useSidebarContext } from '../../../context'; import { useLocation } from 'react-router-dom'; import PropTypes from 'prop-types'; -import MDBox from './MDBOX/index'; +import MDBox from '../MDBOX/index'; function DashboardLayout({ children }) { const { pathname } = useLocation(); diff --git a/src/layout/REUSABLE_COMPONENTS/DashboardBox.jsx b/src/layout/REUSABLE_COMPONENTS/layout-utils/DashboardBox.jsx similarity index 100% rename from src/layout/REUSABLE_COMPONENTS/DashboardBox.jsx rename to src/layout/REUSABLE_COMPONENTS/layout-utils/DashboardBox.jsx diff --git a/src/layout/REUSABLE_COMPONENTS/FlexBetween.jsx b/src/layout/REUSABLE_COMPONENTS/layout-utils/FlexBetween.jsx similarity index 100% rename from src/layout/REUSABLE_COMPONENTS/FlexBetween.jsx rename to src/layout/REUSABLE_COMPONENTS/layout-utils/FlexBetween.jsx diff --git a/src/layout/REUSABLE_COMPONENTS/NivoContainer.jsx b/src/layout/REUSABLE_COMPONENTS/layout-utils/NivoContainer.jsx similarity index 100% rename from src/layout/REUSABLE_COMPONENTS/NivoContainer.jsx rename to src/layout/REUSABLE_COMPONENTS/layout-utils/NivoContainer.jsx diff --git a/src/layout/REUSABLE_COMPONENTS/PageHeader.jsx b/src/layout/REUSABLE_COMPONENTS/layout-utils/PageHeader.jsx similarity index 79% rename from src/layout/REUSABLE_COMPONENTS/PageHeader.jsx rename to src/layout/REUSABLE_COMPONENTS/layout-utils/PageHeader.jsx index 4e17ec0..5ee06c6 100644 --- a/src/layout/REUSABLE_COMPONENTS/PageHeader.jsx +++ b/src/layout/REUSABLE_COMPONENTS/layout-utils/PageHeader.jsx @@ -1,14 +1,14 @@ import React from 'react'; import styled from 'styled-components'; import { Box } from '@mui/material'; -import { useMode } from '../../context'; -import SimpleCard from './unique/SimpleCard'; -import SimpleSectionHeader from './unique/SimpleSectionHeader'; -import uniqueTheme from './unique/uniqueTheme'; -import { PageHeaderSkeleton } from './system-utils/SkeletonVariants'; -import RCButton from './RCBUTTON'; -import useUserData from '../../context/MAIN_CONTEXT/UserContext/useUserData'; -import { useFormManagement } from '../../components/forms/hooks/useFormManagement'; +import { useMode } from '../../../context'; +import SimpleCard from '../unique/SimpleCard'; +import SimpleSectionHeader from '../unique/SimpleSectionHeader'; +import uniqueTheme from '../unique/uniqueTheme'; +import { PageHeaderSkeleton } from '../system-utils/SkeletonVariants'; +import RCButton from '../RCBUTTON'; +import useUserData from '../../../context/MAIN_CONTEXT/UserContext/useUserData'; +import { useFormManagement } from '../../../components/forms/hooks/useFormManagement'; const FlexContainer = styled(Box)(({ theme }) => ({ display: 'flex', diff --git a/src/layout/REUSABLE_COMPONENTS/PageLayout.jsx b/src/layout/REUSABLE_COMPONENTS/layout-utils/PageLayout.jsx similarity index 93% rename from src/layout/REUSABLE_COMPONENTS/PageLayout.jsx rename to src/layout/REUSABLE_COMPONENTS/layout-utils/PageLayout.jsx index fd80c50..fc9e507 100644 --- a/src/layout/REUSABLE_COMPONENTS/PageLayout.jsx +++ b/src/layout/REUSABLE_COMPONENTS/layout-utils/PageLayout.jsx @@ -1,8 +1,8 @@ import { useLocation } from 'react-router-dom'; import PropTypes from 'prop-types'; -import MDBox from './MDBOX'; +import MDBox from '../MDBOX'; import { Grid } from '@mui/material'; -import { useMode } from '../../context'; +import { useMode } from '../../../context'; function PageLayout({ background, backCol, children }) { const { pathname } = useLocation(); diff --git a/src/layout/REUSABLE_COMPONENTS/StatBox.jsx b/src/layout/REUSABLE_COMPONENTS/layout-utils/StatBox.jsx similarity index 94% rename from src/layout/REUSABLE_COMPONENTS/StatBox.jsx rename to src/layout/REUSABLE_COMPONENTS/layout-utils/StatBox.jsx index 767b63e..2130d97 100644 --- a/src/layout/REUSABLE_COMPONENTS/StatBox.jsx +++ b/src/layout/REUSABLE_COMPONENTS/layout-utils/StatBox.jsx @@ -1,7 +1,7 @@ import { Box, Card, CardContent, Typography, useTheme } from '@mui/material'; -import ProgressCircle from './ProgressCircle'; -import { useMode } from '../../context'; -import MDBox from './MDBOX'; +import ProgressCircle from '../system-utils/ProgressCircle'; +import { useMode } from '../../../context'; +import MDBox from '../MDBOX'; const StatBox = ({ title, subtitle, icon, progress, increase, wrapIcon }) => { const { theme } = useMode(); diff --git a/src/layout/REUSABLE_COMPONENTS/ProgressCircle.jsx b/src/layout/REUSABLE_COMPONENTS/system-utils/ProgressCircle.jsx similarity index 94% rename from src/layout/REUSABLE_COMPONENTS/ProgressCircle.jsx rename to src/layout/REUSABLE_COMPONENTS/system-utils/ProgressCircle.jsx index b5efa6b..2f40d4d 100644 --- a/src/layout/REUSABLE_COMPONENTS/ProgressCircle.jsx +++ b/src/layout/REUSABLE_COMPONENTS/system-utils/ProgressCircle.jsx @@ -2,9 +2,9 @@ import React from 'react'; import { Box, useTheme } from '@mui/material'; import PropTypes from 'prop-types'; -import RCToolTip from './RCTOOLTIP/RCToolTip'; -import { useMode } from '../../context'; -import useSkeletonLoader from './system-utils/useSkeletonLoader'; +import RCToolTip from '../RCTOOLTIP/RCToolTip'; +import { useMode } from '../../../context'; +import useSkeletonLoader from './useSkeletonLoader'; const ProgressCircleSkeleton = ({ size = 120 }) => { const theme = useTheme(); diff --git a/src/layout/REUSABLE_COMPONENTS/system-utils/SkeletonVariants.jsx b/src/layout/REUSABLE_COMPONENTS/system-utils/SkeletonVariants.jsx index 906d300..f13ff22 100644 --- a/src/layout/REUSABLE_COMPONENTS/system-utils/SkeletonVariants.jsx +++ b/src/layout/REUSABLE_COMPONENTS/system-utils/SkeletonVariants.jsx @@ -93,7 +93,8 @@ const HeroSectionSkeleton = () => { background: 'rgba(189, 181, 181, 0.1)', boxShadow: '0 4px 30px rgba(0, 0, 0, 0.1)', backdropFilter: 'blur(20px)', - '-webkit-backdrop-filter': 'blur(20px)', + + // '-webkit-backdrop-filter': 'blur(20px)', border: '1px solid rgba(255, 255, 255, 0.3)', borderRadius: '25px', padding: '30px', diff --git a/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx b/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx index 7c79547..bd49570 100644 --- a/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx +++ b/src/layout/REUSABLE_COMPONENTS/unique/SimpleCard.jsx @@ -9,7 +9,7 @@ import MDBox from '../MDBOX'; import SaveIcon from '@mui/icons-material/Save'; import AddIcon from '@mui/icons-material/Add'; import CollectionsIcon from '@mui/icons-material/Collections'; -import FlexBetween from '../FlexBetween'; +import FlexBetween from '../layout-utils/FlexBetween'; const getPrimaryStyle = (theme, isPrimary) => ({ background: isPrimary ? theme.colorPrimary : undefined, diff --git a/src/layout/collection/ChartConfigs.jsx b/src/layout/collection/ChartConfigs.jsx deleted file mode 100644 index 2c9485a..0000000 --- a/src/layout/collection/ChartConfigs.jsx +++ /dev/null @@ -1,195 +0,0 @@ -/* eslint-disable max-len */ -import { useAppContext, useMode } from '../../context'; -import { useEffect, useMemo, useState } from 'react'; -import NivoContainer from '../REUSABLE_COMPONENTS/NivoContainer'; -import { BasicTooltip } from '@nivo/tooltip'; -import { useEventHandlers } from '../../context/hooks/useEventHandlers'; -import MyResponsiveLine from './MyPortfolioLineChart'; -import { ChartArea } from '../../pages/pageStyles/StyledComponents'; -import { useForm, useWatch } from 'react-hook-form'; -import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -import useRCFormHook from '../../components/forms/hooks/useRCFormHook'; -import { useCompileCardData } from '../../context/MISC_CONTEXT/AppContext/useCompileCardData'; -import LoadingOverlay from '../REUSABLE_COMPONENTS/system-utils/LoadingOverlay'; -import { i } from 'mathjs'; -const formatDateBasedOnRange = (range) => { - const formatMap = { - '24hr': { format: '%H:%M', ticks: 'every hour' }, - '7d': { format: '%b %d', ticks: 'every day' }, - '30d': { format: '%b %d', ticks: 'every day' }, - '90d': { format: '%b %d', ticks: 'every 3 days' }, - '180d': { format: '%b %d', ticks: 'every 6 days' }, - '270d': { format: '%b %d', ticks: 'every 9 days' }, - '365d': { format: '%b %d', ticks: 'every 12 days' }, - default: { format: '%b %d', ticks: 'every day' }, - }; - - return formatMap[range] || formatMap.default; -}; -export const ChartConfiguration = () => { - const { theme } = useMode(); - const { greenAccent, redAccent, grey } = theme.palette.chartTheme; - const { selectedTimeRange = '24hr' } = useCompileCardData(); - const { selectedCollection, createMarkers } = useSelectedCollection(); - - // Early return if collection is not selected - if (!selectedCollection) { - return ; - } - - // Calculate memoized values for chart data and markers - const memoChartData = useMemo(() => { - const { selectedChartData, averagedChartData, selectedChartDataKey } = - selectedCollection; - // if selectedChartData is an empty object, return the averagedChartData, else return selectedChartData - return !selectedChartData.data.length - ? averagedChartData[selectedChartDataKey] - : selectedChartData; - }, [selectedCollection]); - - const validMarkers = useMemo(() => { - const markers = createMarkers(selectedCollection); - return markers.filter((marker) => marker.id && marker.value !== undefined); - }, [createMarkers, selectedCollection]); - - const tickValues = useMemo(() => { - const { ticks } = formatDateBasedOnRange(selectedTimeRange); - return ticks.split(' ').map((tick) => new Date(tick).getTime()); - }, [selectedTimeRange]); - - useEffect(() => { - console.log('Chart data:', memoChartData); - }, [memoChartData]); - - if (!memoChartData) { - return ; - } - - return ( - - - - - - ); -}; -// const TooltipLayer = ({ points }) => ( -// <> -// {points?.map((point) => ( -// -// ))} -// -// ); -// import PropTypes from 'prop-types'; - -// export const ChartConfiguration = () => { -// const { theme } = useMode(); -// const { greenAccent, redAccent, grey } = theme.palette.chartTheme; -// const { selectedTimeRange = '24hr' } = useCompileCardData(); -// // , chartData } = useCompileCardData(); // Default to '24hr' if not set -// const { selectedCollection, updateCollectionField, createMarkers } = -// useSelectedCollection(); -// // const { handleMouseMove, handleMouseLeave } = useEventHandlers(); -// if (!selectedCollection) { -// return ; -// } -// if (selectedCollection?.selectedChartData?.data?.length === 0) { -// return ; -// } -// const memoTimeRange = useMemo( -// () => -// !selectedCollection?.selectedChartData || -// !selectedCollection?.selectedChartData?.data?.length -// ? selectedTimeRange -// : selectedCollection.selectedChartDataKey, -// [selectedCollection?.selectedChartData, selectedTimeRange] -// ); -// const memoChartData = useMemo( -// () => -// !selectedCollection?.selectedChartData -// ? selectedCollection?.averagedChartData[memoTimeRange] -// : selectedCollection?.selectedChartData, -// [ -// selectedCollection?.selectedChartDataKey, -// selectedCollection?.averagedChartData, -// memoTimeRange, -// ] -// ); -// const validMarkers = useMemo(() => { -// const markers = createMarkers(selectedCollection); -// return markers?.filter((marker) => marker.value !== undefined), markers; -// }, [createMarkers, selectedCollection]); -// const tickValues = useMemo(() => { -// const { format, ticks } = formatDateBasedOnRange(selectedTimeRange); -// return ticks?.split(' ')?.map((tick) => new Date(tick)?.getTime()); -// }, [selectedTimeRange]); -// const xformat = useMemo(() => { -// const format = memoChartData?.id === '24hr' ? '%H:%M' : '%b %d'; -// return format; -// }, [memoChartData?.id]); -// useEffect(() => { -// console.log('NIVO SELECTED DATA ', memoChartData); -// console.log('NIVO SELECTED TIME RANGE ', selectedTimeRange); -// }, [memoChartData]); -// if (!memoChartData) { -// return ; -// } -// return ( -// -// -// -// -// -// ); -// }; - -// ChartConfiguration.propTypes = { -// markers: PropTypes.arrayOf( -// PropTypes.shape({ -// axis: PropTypes.oneOf(['x', 'y']).isRequired, -// value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) -// .isRequired, -// legend: PropTypes.string, -// lineStyle: PropTypes.object, -// }) -// ), -// height: PropTypes.number.isRequired, -// nivoChartData: PropTypes.arrayOf(PropTypes.object).isRequired, -// range: PropTypes.oneOf(['24hr', '7d', '30d', '90d', '180d', '270d', '365d']) -// .isRequired, -// loadingID: PropTypes.string.isRequired, -// }; diff --git a/src/layout/collection/ChartGridLayout.jsx b/src/layout/collection/ChartGridLayout.jsx index 5442a88..471ff5d 100644 --- a/src/layout/collection/ChartGridLayout.jsx +++ b/src/layout/collection/ChartGridLayout.jsx @@ -1,23 +1,29 @@ import React, { Suspense, useEffect, useMemo, useState } from 'react'; import { Grid, useMediaQuery, Icon, Box } from '@mui/material'; import MDBox from '../REUSABLE_COMPONENTS/MDBOX'; -import { useAppContext, useMode } from '../../context'; -import DashboardBox from '../REUSABLE_COMPONENTS/DashboardBox'; -import BoxHeader from '../REUSABLE_COMPONENTS/BoxHeader'; +import { useMode } from '../../context'; +import DashboardBox from '../REUSABLE_COMPONENTS/layout-utils/DashboardBox'; +import BoxHeader from '../REUSABLE_COMPONENTS/layout-utils/BoxHeader'; import SimpleCard from '../REUSABLE_COMPONENTS/unique/SimpleCard'; import uniqueTheme from '../REUSABLE_COMPONENTS/unique/uniqueTheme'; import { ChartArea } from '../../pages/pageStyles/StyledComponents'; import useSelectedCollection from '../../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; -import { ChartConfiguration } from './ChartConfigs'; import LoadingOverlay from '../REUSABLE_COMPONENTS/system-utils/LoadingOverlay'; import RCWrappedIcon from '../REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIcon'; import { ResponsiveContainer } from 'recharts'; import preparePortfolioTableData from './data/portfolioData'; -import { calculateChangePercentage } from '../../context/Helpers'; +import { + calculateChangePercentage, + formatDateBasedOnRange, +} from '../../context/Helpers'; import { TopCardsDisplayRow } from './TopCardsDisplayRow'; import { formFields } from '../../components/forms/formsConfig'; import RCDynamicForm from '../../components/forms/Factory/RCDynamicForm'; import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useCompileCardData } from '../../context/MISC_CONTEXT/AppContext/useCompileCardData'; +import NivoContainer from '../REUSABLE_COMPONENTS/layout-utils/NivoContainer'; +import MyResponsiveLine from './MyPortfolioLineChart'; +import { CircularProgress } from '@mui/joy'; const renderCardContainer = (content) => { return ( @@ -81,7 +87,20 @@ const ChartGridLayout = () => { /> {/* CHART ROW SECTION */} - + {selectedCollection?.cards?.length < 5 ? ( + + + + ) : ( + + )} {/* FORM SELECTOR ROW SECTION */} {/* TOP CARDS ROW SECTION */} @@ -100,6 +119,7 @@ const ChartGridLayout = () => { }; export default ChartGridLayout; +// !--------------------- FORM SELECTOR ROW COMPONENTS --------------------- const FormSelectorRow = React.memo(({ isXs }) => { const formKeys = ['statRangeForm', 'timeRangeForm', 'themeRangeForm']; return ( @@ -138,17 +158,60 @@ const FormSelectorRow = React.memo(({ isXs }) => { ); }); FormSelectorRow.displayName = 'FormSelectorRow'; -const ChartAreaComponent = React.memo(({ theme }) => { - const { selectedCollection, markers } = useSelectedCollection(); +// !--------------------- CHART COMPONENT --------------------- +const ChartAreaComponent = React.memo(() => { + const { theme } = useMode(); + const { greenAccent, redAccent, grey } = theme.palette.chartTheme; + const { selectedTimeRange = '24hr' } = useCompileCardData(); + const { selectedCollection, createMarkers } = useSelectedCollection(); + if (!selectedCollection) { + return ; + } + const memoChartData = useMemo(() => { + const { selectedChartData, averagedChartData, selectedChartDataKey } = + selectedCollection; + return !selectedChartData.data.length + ? averagedChartData[selectedChartDataKey] + : selectedChartData; + }, [selectedCollection]); + if (!memoChartData) { + return ; + } + const validMarkers = useMemo(() => { + const markers = createMarkers(selectedCollection); + return markers.filter((marker) => marker.id && marker.value !== undefined); + }, [createMarkers, selectedCollection]); + const tickValues = useMemo(() => { + const { ticks } = formatDateBasedOnRange(selectedTimeRange); + return ticks.split(' ').map((tick) => new Date(tick).getTime()); + }, [selectedTimeRange]); + useEffect(() => { + console.log('Chart data:', memoChartData); + }, [memoChartData]); return renderCardContainer( }> - + + + + + ); }); ChartAreaComponent.displayName = 'ChartAreaComponent'; +//!--------------------- CARD DISPLAY COMPONENT --------------------- const TopCardsDisplayRowComponent = React.memo(({ isXs }) => { return ( @@ -163,7 +226,12 @@ const TopCardsDisplayRowComponent = React.memo(({ isXs }) => { sm={12} md={12} lg={12} - sx={{ width: '100%', flexGrow: 1 }} + sx={{ + width: '100%', + flexGrow: 1, + display: 'flex', + justifyContent: 'center', + }} > @@ -172,6 +240,7 @@ const TopCardsDisplayRowComponent = React.memo(({ isXs }) => { ); }); TopCardsDisplayRowComponent.displayName = 'TopCardsDisplayRowComponent'; +//!--------------------- CARD LIST COMPONENT --------------------- const CollectionCardList = React.memo( ({ data, columns, theme, selectedCollection }) => { const formattedTime = selectedCollection?.updatedAt @@ -278,15 +347,4 @@ const CollectionCardList = React.memo( } ); CollectionCardList.displayName = 'CollectionCardList'; -{ - /* */ -} +// !--------------------- END OF COMPONENTS --------------------- diff --git a/src/layout/collection/CollectionListItem.jsx b/src/layout/collection/CollectionListItem.jsx index f44d4c7..3f5d3cb 100644 --- a/src/layout/collection/CollectionListItem.jsx +++ b/src/layout/collection/CollectionListItem.jsx @@ -28,7 +28,8 @@ const CollectionListItem = memo( return ; } const percentageChange = - collection?.collectionStatistics?.percentageChange || 0; + roundToNearestTenth(collection?.collectionStatistics?.percentageChange) || + 0; const handleEdit = useCallback( (event) => { @@ -54,7 +55,7 @@ const CollectionListItem = memo( diff --git a/src/layout/collection/CollectionPortfolioHeader.jsx b/src/layout/collection/CollectionPortfolioHeader.jsx index 08c7c04..fcbad66 100644 --- a/src/layout/collection/CollectionPortfolioHeader.jsx +++ b/src/layout/collection/CollectionPortfolioHeader.jsx @@ -10,7 +10,7 @@ import { useMode } from '../../context'; import { DEFAULT_COLLECTION } from '../../context/constants'; import uniqueTheme from '../REUSABLE_COMPONENTS/unique/uniqueTheme'; import IconStatWrapper from '../REUSABLE_COMPONENTS/unique/IconStatWrapper'; -import DashboardBox from '../REUSABLE_COMPONENTS/DashboardBox'; +import DashboardBox from '../REUSABLE_COMPONENTS/layout-utils/DashboardBox'; import { PageHeaderSkeleton } from '../REUSABLE_COMPONENTS/system-utils/SkeletonVariants'; import { collectionPortfolioHeaderItems } from '../../data/collectionPortfolioHeaderItems'; diff --git a/src/layout/collection/SelectCollectionList.jsx b/src/layout/collection/SelectCollectionList.jsx index a3c869c..7c17e60 100644 --- a/src/layout/collection/SelectCollectionList.jsx +++ b/src/layout/collection/SelectCollectionList.jsx @@ -20,18 +20,53 @@ const SelectCollectionList = ({ handleSelectAndShowCollection }) => { if (!collectionData || !selectionData) { return ; } - const { allCollections, allIds, refreshCollections } = selectionData; - // const [collectionListData, setCollectionListData] = useState( - // allCollections?.map((collection, index) => ({ - // key: `${collection._id}-collection-${index}`, - // collection: collection, - // handleSelectAndShowCollection: handleSelectAndShowCollection, - // handleDelete: () => handleDelete(collection._id), - // })) - // ); + const { hasFetchedCollections, fetchCollections } = collectionData; + const { + allCollections, + allIds, + refreshCollections, + refreshCollections2, + collections, + } = selectionData; const [collectionList, setCollectionList] = useState(allCollections); + const [safeCollectionList, setSafeCollectionList] = useState([]); + const [error, setError] = useState(null); + const collectionsRef = useRef([]); useEffect(() => { setCollectionList(allCollections); + setSafeCollectionList(allCollections); + }, []); + useEffect(() => { + if (!hasFetchedCollections) { + fetchCollections(); + collectionsRef.current = allCollections; + setSafeCollectionList(allCollections); + console.log('DECKS REF', collectionsRef.current); + // fetchDecks(); + } + }, [hasFetchedCollections]); + useEffect(() => { + const handleStorageChange = () => { + const newData = JSON.parse(localStorage.getItem('collections') || '{}'); + if ( + newData !== collections && + newData.lastUpdated !== collections.lastUpdated + ) { + // filter out any collections with _id value containing empty strings + const newAllCollections = Object.values(newData?.byId).filter( + (collection) => collection?._id !== '' + ); + if (!newAllCollections.length) { + setError('No decks found in storage.'); + return; + } + collectionsRef.current = newAllCollections; + setSafeCollectionList(newAllCollections); + refreshCollections2(); + } + }; + window.addEventListener('storage', handleStorageChange); + return () => window.removeEventListener('storage', handleStorageChange); }, []); const { deleteCollection } = collectionData; const handleDelete = useCallback( @@ -80,35 +115,36 @@ const SelectCollectionList = ({ handleSelectAndShowCollection }) => { } return ( - <> - + - - - {collectionList?.map((item, index) => + + {safeCollectionList?.map((item, index) => + renderCollectionItem(item, index) + )} + {/* {collectionList?.map((item, index) => renderCollectionItem(item, index) - )} - - - - + )} */} + + + ); }; diff --git a/src/layout/collection/StatBoard.jsx b/src/layout/collection/StatBoard.jsx index 4f301ca..1891a1c 100644 --- a/src/layout/collection/StatBoard.jsx +++ b/src/layout/collection/StatBoard.jsx @@ -1,16 +1,15 @@ /* eslint-disable react/jsx-key */ -import { Box, Grid, Typography, Skeleton, useMediaQuery } from '@mui/material'; +import { Grid } from '@mui/material'; import TotalPriceStatBox from './statItems/TotalPriceStatBox'; import SimpleCard from '../REUSABLE_COMPONENTS/unique/SimpleCard'; import uniqueTheme from '../REUSABLE_COMPONENTS/unique/uniqueTheme'; -import { useAppContext, useMode } from '../../context'; +import { useMode } from '../../context'; import ValuDistributionCircle from './statItems/ValuDistributionCircle'; import PricedCardList from './statItems/PricedCardList'; import MDBox from '../REUSABLE_COMPONENTS/MDBOX'; import TotalCardsCollectedStatBox from './statItems/TotalCardsCollectedStatBox'; -import FlexBetween from '../REUSABLE_COMPONENTS/FlexBetween'; +import FlexBetween from '../REUSABLE_COMPONENTS/layout-utils/FlexBetween'; import { Divider } from '@mui/joy'; -import { SkeletonPieChart } from '../REUSABLE_COMPONENTS/system-utils/SkeletonVariants'; import useBreakpoint from '../../context/hooks/useBreakPoint'; const StatBoxes = () => { @@ -63,7 +62,7 @@ const StatBoard = () => { { 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); @@ -56,8 +58,10 @@ export const TopCardsDisplayRow = () => { display: 'flex', flexDirection: 'column', alignItems: 'flex-start', + justifyContent: 'center', width: '100%', transition: 'transform 0.9s', + flexGrow: 1, }} > { flexDirection: isMobileView ? 'column' : 'row', // Adjust direction based on screen size width: '100%', height: '100%', + alignItems: 'flex-start', + justifyContent: 'center', }} > { flexGrow: 1, width: isMobileView ? '100%' : 'auto', // Adjust width based on screen size minHeight: '100%', // Ensure card takes full height - p: theme.spacing(2), - m: theme.spacing(1), + alignItems: 'flex-start', + flexDirection: isMobileView ? 'column' : 'row', // Adjust direction based on screen size + justifyContent: 'center', + // p: theme.spacing(2), + // m: theme.spacing(1), }} > - + + + + check_circle + + + } + /> + {/* + {card.name} + */} + {/* + + check_circle + + + } + /> */} { style={{ borderRadius: 0, width: 'auto', + // height: 'auto', + mx: 'auto', }} /> @@ -103,6 +180,7 @@ export const TopCardsDisplayRow = () => { justifyContent: 'center', alignItems: 'center', width: 'auto', + my: 'auto', }} > ( { const { theme } = useMode(); @@ -97,6 +99,17 @@ const CollectionPortfolio = () => { allCollections, handleSelectCollection, } = useSelectedCollection(); + const collectionData = useCollectionManager(); + if (!collectionData) { + return ; + } + const { fetchCollections, hasFetchedCollections } = collectionData; + // useEffect(() => { + // if (!hasFetchedCollections) { + // fetchCollections(); + // } + // }, []); + const { chartData, setChartData } = useCompileCardData(); const { dialogState, openDialog, closeDialog } = useDialogState(); const [activeTab, setActiveTab] = useState(0); diff --git a/src/layout/collection/statItems/PricedCardList.jsx b/src/layout/collection/statItems/PricedCardList.jsx index b455d23..e724505 100644 --- a/src/layout/collection/statItems/PricedCardList.jsx +++ b/src/layout/collection/statItems/PricedCardList.jsx @@ -1,5 +1,5 @@ import MDBox from '../../REUSABLE_COMPONENTS/MDBOX'; -import BoxHeader from '../../REUSABLE_COMPONENTS/BoxHeader'; +import BoxHeader from '../../REUSABLE_COMPONENTS/layout-utils/BoxHeader'; import { Box, Card, CardContent } from '@mui/material'; import { DataGrid } from '@mui/x-data-grid'; import prepareTableData from '../data/topCards'; diff --git a/src/layout/collection/statItems/TotalCardsCollectedStatBox.jsx b/src/layout/collection/statItems/TotalCardsCollectedStatBox.jsx index be2064d..7f0d8e0 100644 --- a/src/layout/collection/statItems/TotalCardsCollectedStatBox.jsx +++ b/src/layout/collection/statItems/TotalCardsCollectedStatBox.jsx @@ -1,5 +1,5 @@ import { Box } from '@mui/material'; -import StatBox from '../../REUSABLE_COMPONENTS/StatBox'; +import StatBox from '../../REUSABLE_COMPONENTS/layout-utils/StatBox'; import { useMode, useAppContext } from '../../../context'; import FormatListNumberedRoundedIcon from '@mui/icons-material/FormatListNumberedRounded'; const TotalCardsCollectedStatBox = () => { diff --git a/src/layout/collection/statItems/TotalPriceStatBox.jsx b/src/layout/collection/statItems/TotalPriceStatBox.jsx index 9a61d36..3fd4cdd 100644 --- a/src/layout/collection/statItems/TotalPriceStatBox.jsx +++ b/src/layout/collection/statItems/TotalPriceStatBox.jsx @@ -1,5 +1,5 @@ import { Box } from '@mui/material'; -import StatBox from '../../REUSABLE_COMPONENTS/StatBox'; +import StatBox from '../../REUSABLE_COMPONENTS/layout-utils/StatBox'; import { useMode, useAppContext } from '../../../context'; import MonetizationOnIcon from '@mui/icons-material/MonetizationOn'; import { roundToNearestTenth } from '../../../context/Helpers'; diff --git a/src/layout/collection/statItems/ValuDistributionCircle.jsx b/src/layout/collection/statItems/ValuDistributionCircle.jsx index 9dd472c..89bfa3e 100644 --- a/src/layout/collection/statItems/ValuDistributionCircle.jsx +++ b/src/layout/collection/statItems/ValuDistributionCircle.jsx @@ -10,7 +10,7 @@ import { ResponsiveContainer, Legend, } from 'recharts'; -import BoxHeader from '../../REUSABLE_COMPONENTS/BoxHeader'; +import BoxHeader from '../../REUSABLE_COMPONENTS/layout-utils/BoxHeader'; import { useCompileCardData } from '../../../context/MISC_CONTEXT/AppContext/useCompileCardData'; import RCWrappedIcon from '../../REUSABLE_COMPONENTS/RCWRAPPEDICON/RCWrappedIcon'; import MDBox from '../../REUSABLE_COMPONENTS/MDBOX'; @@ -72,6 +72,7 @@ const ValuDistributionCircle = () => { // p: 2, maxHeight: 270, border: 'none', + borderRadius: theme.shape.borderRadius, }} > { paddingVariant={theme.spacing(2)} sx={{ color: colors.greenAccent.default, - borderRadius: theme.shape.borderRadius, + borderRadius: theme.shape.borderRadiusLarge, }} /> diff --git a/src/layout/deck/DeckListItem.jsx b/src/layout/deck/DeckListItem.jsx index 421fdec..9ec9275 100644 --- a/src/layout/deck/DeckListItem.jsx +++ b/src/layout/deck/DeckListItem.jsx @@ -64,6 +64,8 @@ const DeckListItem = ({ isEditPanelOpen, handleDelete, handleDeckLoaded, + setLoadingState, + loadingState, }) => { const { genData, infoItems } = prepareDeckData(deck); const { theme } = useMode(); @@ -172,8 +174,6 @@ const DeckListItem = ({ }} /> - {/* - */} diff --git a/src/layout/deck/index.jsx b/src/layout/deck/index.jsx index de10400..627720b 100644 --- a/src/layout/deck/index.jsx +++ b/src/layout/deck/index.jsx @@ -9,14 +9,14 @@ import { } from '@mui/material'; import { useMode } from '../../context'; import MDBox from '../REUSABLE_COMPONENTS/MDBOX'; -import DashboardLayout from '../REUSABLE_COMPONENTS/DashBoardLayout'; +import DashboardLayout from '../REUSABLE_COMPONENTS/layout-utils/DashBoardLayout'; import SearchComponent from '../../components/forms/search/SearchComponent'; import DeckDialog from '../../components/dialogs/DeckDialog'; 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 PageHeader from '../REUSABLE_COMPONENTS/PageHeader'; +import DashboardBox from '../REUSABLE_COMPONENTS/layout-utils/DashboardBox'; +import PageHeader from '../REUSABLE_COMPONENTS/layout-utils/PageHeader'; import useUserData from '../../context/MAIN_CONTEXT/UserContext/useUserData'; import { useFormManagement } from '../../components/forms/hooks/useFormManagement'; import useDeckManager from '../../context/MAIN_CONTEXT/DeckContext/useDeckManager'; @@ -38,91 +38,29 @@ const DeckBuilder = () => { handleSelectDeck, refreshDecks, } = useSelectedDeck(); - const { isLoading } = useLoading(); - // const { allDecks } = prepareDeckData(selectedDeckId); const { setActiveFormSchema } = useFormManagement(); const { dialogState, openDialog, closeDialog } = useDialogState(); - const handleOpenAddDialog = useCallback(() => { - setActiveFormSchema('addDeckForm'); - openDialog('isAddDeckDialogOpen'); - }, [openDialog, setActiveFormSchema]); const [activeTab, setActiveTab] = useState(0); - const [allDecksLoading, setAllDecksLoading] = useState(false); const [loadingState, setLoadingState] = useState({}); const [safeDeckList, setSafeDeckList] = useState([]); - const prevSelectedDeckId = useRef(null); // Store the previous deck ID - const handleDeckLoaded = useCallback((deckId) => { - setLoadingState((prev) => ({ ...prev, [deckId]: false })); - }, []); + const [error, setError] = useState(null); + const decksRef = useRef([]); useEffect(() => { if (!hasFetchedDecks) { + // decksRef.current = allDecks; fetchDecks(); - setActiveTab(0); + setSafeDeckList(allDecks); + console.log('DECKS REF', decksRef.current); + // fetchDecks(); } - }, []); - useEffect(() => { - const handleStorageChange = () => { - const newData = JSON.parse(localStorage.getItem('decks') || '{}'); - if (newData !== decks && newData.lastUpdated !== decks.lastUpdated) { - const newAllDecks = Object.values(newData.byId); - setSafeDeckList(newAllDecks); - refreshDecks(); - } - }; - window.addEventListener('storage', handleStorageChange); - return () => window.removeEventListener('storage', handleStorageChange); - }, []); - useEffect(() => { - setActiveTab((prevActiveTab) => { - const newIndex = Math.min(prevActiveTab, safeDeckList.length - 1); - return newIndex >= 0 ? newIndex : 0; // Ensure that the index is always non-negative - }); - }, [safeDeckList.length]); // Update activeTab when the length of safeDeckList changes - useEffect(() => { - if (prevSelectedDeckId.current !== selectedDeckId) { - prevSelectedDeckId.current = selectedDeckId; - } - }, [selectedDeckId]); - // useEffect(() => { - // if (isLoading('fetchDecks')) { - // setAllDecksLoading(true); - // } - // if (!isLoading('fetchDecks')) { - // setAllDecksLoading(false); - // } - // }, [isLoading('fetchDecks')]); - const handleChangeTab = useCallback( - (event, newValue) => { - setActiveTab(newValue); - const newDeck = safeDeckList[newValue]; - handleSelectDeck(newDeck); - - if (!loadingState[newDeck._id]) { - setLoadingState((prev) => ({ ...prev, [newDeck._id]: true })); // Set loading for the new tab - } - }, - [safeDeckList, handleSelectDeck, loadingState] - ); - - // const handleChangeTab = useCallback( - // (event, newValue) => { - // setActiveTab(newValue); - // handleSelectDeck(safeDeckList[newValue]); - // // setLoadingState((prev) => ({ - // // ...prev, - // // [safeDeckList[newValue]._id]: true, - // // })); - // // setLoadingState((prev) => ({ ...prev, [newValue]: true })); // Set loading to true for the new tab - // }, - // [safeDeckList, handleSelectDeck] - // ); - + }, [hasFetchedDecks]); const handleDelete = useCallback( async (deck) => { if (!deck) return; try { - await deleteDeck(deck._id); + deleteDeck(deck?._id); const updatedDecks = safeDeckList.filter((col) => col._id !== deck._id); + setSafeDeckList((prev) => updatedDecks); console.log('UPDATED DECKS', updatedDecks); // refreshDecks(updatedCollections); // Assuming refreshCollections now directly accepts an updated array } catch (error) { @@ -131,57 +69,144 @@ const DeckBuilder = () => { }, [deleteDeck] ); + const handleDeckLoaded = useCallback((deckId) => { + setLoadingState((prev) => ({ + ...prev, + [deckId]: false, + })); // Set loading state to false when deck is fully loaded + }, []); const deckTabs = safeDeckList?.map((deck, index) => ( : deck.name + loadingState[deck?._id] ? : deck.name } value={index} - key={deck._id || `deck-tab-${index}`} + key={`deck-tab-${deck?._id}-${index}`} /> )); const deckList = safeDeckList?.map((deck, index) => ( - + handleDelete(deck)} - isEditPanelOpen={selectedDeckId === deck._id} + isEditPanelOpen={selectedDeckId === deck?._id} activeItem={activeTab === index} handleSelectAndShowDeck={() => handleSelectDeck(deck)} handleDeckLoaded={() => handleDeckLoaded(deck?._id)} loadingState={loadingState[deck?._id]} + setLoadingState={setLoadingState} /> )); - // useEffect(() => { - // // Simulate deck loading process - // if (selectedDeckId && loadingState[selectedDeckId]) { - // setLoadingState((prev) => ({ - // ...prev, - // [selectedDeckId]: true, - // })); - // setTimeout(() => handleDeckLoaded(selectedDeckId), 2000); // Simulate fetching time - // } - // }, [selectedDeckId, loadingState, handleDeckLoaded]); + const handleOpenAddDialog = useCallback(() => { + setActiveFormSchema('addDeckForm'); + openDialog('isAddDeckDialogOpen'); + }, [openDialog, setActiveFormSchema]); + const handleChangeTab = useCallback( + (event, newValue) => { + const newDeck = safeDeckList[newValue]; + if (newDeck) { + setActiveTab(newValue); + console.log('Tab changed to:', newValue); + setLoadingState((prev) => ({ + ...prev, + [newDeck._id]: true, // Assuming _id is always available; handle cases where it might not be + })); + decksRef.current[newValue] = newDeck; + handleSelectDeck(newDeck); + console.log('NEW SELECTED DECK', newDeck); + // console.log('DECKS REF', decksRef.current); + if (!loadingState[newDeck._id]) { + setTimeout(() => handleDeckLoaded(newDeck._id), 2000); // Simulate loading + } + } else { + console.error('Selected deck is undefined.'); + } + // if (!loadingState[newDeck._id]) { + // setLoadingState((prev) => ({ ...prev, [newDeck._id]: true })); // Set loading true for the new tab + // setTimeout(() => handleDeckLoaded(newDeck._id), 2000); // Simulate fetching time + // } + }, + [safeDeckList, handleSelectDeck] + ); + const handleStorageChange = () => { + const rawData = localStorage.getItem('decks'); + let newData = { byId: {} }; - // useEffect(() => { - // if (selectedDeckId) { - // setLoadingState((prev) => ({ ...prev, [selectedDeckId]: true })); - // // setLoadingState((prev) => ({ ...prev, [newValue]: true })); // Set loading to true for the new tab - // console.log(`LOADING DECK ${selectedDeckId}`); - // // setLoadingState((prev) => ({ ...prev, [selectedDeckId]: true })); - // } + try { + newData = rawData ? JSON.parse(rawData) : newData; + } catch (e) { + console.error('Failed to parse decks data:', e); + setError('Failed to load decks from storage.'); + return; + } - // // if (prevSelectedDeckId.current !== selectedDeckId) { - // // setLoadingState((prev) => ({ ...prev, [selectedDeckId]: true })); - // // // setLoadingState((prev) => ({ ...prev, [newValue]: true })); // Set loading to true for the new tab - // // console.log(`LOADING DECK ${selectedDeckId}`); - // // // setLoadingState((prev) => ({ ...prev, [selectedDeckId]: true })); - // // } - // }, [selectedDeckId]); + if (newData && typeof newData.byId === 'object') { + const newAllDecks = Object.values(newData.byId).filter( + (deck) => deck && deck._id !== undefined + ); + if (!newAllDecks.length) { + setError('No decks found in storage.'); + return; + } + + decksRef.current = newAllDecks; + setSafeDeckList(newAllDecks); + refreshDecks(); + } else { + setError('Invalid or corrupted deck data.'); + } + }; + useEffect(() => { + // const handleStorageChange = () => { + // const newData = JSON.parse(localStorage.getItem('decks') || '{}'); + // // if (decksRef.current[0]?.userId !== null) { + // // setSafeDeckList(Object.values(newData?.byId)); + // // } else { + // // setSafeDeckList( + // // Object.values(newData?.byId).filter( + // // (deck) => deck?.userId === user?._id + // // ) + // // ); + // // } + // if (newData !== decks && newData?.lastUpdated !== decks.lastUpdated) { + // const newAllDecks = Object.values(newData?.byId).filter( + // (deck) => deck._id !== undefined + // ); + // if (!newAllDecks.length) { + // setError('No decks found in storage.'); + // return; + // } + // decksRef.current = newAllDecks; + // setSafeDeckList(newAllDecks); + // refreshDecks(); + // } + // }; + window.addEventListener('storage', handleStorageChange); + return () => window.removeEventListener('storage', handleStorageChange); + }, []); + useEffect(() => { + setActiveTab((prevActiveTab) => { + const newIndex = Math.min(prevActiveTab, safeDeckList.length - 1); + const newDeck = safeDeckList[newIndex]; + setLoadingState((prev) => ({ + ...prev, + [newDeck?._id]: true, + })); // Set loading state to true when selecting a new deck + decksRef.current[newIndex] = newDeck; + console.log('NEW DECK', newDeck); + console.log('DECKS REF', decksRef.current); + // handleSelectDeck(newDeck); + if (!loadingState[newDeck?._id]) { + setLoadingState((prev) => ({ ...prev, [newDeck?._id]: true })); // Set loading true for the new tab + setTimeout(() => handleDeckLoaded(newDeck?._id), 2000); // Simulate fetching time + } + return newIndex >= 0 ? newIndex : 0; // Ensure that the index is always non-negative + }); + }, [safeDeckList.length]); return ( diff --git a/src/layout/home/HeroChartSection.jsx b/src/layout/home/HeroChartSection.jsx index 29fe2a9..ddcbce4 100644 --- a/src/layout/home/HeroChartSection.jsx +++ b/src/layout/home/HeroChartSection.jsx @@ -1,7 +1,7 @@ import MDBox from '../REUSABLE_COMPONENTS/MDBOX'; import placeHolder from '../../assets/images/placeholder.jpeg'; import { Card, CardContent, Zoom } from '@mui/material'; -import FlexBetween from '../REUSABLE_COMPONENTS/FlexBetween'; +import FlexBetween from '../REUSABLE_COMPONENTS/layout-utils/FlexBetween'; import { ResponsiveContainer, CartesianGrid, @@ -12,8 +12,8 @@ import { Line, Tooltip, } from 'recharts'; -import DashboardBox from '../REUSABLE_COMPONENTS/DashboardBox'; -import BoxHeader from '../REUSABLE_COMPONENTS/BoxHeader'; +import DashboardBox from '../REUSABLE_COMPONENTS/layout-utils/DashboardBox'; +import BoxHeader from '../REUSABLE_COMPONENTS/layout-utils/BoxHeader'; import { useMode } from '../../context'; import { useMemo } from 'react'; import { diff --git a/src/layout/profile/index.jsx b/src/layout/profile/index.jsx index 926a9ea..e0cfb38 100644 --- a/src/layout/profile/index.jsx +++ b/src/layout/profile/index.jsx @@ -15,7 +15,7 @@ import team1 from '../../assets/images/bg1.jpg'; import team2 from '../../assets/images/bg1.jpg'; import team3 from '../../assets/images/bg1.jpg'; import team4 from '../../assets/images/bg1.jpg'; -import DashboardLayout from '../REUSABLE_COMPONENTS/DashBoardLayout'; +import DashboardLayout from '../REUSABLE_COMPONENTS/layout-utils/DashBoardLayout'; import MDBox from '../REUSABLE_COMPONENTS/MDBOX'; import Header from './sub-components/Header'; import PlatformSettings from './sub-components/Settings'; diff --git a/src/layout/store/index.jsx b/src/layout/store/index.jsx index 7893be2..fce0e14 100644 --- a/src/layout/store/index.jsx +++ b/src/layout/store/index.jsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { useMode } from '../../context'; import MDBox from '../REUSABLE_COMPONENTS/MDBOX'; -import PageLayout from '../REUSABLE_COMPONENTS/PageLayout'; +import PageLayout from '../REUSABLE_COMPONENTS/layout-utils/PageLayout'; import { PortfolioBoxA } from '../../pages/pageStyles/StyledComponents'; import SearchComponent from '../../components/forms/search/SearchComponent'; diff --git a/src/pages/CartPage.js b/src/pages/CartPage.js index 80d91fd..bc4d126 100644 --- a/src/pages/CartPage.js +++ b/src/pages/CartPage.js @@ -3,7 +3,7 @@ import { Box, Card, CardContent, Grid, useTheme } from '@mui/material'; import CartContent from '../layout/cart/CartContent'; import { useMode } from '../context'; import Checkout from '../layout/cart/cartPageContainers/Checkout'; -import PageLayout from '../layout/REUSABLE_COMPONENTS/PageLayout'; +import PageLayout from '../layout/REUSABLE_COMPONENTS/layout-utils/PageLayout'; import { useCartManager } from '../context/MAIN_CONTEXT/CartContext/useCartManager'; import MDBox from '../layout/REUSABLE_COMPONENTS/MDBOX'; diff --git a/src/pages/CollectionPage.js b/src/pages/CollectionPage.js index 8b2b70a..6946a68 100644 --- a/src/pages/CollectionPage.js +++ b/src/pages/CollectionPage.js @@ -3,8 +3,7 @@ import { Box, Grid } from '@mui/material'; import CollectionPortfolio from '../layout/collection'; import GenericCardDialog from '../components/dialogs/GenericCardDialog'; import useLoadingAndModal from './pageStyles/useLoadingAndModal'; -import HeroBanner from './pageStyles/HeroBanner'; -import PageLayout from '../layout/REUSABLE_COMPONENTS/PageLayout'; +import PageLayout from '../layout/REUSABLE_COMPONENTS/layout-utils/PageLayout'; import useSelectedCollection from '../context/MAIN_CONTEXT/CollectionContext/useSelectedCollection'; import useCollectionManager from '../context/MAIN_CONTEXT/CollectionContext/useCollectionManager'; import { useLoading } from '../context/hooks/useLoading'; @@ -21,11 +20,8 @@ const CollectionPage = () => { return ; } const { fetchCollections, hasFetchedCollections } = collectionData; - const { returnDisplay, isModalOpen, modalContent, closeModal } = useLoadingAndModal(); - const { isPageLoading } = useLoading(); - useEffect(() => { if (!hasFetchedCollections) { fetchCollections(); @@ -34,21 +30,7 @@ const CollectionPage = () => { return ( - - {!selectedCollection && ( - - )} + {isModalOpen && ( { const { closeModal, returnDisplay, isModalOpen, modalContent } = useLoadingAndModal(); const { hasFetchedDecks, fetchDecks } = useDeckManager(); // useEffect(() => { - // fetchDecks(); - // }, []); - // useEffect(() => { // if (!hasFetchedDecks) { // fetchDecks(); // } diff --git a/src/pages/HomePage.js b/src/pages/HomePage.js index c0af733..c2413e1 100644 --- a/src/pages/HomePage.js +++ b/src/pages/HomePage.js @@ -4,7 +4,7 @@ import { useModalContext } from '../context/UTILITIES_CONTEXT/ModalContext/Modal import GenericCardDialog from '../components/dialogs/GenericCardDialog'; import DetailsModal from '../components/dialogs/DetailsModal'; import SplashPage2 from '../layout/REUSABLE_COMPONENTS/system-utils/SplashPage2'; -import PageLayout from '../layout/REUSABLE_COMPONENTS/PageLayout'; +import PageLayout from '../layout/REUSABLE_COMPONENTS/layout-utils/PageLayout'; // import MainContentSection from './sections/MainContentSection'; import HeroSection from '../layout/home/HeroSection'; import FeatureCardsSection from '../layout/home/FeatureCardsSection'; diff --git a/src/pages/StorePage.js b/src/pages/StorePage.js index 026d971..7dcc07a 100644 --- a/src/pages/StorePage.js +++ b/src/pages/StorePage.js @@ -3,7 +3,7 @@ import GenericCardDialog from '../components/dialogs/GenericCardDialog'; import MDBox from '../layout/REUSABLE_COMPONENTS/MDBOX'; import useLoadingAndModal from './pageStyles/useLoadingAndModal'; import HeroBanner from './pageStyles/HeroBanner'; -import PageLayout from '../layout/REUSABLE_COMPONENTS/PageLayout'; +import PageLayout from '../layout/REUSABLE_COMPONENTS/layout-utils/PageLayout'; import StoreSearch from '../layout/store'; const StorePage = () => { const { loadingStatus, returnDisplay, isModalOpen, modalContent } =