*/}
diff --git a/src/components/other/dataDisplay/chart/PieChartStats.jsx b/src/components/other/dataDisplay/chart/PieChartStats.jsx
index 8a1755b..4437ebf 100644
--- a/src/components/other/dataDisplay/chart/PieChartStats.jsx
+++ b/src/components/other/dataDisplay/chart/PieChartStats.jsx
@@ -1,18 +1,48 @@
import React from 'react';
-import { Box, Grid, Paper, Typography } from '@mui/material';
-import { PieChart } from '@mui/x-charts/PieChart';
+import { Box, Grid, Paper, Typography, useTheme } from '@mui/material';
+import { styled } from '@mui/system';
+import { useMode } from '../../../../context';
+import { PieChart } from '@mui/x-charts';
-const PieChartStats = ({ classes, chartData }) => (
-
-
-
- Collection Statistics
-
-
-
- {' '}
-
-
-);
+// Define styled components
+const StatisticPaper = styled(Paper)(({ theme }) => ({
+ padding: theme.spacing(2),
+ color: theme.palette.text.secondary,
+ background: theme.palette.background.paper,
+ boxShadow: theme.shadows[2],
+ borderRadius: theme.shape.borderRadius,
+ transition: 'box-shadow 0.3s',
+ '&:hover': {
+ boxShadow: theme.shadows[4],
+ },
+}));
+
+const StatisticHeader = styled(Typography)(({ theme }) => ({
+ fontWeight: 'bold',
+ color: theme.palette.primary.main,
+ marginBottom: theme.spacing(1),
+}));
+
+const PieChartStats = ({ chartData }) => {
+ const { theme } = useMode();
+
+ return (
+
+
+
+ Collection Statistics
+
+
+
+
+
+
+ );
+};
export default PieChartStats;
diff --git a/src/components/other/dataDisplay/chart/PortfolioChart.jsx b/src/components/other/dataDisplay/chart/PortfolioChart.jsx
deleted file mode 100644
index 08276d7..0000000
--- a/src/components/other/dataDisplay/chart/PortfolioChart.jsx
+++ /dev/null
@@ -1,224 +0,0 @@
-import React, {
- useCallback,
- useEffect,
- useLayoutEffect,
- useMemo,
- useRef,
- useState,
-} from 'react';
-import {
- Box,
- Container,
- Grid,
- Paper,
- styled,
- useMediaQuery,
- useTheme,
-} from '@mui/material';
-import LinearChart from './LinearChart';
-import { useChartContext } from '../../../../context/ChartContext/ChartContext';
-import ErrorBoundary from '../../../../context/ErrorBoundary';
-import { useCollectionStore } from '../../../../context/CollectionContext/CollectionContext';
-import debounce from 'lodash/debounce';
-import { getUniqueValidData } from '../../../../context/CollectionContext/helpers';
-import { usePageContext } from '../../../../context';
-import LoadingIndicator from '../../../reusable/indicators/LoadingIndicator';
-
-const ChartPaper = styled(Paper)(({ theme }) => ({
- borderRadius: theme.shape.borderRadius,
- boxShadow: theme.shadows[5],
- backgroundColor: theme.palette.background.paper,
- color: theme.palette.text.secondary,
- padding: theme.spacing(2),
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- justifyContent: 'center',
- width: '100%',
- minHeight: '400px',
- overflow: 'hidden',
- margin: theme.spacing(2, 0),
-}));
-
-const ResponsiveSquare = styled(Box)(({ theme }) => ({
- width: '100%',
- paddingTop: '100%',
- backgroundColor: theme.palette.background.paper,
- borderRadius: theme.shape.borderRadius,
- boxShadow: theme.shadows[5],
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
-}));
-
-function handleThresholdUpdate(lastUpdateTime, setLastUpdateTime) {
- const currentTime = new Date().getTime();
- if (!lastUpdateTime || currentTime - lastUpdateTime >= 600000) {
- setLastUpdateTime(currentTime);
- return currentTime;
- }
- return lastUpdateTime;
-}
-
-const PortfolioChart = () => {
- const { selectedCollection, allXYValues } = useCollectionStore();
- const { latestData, timeRange, groupAndAverageData, convertDataForNivo2 } =
- useChartContext();
- const { isLoading, setIsLoading, displayLoadingIndicator } = usePageContext();
- const theme = useTheme();
- const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
-
- // const [lastUpdateTime, setLastUpdateTime] = useState(null);
- const lastUpdateTimeRef = useRef(null);
-
- const chartContainerRef = useRef(null);
- const [chartDimensions, setChartDimensions] = useState({
- width: 0,
- height: 0,
- });
- const HEIGHT_TO_WIDTH_RATIO = 7 / 10;
- const threshold = useMemo(
- () => handleThresholdUpdate(lastUpdateTimeRef),
- [lastUpdateTimeRef]
- );
- const [dataReady, setDataReady] = useState(false);
- const calculateChartDimensions = useCallback(() => {
- if (chartContainerRef.current) {
- const width = chartContainerRef.current.offsetWidth;
- const height = width * HEIGHT_TO_WIDTH_RATIO;
- setChartDimensions({ width, height });
- }
- }, []);
-
- // Effect: Update chart dimensions on window resize
- useEffect(() => {
- const handleResize = debounce(calculateChartDimensions, 100);
- window.addEventListener('resize', handleResize);
- return () => {
- handleResize.cancel();
- window.removeEventListener('resize', handleResize);
- };
- }, [calculateChartDimensions]);
-
- // Effect: Set data ready state based on allXYValues
- useEffect(() => {
- if (Array.isArray(allXYValues) && allXYValues.length > 0) {
- setDataReady(true);
- }
- }, [allXYValues]);
- // const calculateChartDimensions = () => {
- // if (chartContainerRef.current) {
- // const width = chartContainerRef.current.offsetWidth;
- // const height = width * HEIGHT_TO_WIDTH_RATIO;
- // setChartDimensions({ width, height });
- // }
- // };
- // useEffect(() => {
- // if (allXYValues && Array.isArray(allXYValues) && allXYValues.length > 0) {
- // setDataReady(true);
- // }
- // }, [allXYValues]);
- useLayoutEffect(() => {
- if (dataReady) {
- calculateChartDimensions();
- }
- }, [dataReady]);
- const getFilteredData2 = useCallback((values) => {
- if (!values || !Array.isArray(values) || !values?.length > 0) return [];
- return getUniqueValidData(values || []);
- }, []);
- const filteredChartData2 = useMemo(
- () => getFilteredData2(allXYValues),
- [allXYValues, timeRange, getFilteredData2]
- );
- const isEnoughData = filteredChartData2?.length >= 5;
- const rawData2 = useMemo(() => {
- return groupAndAverageData(filteredChartData2, threshold, timeRange);
- }, [filteredChartData2, threshold, timeRange]);
-
- const nivoReadyData2 = useMemo(() => {
- return rawData2?.length > 0 ? convertDataForNivo2(rawData2) : [];
- }, [rawData2]);
- // useMemo for expensive calculations
-
- // // Debounced window resize handler
- // const debouncedCalculateChartDimensions = useMemo(
- // () => debounce(calculateChartDimensions, 100),
- // []
- // );
-
- // useEffect(() => {
- // window.addEventListener('resize', debouncedCalculateChartDimensions);
- // return () => {
- // window.removeEventListener('resize', debouncedCalculateChartDimensions);
- // debouncedCalculateChartDimensions.cancel();
- // };
- // }, [debouncedCalculateChartDimensions]);
-
- // Layout Effect: Update chart dimensions if data is ready
- useLayoutEffect(() => {
- if (dataReady) calculateChartDimensions();
- }, [dataReady, calculateChartDimensions]);
-
- // Render loading indicator, error message or chart based on conditions
- if (!dataReady) {
- return
;
- }
-
- if (!isEnoughData || !rawData2?.length) {
- return (
-
- Not enough data points
-
- );
- }
-
- if (!filteredChartData2?.length) {
- return
No data available
;
- }
-
- return (
-
-
-
-
-
- {filteredChartData2?.length > 0 && rawData2?.length > 0 ? (
-
- ) : (
- No data available
- )}
-
-
-
-
-
- );
-};
-
-export default PortfolioChart;
diff --git a/src/components/other/search/SearchBar.js b/src/components/other/search/SearchBar.js
index 7d098e6..8cdcb47 100644
--- a/src/components/other/search/SearchBar.js
+++ b/src/components/other/search/SearchBar.js
@@ -14,107 +14,146 @@ export const commonPaperStyles = (theme) => ({
// SearchBar.jsx
import React, { useState, useEffect } from 'react';
-import { Grid, Typography, Container, Paper } from '@mui/material';
-import { Button, TextField } from '@mui/material';
+import SettingsIcon from '@mui/icons-material/Settings';
-import { useCardStore, useDeckStore, useMode } from '../../../context';
-// import SearchForm from '../../forms/SearchForm';
-import search from './search.json';
-// import { commonPaperStyles } from '../../../path/to/commonStyles'; // import common styles
+import {
+ Container,
+ Paper,
+ Typography,
+ Box,
+ IconButton,
+ Menu,
+ MenuItem,
+} from '@mui/material';
+import {
+ useCardStore,
+ useDeckStore,
+ useFormContext,
+ useMode,
+} from '../../../context';
+import SearchForm from '../../forms/SearchForm';
+import SearchSettings from './SearchSettings';
-const SearchBar = ({ onSearchFocus, onSearchBlur, searchTerm }) => {
+const SearchBar = ({ onSearchFocus, onSearchBlur }) => {
const { theme } = useMode();
- const { initialState, filters } = search;
- const { selectedDeck } = useDeckStore();
const { handleRequest } = useCardStore();
- const [searchParams, setSearchParams] = useState(initialState);
+ const { forms, handleChange, handleSubmit } = useFormContext(); // Consume context
+ useEffect(
+ () => {
+ console.log('SearchBar forms', forms);
+ if (!forms?.searchForm?.searchTerm) return;
+ handleRequest(forms?.searchForm?.searchTerm);
+ },
+ [forms?.searchForm?.searchTerm],
+ handleSubmit
+ ); // Watch for changes in the form data
+ const [anchorEl, setAnchorEl] = useState(null);
+ const open = Boolean(anchorEl);
- useEffect(() => {
- if (selectedDeck) {
- Object.entries(selectedDeck).forEach(([key, value]) => {
- setSearchParams((prev) => ({ ...prev, [key]: value || '' }));
- });
- }
- }, [selectedDeck]);
-
- const handleChange = (name, newValue) => {
- setSearchParams((prev) => ({ ...prev, [name]: newValue }));
+ const handleClick = (event) => {
+ setAnchorEl(event.currentTarget);
};
- const handleSubmit = () => {
- handleRequest(searchParams);
+ const handleClose = () => {
+ setAnchorEl(null);
};
-
return (
-
-
- {/* eslint-disable-next-line max-len */}
-
+
+
- Search Cards
-
- handleChange('name', e.target.value)}
- handleSubmit={handleSubmit}
- handleKeyPress={(e) => e.key === 'Enter' && handleSubmit()}
- theme={theme}
- />
-
-
+
+ Search Cards
+
+
+
+
+
+
+
+ {/* eslint-disable-next-line max-len */}
+ {
+ e.preventDefault();
+ handleSubmit('searchForm')(e); // Pass the event to the handleSubmit function
+ }}
+ handleKeyPress={(e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ handleSubmit('searchForm')(e);
+ }
+ }}
+ onFocus={onSearchFocus}
+ onBlur={onSearchBlur}
+ />
+
+
+
);
};
export default SearchBar;
-// // SearchForm.jsx
-// import React from 'react';
-// import { Button, TextField, Paper } from '@mui/material';
+// // // SearchForm.jsx
+// // import React from 'react';
+// // import { Button, TextField, Paper } from '@mui/material';
-const SearchForm = ({
- searchTerm,
- handleChange,
- handleSubmit,
- handleKeyPress,
-}) => {
- const { theme } = useMode();
- return (
-
-
-
- );
-};
+// const SearchForm = ({
+// searchTerm,
+// handleChange,
+// handleSubmit,
+// handleKeyPress,
+// }) => {
+// const { theme } = useMode();
+// return (
+//
+//
+//
+// );
+// };
-// export default SearchForm;
+// // export default SearchForm;
diff --git a/src/components/other/search/SearchFormB.jsx b/src/components/other/search/SearchFormB.jsx
new file mode 100644
index 0000000..7f0e0b5
--- /dev/null
+++ b/src/components/other/search/SearchFormB.jsx
@@ -0,0 +1,58 @@
+// SearchForm.jsx
+import React from 'react';
+import {
+ TextField,
+ MenuItem,
+ FormControl,
+ Select,
+ InputLabel,
+} from '@mui/material';
+import { styled } from '@mui/system';
+
+const StyledFormControl = styled(FormControl)(({ theme }) => ({
+ minWidth: 120,
+}));
+
+const SearchFormB = ({
+ searchSettings,
+ handleSearch,
+ handleSearchBy,
+ handleSortBy,
+}) => {
+ return (
+ <>
+
+
+ Search by
+
+
+
+ Sort by
+
+
+ >
+ );
+};
+
+export default SearchFormB;
diff --git a/src/components/other/search/SearchSettings.jsx b/src/components/other/search/SearchSettings.jsx
new file mode 100644
index 0000000..a7fa97c
--- /dev/null
+++ b/src/components/other/search/SearchSettings.jsx
@@ -0,0 +1,86 @@
+// SearchSettings.jsx
+import React, { useState } from 'react';
+import { Paper, Box, Container, Typography } from '@mui/material';
+import { styled } from '@mui/system';
+import SearchFormB from './SearchFormB'; // Ensure path is correct
+import { useMode } from '../../../context';
+
+export const commonPaperStyles = (theme) => ({
+ padding: theme.spacing(3),
+ borderRadius: theme.shape.borderRadius,
+ background: theme.palette.success.dark,
+ boxShadow: theme.shadows[3],
+ margin: 'auto',
+ width: '100%',
+ maxWidth: 'md',
+ '&:hover': {
+ boxShadow: theme.shadows[6],
+ },
+});
+
+const SearchSettingsBox = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(2),
+}));
+
+const SearchSettings = () => {
+ const [searchSettings, setSearchSettings] = useState({
+ search: '',
+ searchBy: 'title',
+ sortBy: 'release_date',
+ });
+
+ const { theme } = useMode();
+
+ const handleSearch = (e) => {
+ setSearchSettings({
+ ...searchSettings,
+ search: e.target.value,
+ });
+ };
+
+ const handleSearchBy = (e) => {
+ setSearchSettings({
+ ...searchSettings,
+ searchBy: e.target.value,
+ });
+ };
+
+ const handleSortBy = (e) => {
+ setSearchSettings({
+ ...searchSettings,
+ sortBy: e.target.value,
+ });
+ };
+
+ return (
+
+
+ {' '}
+ {/*
+ Search Settings
+ */}
+
+
+
+
+
+ );
+};
+
+export default SearchSettings;
diff --git a/src/components/reusable/PrivateRoute.jsx b/src/components/reusable/PrivateRoute.jsx
index c4c3dba..f7856e1 100644
--- a/src/components/reusable/PrivateRoute.jsx
+++ b/src/components/reusable/PrivateRoute.jsx
@@ -4,9 +4,11 @@ import { useAuthContext } from '../../context';
const PrivateRoute = ({ children }) => {
const { isLoggedIn } = useAuthContext();
- if (!isLoggedIn) return
;
- // If isLoggedIn from either cookie or context is true, proceed to the route
+ if (!isLoggedIn) {
+ return
;
+ }
+
return children;
};
diff --git a/src/containers/collectionPageContainers/CollectionPortfolioChartContainer.jsx b/src/containers/collectionPageContainers/CollectionPortfolioChartContainer.jsx
index 99ac52c..0f17ec7 100644
--- a/src/containers/collectionPageContainers/CollectionPortfolioChartContainer.jsx
+++ b/src/containers/collectionPageContainers/CollectionPortfolioChartContainer.jsx
@@ -1,6 +1,20 @@
-import React from 'react';
-import { Box, Grid, useTheme, useMediaQuery } from '@mui/material';
-import PortfolioChart from '../../components/other/dataDisplay/chart/PortfolioChart';
+import React, {
+ useCallback,
+ useEffect,
+ useLayoutEffect,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
+import {
+ Box,
+ Grid,
+ useTheme,
+ useMediaQuery,
+ Paper,
+ Container,
+} from '@mui/material';
+import PortfolioChart from '../../assets/cleanup/PortfolioChart';
import TimeRangeSelector from '../../components/other/InputComponents/TimeRangeSelector';
import UpdateStatusBox2 from '../../components/other/InputComponents/UpdateStatusBox2';
import TopCardsDisplay from '../../components/other/dataDisplay/TopCardsDisplay';
@@ -11,16 +25,116 @@ import {
useCombinedContext,
useStatisticsStore,
useMode,
+ ErrorBoundary,
+ usePageContext,
} from '../../context';
+import { styled } from '@mui/styles';
+import { debounce } from 'lodash';
+import LoadingIndicator from '../../components/reusable/indicators/LoadingIndicator';
+import LinearChart from '../../components/other/dataDisplay/chart/LinearChart';
+
+const ChartPaper = styled(Paper)(({ theme }) => ({
+ borderRadius: theme.shape.borderRadius,
+ boxShadow: theme.shadows[5],
+ backgroundColor: theme.palette.background.paper,
+ color: theme.palette.text.secondary,
+ padding: theme.spacing(2),
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
+ width: '100%',
+ minHeight: '400px',
+ overflow: 'hidden',
+ margin: theme.spacing(2, 0),
+ flexGrow: 1,
+}));
+
+const ResponsiveSquare = styled(Box)(({ theme }) => ({
+ width: '100%',
+ paddingTop: '100%',
+ backgroundColor: theme.palette.background.paper,
+ borderRadius: theme.shape.borderRadius,
+ boxShadow: theme.shadows[5],
+ display: 'flex',
+ flexGrow: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+}));
+function handleThresholdUpdate(lastUpdateTime, setLastUpdateTime) {
+ const currentTime = new Date().getTime();
+ if (!lastUpdateTime || currentTime - lastUpdateTime >= 600000) {
+ setLastUpdateTime(currentTime);
+ return currentTime;
+ }
+ return lastUpdateTime;
+}
const CollectionPortfolioChartContainer = ({ selectedCards, removeCard }) => {
const { theme } = useMode();
const theme2 = useTheme();
const isMobile = useMediaQuery(theme2.breakpoints.down('sm'));
- const { timeRange } = useChartContext();
+ const [dataReady, setDataReady] = useState(false);
+
+ const { latestData, timeRange, groupAndAverageData, convertDataForNivo2 } =
+ useChartContext();
const { allCollections } = useCollectionStore();
const { socket } = useCombinedContext();
- const { stats, statsByCollectionId } = useStatisticsStore();
+ const { stats, statsByCollectionId, markers } = useStatisticsStore();
+ const { selectedCollection, allXYValues } = useCollectionStore();
+ const lastUpdateTimeRef = useRef(null);
+
+ const chartRef = useRef(null);
+ const [chartDimensions, setChartDimensions] = useState({
+ width: 0,
+ height: 0,
+ });
+ const HEIGHT_TO_WIDTH_RATIO = 7 / 10;
+ const threshold = useMemo(
+ () => handleThresholdUpdate(lastUpdateTimeRef),
+ [lastUpdateTimeRef]
+ );
+ // Dynamically calculate and set chart dimensions
+ const calculateChartDimensions = useCallback(() => {
+ if (chartRef.current) {
+ const width = chartRef.current.offsetWidth;
+ const height = width * HEIGHT_TO_WIDTH_RATIO; // Maintain aspect ratio
+ setChartDimensions({ width, height });
+ }
+ }, []);
+
+ useEffect(() => {
+ calculateChartDimensions(); // Calculate on initial load
+ const handleResize = debounce(calculateChartDimensions, 100); // Debounce resize event
+ window.addEventListener('resize', handleResize);
+ return () => {
+ handleResize.cancel();
+ window.removeEventListener('resize', handleResize);
+ };
+ }, [calculateChartDimensions]);
+
+ if (!allXYValues?.length) {
+ return
; // Show loading indicator while data is being fetched
+ }
+
+ const rawData = groupAndAverageData(allXYValues, threshold, timeRange);
+ const nivoReadyData = convertDataForNivo2(rawData);
+
+ if (!nivoReadyData.length) {
+ return
No data available;
+ }
+
+ // Effect: Set data ready state based on allXYValues
+ useEffect(() => {
+ if (Array.isArray(allXYValues) && allXYValues?.length > 0) {
+ setDataReady(true);
+ }
+ }, [allXYValues]);
+ useLayoutEffect(() => {
+ if (dataReady) {
+ calculateChartDimensions();
+ }
+ }, [dataReady]);
return (
{
{/* Portfolio Chart Row */}
-
+
+
+ {/* Portfolio Chart */}
+
+
+
+
+
+
+
+ {/*
+
+
+
+ {allXYValues?.length > 0 && rawData2?.length > 0 ? (
+
+ ) : (
+ No data available
+ )}
+
+
+
+ */}
+
+
diff --git a/src/context/AuthContext/authContext.js b/src/context/AuthContext/authContext.js
index e0964d5..f1859ec 100644
--- a/src/context/AuthContext/authContext.js
+++ b/src/context/AuthContext/authContext.js
@@ -1,33 +1,70 @@
+/* eslint-disable @typescript-eslint/no-empty-function */
import React, {
useState,
useEffect,
useCallback,
useRef,
useContext,
+ createContext,
+ useMemo,
} from 'react';
import axios from 'axios';
import { useCookies } from 'react-cookie';
import { processResponseData } from './helpers';
import { usePageContext } from '../PageContext/PageContext';
-export const AuthContext = React.createContext();
-
+// export const AuthContext = React.createContext();
+export const AuthContext = createContext({
+ isLoggedIn: false,
+ authUser: null,
+ token: null,
+ user: null,
+ responseData: null,
+ login: () => {},
+ signup: () => {},
+ logout: () => {},
+ resetLogoutTimer: () => {},
+ setUser: () => {},
+ setIsLoggedIn: () => {},
+ setAuthUser: () => {},
+});
export default function AuthProvider({ children, serverUrl }) {
const { setLoading } = usePageContext();
const [cookies, setCookie, removeCookie] = useCookies([
+ 'basicData',
+ 'securityData',
'user',
'isLoggedIn',
'userId',
'authUser',
'authToken',
+ 'lastLogin',
+ 'lastLogout',
]);
const [isLoggedIn, setIsLoggedIn] = useState(false);
- const [user, setUser] = useState(null);
- const [authUser, setAuthUser] = useState(null);
- const [responseData, setResponseData] = useState(null);
+ const [basicData, setBasicData] = useState(cookies.basicData);
+ const [securityData, setSecurityData] = useState(cookies.securityData);
+ const [user, setUser] = useState(cookies.user);
+ const [authUser, setAuthUser] = useState(cookies.authUser);
const [token, setToken] = useState(cookies.authToken);
+ const [responseData, setResponseData] = useState(null);
+ const lastLogin = useRef(null);
const logoutTimerRef = useRef(null);
-
+ // function to set login times for tracking
+ const setLoginTimes = useCallback(() => {
+ lastLogin.current = new Date();
+ setCookie('lastLogin', lastLogin.current, { path: '/' });
+ setCookie('lastLogout', logoutTimerRef.current, { path: '/' });
+ }, [setCookie, logoutTimerRef]);
+ // value for tracking changes in login status
+ const [loginStatus, setLoginStatus] = useState({
+ isLoggedIn: isLoggedIn,
+ lastLogin: lastLogin.current,
+ lastLogout: logoutTimerRef.current,
+ authUser: authUser,
+ token: token,
+ user: user,
+ });
const REACT_APP_SERVER = serverUrl || process.env.REACT_APP_SERVER;
const executeAuthAction = async (actionType, url, requestData) => {
@@ -40,19 +77,48 @@ export default function AuthProvider({ children, serverUrl }) {
console.log('Response:', response);
const processedData = processResponseData(response, actionType);
if (response.status === 200 || response.status === 201) {
- const { token, userData, authData, message, data } = processedData;
+ const {
+ token,
+ userData,
+ authData,
+ message,
+ data,
+ securityData,
+ basicData,
+ } = processedData;
+ // console.log('Processed Data: ', data);
console.log('Processed Data Message: ', message);
console.log('Processed User Data: ', userData);
+ console.log('Processed Auth Data', authData);
+ console.log('Processed Token: ', token);
+ console.log('Processed Security Data: ', securityData);
+ console.log('Processed Basic Data: ', basicData);
setToken(token);
- setUser(userData);
+ setBasicData(basicData);
+ setSecurityData(securityData);
+ setUser(data);
setAuthUser(authData);
setResponseData(data);
setIsLoggedIn(true);
+ setLoginTimes();
+ setLoginStatus({
+ isLoggedIn: isLoggedIn,
+ lastLogin: lastLogin.current,
+ lastLogout: logoutTimerRef.current,
+ authUser: authUser,
+ token: token,
+ user: user,
+ });
+
setCookie('authToken', token, { path: '/' });
- setCookie('authUser', authData, { path: '/' });
+ setCookie('basicData', basicData, { path: '/' });
+ setCookie('securityData', securityData, { path: '/' });
setCookie('user', userData, { path: '/' });
+ setCookie('authUser', authData, { path: '/' });
setCookie('isLoggedIn', true, { path: '/' });
setCookie('userId', authData?.id, { path: '/' });
+ setCookie('lastLogin', lastLogin.current, { path: '/' });
+ setCookie('lastLogout', logoutTimerRef.current, { path: '/' });
}
} catch (error) {
console.error('Auth error:', error);
@@ -60,86 +126,150 @@ export default function AuthProvider({ children, serverUrl }) {
setLoading('isPageLoading', false);
}
};
-
+ // LOGIC FOR LOGOUT, TOKEN EXPIRATION, AND USER ACTIVITY
const logout = useCallback(() => {
removeCookie('authToken');
removeCookie('user');
removeCookie('authUser');
removeCookie('isLoggedIn');
removeCookie('userId');
+ removeCookie('lastLogin');
+ removeCookie('lastLogout');
setIsLoggedIn(false);
+ setLoginStatus({
+ isLoggedIn: isLoggedIn,
+ lastLogin: lastLogin.current,
+ lastLogout: logoutTimerRef.current,
+ });
setUser(null);
setToken(null);
if (logoutTimerRef.current) clearTimeout(logoutTimerRef.current);
}, [removeCookie]);
-
const resetLogoutTimer = useCallback(() => {
clearTimeout(logoutTimerRef.current);
logoutTimerRef.current = setTimeout(logout, 2700000); // 45 minutes
}, [logout]);
-
useEffect(() => {
if (token) {
+ console.log('Token found, resetting logout timer...');
resetLogoutTimer();
}
}, [token, resetLogoutTimer]);
-
useEffect(() => {
- axios.interceptors.request.use(
+ const interceptorId = axios.interceptors.request.use(
(config) => {
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
},
(error) => Promise.reject(error)
);
+
+ return () => axios.interceptors.request.eject(interceptorId);
}, [token]);
+ // LOGIC FOR LOGIN AND SIGNUP
const login = async (username, password) => {
await executeAuthAction('signin', 'signin', {
userSecurityData: { username, password },
});
};
-
const signup = async (securityData, basicData) => {
await executeAuthAction('signup', 'signup', {
userSecurityData: securityData,
userBasicData: basicData,
});
};
-
useEffect(() => {
+ console.log('Auth Context: ');
const storedToken = cookies['authToken'];
+ // const storedUserId = cookies['userId'];
+ const storedUserBasicData = cookies['basicData'];
+ const storedUserSecurityData = cookies['securityData'];
const storedUser = cookies['user'];
const storedAuthUser = cookies['authUser'];
if (storedToken && storedUser) {
setToken(storedToken);
+ setBasicData(storedUserBasicData);
+ setSecurityData(storedUserSecurityData);
setUser(storedUser);
+ setAuthUser(storedAuthUser);
setIsLoggedIn(true);
+ setLoginTimes();
+ setLoginStatus({
+ isLoggedIn: isLoggedIn,
+ lastLogin: lastLogin.current,
+ lastLogout: logoutTimerRef.current,
+ authUser: authUser,
+ token: token,
+ user: user,
+ });
resetLogoutTimer();
}
- }, [cookies, resetLogoutTimer]);
-
+ }, [
+ cookies['authToken'],
+ cookies['basicData'],
+ cookies['securityData'],
+ cookies['user'],
+ cookies['authUser'],
+ resetLogoutTimer,
+ ]);
+ const contextValue = useMemo(
+ () => ({
+ isLoggedIn,
+ authUser,
+ token,
+ user,
+ basicData,
+ securityData,
+ responseData,
+ loginStatus,
+
+ login,
+ signup,
+ logout,
+ resetLogoutTimer,
+ setUser,
+ setIsLoggedIn,
+ setAuthUser,
+ }),
+ [
+ loginStatus,
+ isLoggedIn,
+ authUser,
+ token,
+ user,
+ responseData,
+ login,
+ signup,
+ logout,
+ resetLogoutTimer,
+ setUser,
+ setIsLoggedIn,
+ setAuthUser,
+ ]
+ );
+ useEffect(() => {
+ console.log('AUTH CONTEXT:', {
+ isLoggedIn,
+ authUser,
+ token,
+ user,
+ responseData,
+ loginStatus,
+ });
+ }, [
+ // login,
+ // signup,
+ // logout,
+ // resetLogoutTimer,
+ setUser,
+ setIsLoggedIn,
+ setLoginStatus,
+ setAuthUser,
+ ]);
return (
-
- {children}
-
+ {children}
);
}
diff --git a/src/context/AuthContext/helpers.jsx b/src/context/AuthContext/helpers.jsx
index 39ebcb6..c83604f 100644
--- a/src/context/AuthContext/helpers.jsx
+++ b/src/context/AuthContext/helpers.jsx
@@ -1,9 +1,18 @@
import jwt_decode from 'jwt-decode';
+// login status
export const LOGGED_IN_COOKIE = false;
+// token
export const AUTH_COOKIE = 'authToken';
-export const USER_COOKIE = 'user';
+// 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) => {
@@ -19,13 +28,15 @@ export const processResponseData = (response, type) => {
// if (!validateData(data, `${type} Response`, `process${type}Data`))
// return null;
const { message, data } = response.data;
+ console.log('message --------------->', message);
const token = data?.token;
console.log('token --------------->', token);
if (!token) return null;
const processedData = {
token: token,
authData: jwt_decode(token),
- userData: data?.user,
+ basicData: data?.userBasicData,
+ securityData: data?.userSecurityData,
data: data,
message: message,
};
diff --git a/src/context/CardContext/CardStore.js b/src/context/CardContext/CardStore.js
index 493cd53..a18576b 100644
--- a/src/context/CardContext/CardStore.js
+++ b/src/context/CardContext/CardStore.js
@@ -8,7 +8,6 @@ import {
useLayoutEffect,
} from 'react';
import { useCookies } from 'react-cookie';
-import { useCombinedContext } from '../CombinedContext/CombinedProvider';
import { useCollectionStore } from '../CollectionContext/CollectionContext';
import { useUserContext } from '../UserContext/UserContext';
@@ -17,10 +16,11 @@ const CardContext = createContext();
export const CardProvider = ({ children }) => {
const { user } = useUserContext();
- const [cookies, setCookie] = useCookies(['deckData']);
+ const [cookies, setCookie] = useCookies(['deckData', 'searchHistory']);
const initialStore = cookies.store || [];
const [cardsArray, setCardsArray] = useState(initialStore);
const { allCollections, setAllCollections } = useCollectionStore();
+ const [image, setImage] = useState(null);
// const [searchParam, setSearchParam] = useState('');
// const [searchParams, setSearchParams] = useState([]);
// const [searchParams, setSearchParams] = useState({
@@ -42,18 +42,29 @@ export const CardProvider = ({ children }) => {
const currentDeckData = cookies.deck || [];
const [savedDeckData, setSavedDeckData] = useState(currentDeckData);
const [deckSearchData, setDeckSearchData] = useState([]);
+ const [searchHistory, setSearchHistory] = useState([]);
if (!currenCartArray || !savedDeckData) {
return Loading...
;
}
- const handleRequest = async (searchParamss) => {
+ const handleRequest = async (searchParams) => {
try {
+ console.log('SEARCH PARAMS: ', searchParams);
// setSearchParams([searchParams]);
+ // Adding a unique dummy parameter or timestamp to the request
+ const uniqueParam = { requestTimestamp: new Date().toISOString() };
+ const requestBody = {
+ ...searchParams,
+ ...uniqueParam, // Including the unique or dummy parameter
+ };
+ // update history array with new search params
+ setSearchHistory([...searchHistory, searchParams]);
+ setCookie('searchHistory', searchParams, { path: '/' });
const response = await axios.post(
`${process.env.REACT_APP_SERVER}/api/cards/ygopro`,
- searchParamss
+ requestBody
);
if (response.data.data) {
@@ -94,6 +105,7 @@ export const CardProvider = ({ children }) => {
setCookie('deckSearchData', uniqueCards, { path: '/' });
setCookie('rawSearchData', uniqueCards, { path: '/' });
// setCookie('organizedSearchData', limitedCardsToRender, { path: '/' });
+ return response?.data?.data;
} else {
setSearchData([]);
setDeckSearchData([]);
@@ -104,52 +116,102 @@ export const CardProvider = ({ children }) => {
console.error(err);
}
};
+ // Example client-side function to fetch an image
+ // Function to request the card's image
+ // const fetchCardImage = async (cardId) => {
+ // try {
+ // // Ensure there's a cardId to fetch
+ // if (!cardId) {
+ // console.error('No card ID provided for image fetch');
+ // return;
+ // }
- const handlePatch = async () => {
- let needsUpdate = false;
-
- for (const collection of allCollections) {
- for (const card of collection.cards) {
- if (
- !user.id ||
- !card.card_images ||
- !card.card_sets ||
- !card.card_prices
- ) {
- needsUpdate = true;
- const response = await axios.patch(
- `${process.env.REACT_APP_SERVER}/api/cards/ygopro/${card.name}`,
- { id: card.id, user: user.id },
- { withCredentials: true }
- );
- if (response.data && response.data.data) {
- const updatedCard = response.data.data;
- const cardIndex = collection.cards.findIndex(
- (c) => c.id === updatedCard.id
- );
- if (cardIndex !== -1) {
- collection.cards[cardIndex] = updatedCard;
- }
- }
- }
- }
- }
+ // // Constructing the query URL
+ // const url = `${process.env.REACT_APP_SERVER}/api/card/image/${cardId}`;
- if (needsUpdate) {
- setAllCollections([...allCollections]);
- }
- };
+ // const response = await axios.get(url);
+ // if (response.status === 200 && response.data) {
+ // // Assuming response.data contains the URL of the image
+ // const imageUrl = response.data.imageUrl;
+
+ // // Update your state or perform actions with the image URL
+ // setImage(imageUrl);
+ // } else {
+ // throw new Error('Failed to fetch image');
+ // }
+ // } catch (error) {
+ // console.error('Error fetching the card image:', error);
+ // }
+ // };
+ // Example client-side function to fetch a card's image through the server
+ // const fetchCardImage = async (cardData) => {
+ // try {
+ // const response = await axios.get(
+ // `${process.env.REACT_APP_SERVER}/api/card/ygopro`,
+ // {
+ // params: { name: cardData.name },
+ // }
+ // );
+ // if (response.data) {
+ // // Assuming the server returns the image as a base64 string
+ // const imageBase64 = response.data;
+ // console.log('IMAGE BASE 64: ', imageBase64);
+ // setImage(imageBase64);
+ // return imageBase64;
+ // }
+ // throw new Error('Failed to fetch image.');
+ // } catch (error) {
+ // console.error('Error fetching the image:', error);
+ // throw error;
+ // }
+ // };
+
+ // const handlePatch = async () => {
+ // let needsUpdate = false;
+
+ // for (const collection of allCollections) {
+ // for (const card of collection.cards) {
+ // if (
+ // !user.id ||
+ // !card.card_images ||
+ // !card.card_sets ||
+ // !card.card_prices
+ // ) {
+ // needsUpdate = true;
+ // console.log('PATCHING CARD: ', card);
+ // const response = await axios.patch(
+ // `${process.env.REACT_APP_SERVER}/api/cards/ygopro/${card.name}`,
+ // { id: card.id, user: user.id },
+ // { withCredentials: true }
+ // );
+ // if (response.data && response.data.data) {
+ // const updatedCard = response.data.data;
+ // const cardIndex = collection.cards.findIndex(
+ // (c) => c.id === updatedCard.id
+ // );
+ // if (cardIndex !== -1) {
+ // collection.cards[cardIndex] = updatedCard;
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ // if (needsUpdate) {
+ // setAllCollections([...allCollections]);
+ // }
+ // };
useLayoutEffect(() => {
// Check if there's any collection that requires an update
const hasMissingData = allCollections?.some((collection) =>
collection.cards?.some(
- (card) => !card.card_images || !card.card_sets || !card.card_prices
+ (card) => !card?.card_images || !card?.card_sets || !card?.card_prices
)
);
if (hasMissingData) {
- handlePatch();
+ // handlePatch();
}
}, [allCollections]); // Keep the dependency array, but now it only triggers when necessary
@@ -184,8 +246,11 @@ export const CardProvider = ({ children }) => {
organizedSearchData,
isCardDataValid,
slicedAndMergedSearchData,
+ image,
+ // fetchCardImage,
+ setImage,
- handlePatch,
+ // handlePatch,
setSlicedAndMergedSearchData,
setOrganizedSearchData,
setRawSearchData,
diff --git a/src/context/CardImagesContext/CardImagesContext.jsx b/src/context/CardImagesContext/CardImagesContext.jsx
index aa1bd03..6277838 100644
--- a/src/context/CardImagesContext/CardImagesContext.jsx
+++ b/src/context/CardImagesContext/CardImagesContext.jsx
@@ -34,26 +34,29 @@ export const CardImagesProvider = ({ children }) => {
const [cards, setCards] = useState([]);
const [images, setImages] = useState([]); // [
const [randomCardImage, setRandomCardImage] = useState(null);
+ const [imageSrc, setImageSrc] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
// const BASE_API_URL = 'http://localhost:3001/api/card-image';
const BASE_API_URL = `${process.env.REACT_APP_SERVER}/api/card-image`;
+ // Function to download card images
const downloadCardImages = async () => {
setIsLoading(true);
try {
const response = await fetchWrapper(BASE_API_URL + '/download', 'GET');
- console.log('Response from fetchWrapper:', response);
- // If response is already JSON
- setCards(response.data); // Directly setting the response if it's already in the desired format
- cards.forEach((card) => {
- if (card.card_images && card.card_images.length > 0) {
- // Adding a dummy GET parameter to bypass caching
- const imageUrl =
- card.card_images[0].image_url + '?dummy=' + Date.now();
- setImages(imageUrl);
- }
- });
+ const fetchedCards = response.data;
+ const imageUrls = fetchedCards
+ .map((card) => {
+ if (card.card_images && card.card_images.length > 0) {
+ return card.card_images[0].image_url + '?dummy=' + Date.now();
+ }
+ return null; // or some placeholder image URL
+ })
+ .filter(Boolean); // Remove any nulls
+
+ setCards(fetchedCards);
+ setImages(imageUrls); // Set all image URLs at once
} catch (error) {
console.error('Error in downloadCardImages:', error);
setError(error.message);
@@ -62,21 +65,24 @@ export const CardImagesProvider = ({ children }) => {
}
};
- useEffect(() => {
- downloadCardImages();
- }, []);
+ // useEffect(() => {
+ // downloadCardImages();
+ // }, []);
+ // Handle random card image selection
useEffect(() => {
if (cards && cards.length > 0) {
- const randomCard = cards[Math.floor(Math.random() * cards.length)];
- if (randomCard.card_images && randomCard.card_images.length > 0) {
- // Adding a dummy GET parameter to bypass caching
- const imageUrl =
- randomCard.card_images[0].image_url + '?dummy=' + Date.now();
- setRandomCardImage(imageUrl);
+ const randomCardIndex = Math.floor(Math.random() * cards.length);
+ const randomCard = cards[randomCardIndex];
+ if (
+ randomCard &&
+ randomCard.card_images &&
+ randomCard.card_images.length > 0
+ ) {
+ setRandomCardImage(randomCard.card_images[0].image_url);
}
}
- }, [cards]);
+ }, [cards]); // Dependency on cards means this will rerun when cards are fetched/updated
useEffect(() => {
if (images && images.length > 0) {
@@ -95,6 +101,9 @@ export const CardImagesProvider = ({ children }) => {
randomCardImage,
isLoading,
error,
+ imageSrc,
+
+ setImageSrc,
downloadCardImages,
setImages,
}}
@@ -103,5 +112,3 @@ export const CardImagesProvider = ({ children }) => {
);
};
-
-export default CardImagesProvider;
diff --git a/src/context/CardImagesContext/useCardManager.jsx b/src/context/CardImagesContext/useCardManager.jsx
new file mode 100644
index 0000000..131b9fe
--- /dev/null
+++ b/src/context/CardImagesContext/useCardManager.jsx
@@ -0,0 +1,39 @@
+import { useState } from 'react';
+
+const useCardManager = (initialCards = []) => {
+ const [cards, setCards] = useState(initialCards);
+
+ // General function to update a card
+ const updateCard = (cardId, updatedData) => {
+ setCards((currentCards) =>
+ currentCards.map((card) =>
+ card.id === cardId ? { ...card, ...updatedData } : card
+ )
+ );
+ };
+
+ // Add a new card
+ const addCard = (newCardData) => {
+ setCards([...cards, newCardData]);
+ };
+
+ // General utility to update a specific field of a card
+ const updateField = (cardId, field, value) => {
+ updateCard(cardId, { [field]: value });
+ };
+
+ // Update a nested field like priceHistory or card_images
+ const updateNestedField = (cardId, field, newValue) => {
+ const card = cards.find((card) => card.id === cardId);
+ updateCard(cardId, { [field]: [...card[field], newValue] });
+ };
+
+ return {
+ cards,
+ addCard,
+ updateCard: updateField, // for updating single fields
+ updateNestedField, // for updating nested fields like arrays
+ };
+};
+
+export default useCardManager;
diff --git a/src/context/CardImagesContext/useCardVariantManager.jsx b/src/context/CardImagesContext/useCardVariantManager.jsx
new file mode 100644
index 0000000..b5762fb
--- /dev/null
+++ b/src/context/CardImagesContext/useCardVariantManager.jsx
@@ -0,0 +1,73 @@
+import { useState } from 'react';
+
+const useCardVariantManager = (initialCards = []) => {
+ const [cards, setCards] = useState(initialCards);
+
+ // Function to identify the variant index
+ const findVariantIndex = (cardId, setCode) =>
+ cards.findIndex(
+ (card) => card.id === cardId && card.card_set.set_code === setCode
+ );
+
+ // Function to determine overlay based on rarity
+ const getOverlayByRarity = (rarity) => {
+ // Define overlays for different rarities
+ const overlays = {
+ Common: 'commonOverlay',
+ Rare: 'rareOverlay',
+ 'Ultra Rare': 'ultraRareOverlay',
+ // ... other rarities
+ };
+ return overlays[rarity] || 'defaultOverlay';
+ };
+
+ // Function to update a specific variant field
+ const updateVariantField = (cardId, setCode, field, value) => {
+ const index = findVariantIndex(cardId, setCode);
+ if (index < 0) return; // Variant not found
+
+ const updatedCards = [...cards];
+ updatedCards[index] = {
+ ...updatedCards[index],
+ [field]: value,
+ // Update overlay based on rarity
+ overlay: getOverlayByRarity(updatedCards[index].card_set.set_rarity),
+ };
+ setCards(updatedCards);
+ };
+
+ // Function to add a new variant
+ const addVariant = (cardId, newVariantData) => {
+ const updatedCards = [...cards];
+ updatedCards.push({
+ ...newVariantData,
+ id: cardId,
+ // Assign overlay based on rarity
+ overlay: getOverlayByRarity(newVariantData.card_set.set_rarity),
+ });
+ setCards(updatedCards);
+ };
+
+ // Function to update nested fields like priceHistory for a specific variant
+ const updateNestedField = (cardId, setCode, field, newValue) => {
+ const index = findVariantIndex(cardId, setCode);
+ if (index < 0) return; // Variant not found
+
+ const updatedCards = [...cards];
+ const variant = updatedCards[index];
+ updatedCards[index] = {
+ ...variant,
+ [field]: [...variant[field], newValue],
+ };
+ setCards(updatedCards);
+ };
+
+ return {
+ cards,
+ addVariant,
+ updateVariantField,
+ updateNestedField,
+ };
+};
+
+export default useCardVariantManager;
diff --git a/src/context/CartContext/CartContext.js b/src/context/CartContext/CartContext.js
index 83ea2ba..fd29785 100644
--- a/src/context/CartContext/CartContext.js
+++ b/src/context/CartContext/CartContext.js
@@ -41,7 +41,7 @@ export const CartProvider = ({ children }) => {
totalPrice: 0, // Total price of items
});
const [cookies, setCookie] = useCookies(['authUser', 'cart', 'cartData']);
- const userId = cookies?.authUser?.id;
+ const userId = cookies?.authUser?.userId;
const [totalQuantity, setTotalQuantity] = useState(0);
const [totalPrice, setTotalPrice] = useState(0);
@@ -70,8 +70,10 @@ export const CartProvider = ({ children }) => {
'POST',
JSON.stringify({ userId })
);
- console.log('NEW CART DATA:', newCartData);
- setCartDataAndCookie(newCartData);
+ const { message, data } = newCartData;
+ console.log('CREATE CART: -----> response message', message);
+ console.log('CART DATA: -----> response data', data);
+ setCartDataAndCookie(data);
} catch (error) {
console.error('Error creating cart:', error);
@@ -100,7 +102,7 @@ export const CartProvider = ({ children }) => {
await createUserCart();
}
}
- }, [userId, setCookie]);
+ }, [userId]);
// Set cart data and cookie
const setCartDataAndCookie = useCallback(
@@ -131,78 +133,145 @@ export const CartProvider = ({ children }) => {
}
}, [userId, fetchUserCart]);
useEffect(() => {
- const newTotalQuantity = cartData.cart.reduce(
- (total, item) => total + item.quantity,
+ const newTotalQuantity = cartData?.cart?.reduce(
+ (total, item) => total + item?.quantity,
0
);
setTotalQuantity(newTotalQuantity);
setTotalPrice(totalCost);
}, [cartData.cart, totalCost]);
+
const updateCart = useCallback(
- async (cartId, updatedCart) => {
- if (!userId) return;
- if (!cartId) return;
+ async (cartId, updatedCart, method) => {
+ if (!userId || !cartId) return;
+
const formattedCartData = {
- cartItems: updatedCart.map((item) => ({
- ...item,
- id: item.id,
- quantity: item.quantity,
- })),
userId: userId,
+ cart: updatedCart.map((item) => ({
+ id: item.id, // assuming id is the unique identifier for each cart item
+ quantity: item.quantity, // ensure this is the current quantity to be updated in the cart
+ price: item.price, // ensure this is the current price of the item
+ // Include other necessary fields as per your cart item structure
+ })),
+ method: method, // 'POST' for adding items, 'DELETE' for removing items, 'PUT' for updating items
+ // Calculating total quantity and total price outside of the cart array
+ quantity: updatedCart.reduce((total, item) => total + item.quantity, 0),
+ totalPrice: updatedCart.reduce(
+ (total, item) => total + item.quantity * item.price,
+ 0
+ ),
};
- // const response = await fetchWrapper(
- // `${process.env.REACT_APP_SERVER}${`/api/carts/userCart/${userId}`}`,
- // 'GET'
- // );
- const data = await fetchWrapper(
- `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart/${cartId}/update`,
- 'PUT',
- formattedCartData
- // JSON.stringify(formattedCartData)
- );
- setCartDataAndCookie(data);
- return data;
+
+ try {
+ const response = await fetch(
+ `${process.env.REACT_APP_SERVER}/api/users/${userId}/cart/${cartId}/update`,
+ {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(formattedCartData),
+ }
+ );
+
+ const { message, data } = await response.json();
+ console.log('PUT: /cart -----> response message', message);
+ if (response.ok) {
+ console.log('PUT: /cart -----> response data', data);
+ setCartDataAndCookie(data); // Update your cart state and cookie here
+ } else {
+ console.error(
+ 'Failed to update cart: ',
+ data?.error || 'Error occurred'
+ );
+ // Handle errors appropriately (e.g., show an error message to the user)
+ }
+ } catch (error) {
+ console.error('Error updating cart: ', error);
+ // Handle errors appropriately (e.g., show an error message to the user)
+ }
},
- [fetchWrapper, setCartDataAndCookie]
+ [userId, setCartDataAndCookie] // dependencies array
);
const addOneToCart = useCallback(
async (cardInfo) => {
if (!cartData._id) return;
- console.log('Adding one to cart', cardInfo);
- const { quantityOfSameId, totalItems } = getCardQuantity(
- cartData,
- cardInfo.id
- );
- if (quantityOfSameId >= 3) return;
- let updatedCart = cartData.cart.map((item) =>
- item.id === cardInfo.id
- ? { ...item, quantity: item.quantity + 1 }
- : item
+
+ const existingItem = cartData?.cart?.find(
+ (item) => item.id === cardInfo.id
);
- if (!cartData.cart.some((item) => item.id === cardInfo.id)) {
- updatedCart = [...updatedCart, { ...cardInfo, quantity: 1 }];
+ const updatedExistingItem = {
+ ...cardInfo,
+ quantity: existingItem ? existingItem.quantity + 1 : 1,
+ totalPrice: existingItem
+ ? existingItem.totalPrice + existingItem.price
+ : 1,
+ };
+ const updatedCart = cartData?.cart?.map((item) => {
+ return item.id === cardInfo.id ? updatedExistingItem : item;
+ });
+
+ let newItem = {
+ ...cardInfo,
+ quantity: 1,
+ totalPrice: cardInfo.price,
+ };
+ if (!existingItem) {
+ updatedCart.push(newItem); // New item
}
- const updatedCartData = await updateCart(cartData._id, updatedCart);
- console.log('UPDATED CART DATA:', updatedCartData);
+
+ const method = existingItem ? 'PUT' : 'POST'; // Decide method based on whether the item exists
+ const updatedCartData = await updateCart(
+ cartData._id,
+ updatedCart,
+ method
+ // updatedExistingItem ? updatedExistingItem : newItem
+ );
if (updatedCartData) setCartData(updatedCartData);
},
- [cartData._id, cartData.cart, getCardQuantity, updateCart]
+ [cartData, updateCart, setCartData]
);
const removeOneFromCart = useCallback(
async (cardInfo) => {
- if (cartData.cart.some((item) => item.id === cardInfo.id)) {
- const updatedCart = cartData.cart
- .map((item) =>
- item.id === cardInfo.id && item.quantity > 0
- ? { ...item, quantity: item.quantity - 1 }
- : item
- )
- .filter((item) => item.quantity > 0);
- const updatedCartData = await updateCart(cartData._id, updatedCart);
- if (updatedCartData) setCartData(updatedCartData);
+ const existingItemIndex = cartData.cart.findIndex(
+ (item) => item.id === cardInfo.id
+ );
+
+ if (existingItemIndex === -1) {
+ console.error('Item not found in cart');
+ return; // Item not found in cart
+ }
+
+ const existingItem = cartData.cart[existingItemIndex];
+
+ // Decrement quantity or remove item from cart
+ let updatedCart;
+ let method;
+ if (existingItem.quantity > 1) {
+ // Decrement quantity by 1
+ updatedCart = cartData?.cart?.map((item, index) =>
+ index === existingItemIndex
+ ? { ...item, quantity: item.quantity - 1 }
+ : item
+ );
+ method = 'PUT'; // Update the item quantity
+ } else {
+ // Remove item from cart as its quantity will be 0
+ updatedCart = cartData.cart.filter(
+ (item, index) => index !== existingItemIndex
+ );
+ method = 'DELETE'; // Remove the item from the cart
}
+
+ // Update the cart with new data
+ const updatedCartData = await updateCart(
+ cartData._id,
+ updatedCart,
+ method
+ );
+ if (updatedCartData) setCartData(updatedCartData);
},
- [cartData._id, cartData.cart, updateCart]
+ [cartData, updateCart, setCartData] // dependencies array
);
const deleteFromCart = useCallback(
async (cardInfo) => {
@@ -236,7 +305,7 @@ export const CartProvider = ({ children }) => {
addOneToCart,
removeOneFromCart,
deleteFromCart,
- fetchUserCart,
+ fetchCartForUser: fetchUserCart,
createUserCart,
}),
[
diff --git a/src/context/ChartContext/ChartContext.jsx b/src/context/ChartContext/ChartContext.jsx
index 598219e..d50d3ac 100644
--- a/src/context/ChartContext/ChartContext.jsx
+++ b/src/context/ChartContext/ChartContext.jsx
@@ -1,4 +1,4 @@
-import { createContext, useContext, useState } from 'react';
+import { createContext, useContext, useMemo, useState } from 'react';
import {
groupAndAverageData,
convertDataForNivo2,
@@ -34,6 +34,33 @@ export const ChartProvider = ({ children }) => {
const handleChange = (e) => {
setTimeRange(e.target.value); // Update timeRange based on selection
};
+
+ const { tickValues, xFormat } = useMemo(() => {
+ let format, ticks;
+ switch (timeRange) {
+ case '2 hours':
+ format = '%H:%M';
+ ticks = 'every 15 minutes';
+ break;
+ case '24 hours':
+ format = '%H:%M';
+ ticks = 'every hour';
+ break;
+ case '7 days':
+ format = '%b %d';
+ ticks = 'every day';
+ break;
+ case '1 month':
+ format = '%b %d';
+ ticks = 'every 3 days';
+ break;
+ default:
+ format = '%b %d';
+ ticks = 'every day';
+ }
+ return { tickValues: ticks, xFormat: `time:${format}` };
+ }, [timeRange]);
+
return (
{
latestData,
timeRange,
timeRanges,
+ tickValues,
+ xFormat,
groupAndAverageData,
convertDataForNivo2,
diff --git a/src/context/ChartContext/helpers.jsx b/src/context/ChartContext/helpers.jsx
index 2850d27..c3097ce 100644
--- a/src/context/ChartContext/helpers.jsx
+++ b/src/context/ChartContext/helpers.jsx
@@ -1,4 +1,6 @@
+import { makeStyles } from '@mui/styles';
import { roundToNearestTenth } from '../Helpers';
+import { Tooltip, Typography } from '@mui/material';
export const groupAndAverageData = (data, threshold = 600000, timeRange) => {
if (!data || data.length === 0) return [];
@@ -8,6 +10,7 @@ export const groupAndAverageData = (data, threshold = 600000, timeRange) => {
// console.log('Initial cluster with first data point: ', currentCluster);
+ // loop for each data point
for (let i = 1; i < data.length; i++) {
const prevTime = new Date(data[i - 1].x).getTime();
const currentTime = new Date(data[i].x).getTime();
@@ -78,25 +81,72 @@ export const getTickValues = (timeRange) => {
return mapping[timeRange] || 'every day'; // Default to 'every week' if no match
};
+// export const convertDataForNivo2 = (rawData2) => {
+// if (!Array.isArray(rawData2) || rawData2?.length === 0) {
+// console.error('Invalid or empty rawData provided', rawData2);
+// return [];
+// }
+
+// console.log('rawData2: ', rawData2);
+// // console.log('rawData2: ', rawData2);
+// // console.log('rawData2.data: ', rawData2[0]);
+// // rawData is assumed to be an array of objects with 'label', 'x', and 'y' properties
+// switch (rawData2) {
+// case rawData2[0].x instanceof Date:
+// return rawData2.map((dataPoint) => ({
+// x: dataPoint[0]?.x, // x value is already an ISO date string
+// y: dataPoint.y, // y value
+// }));
+// // equals array
+// case typeof rawData2[0] === Array.isArray:
+// return rawData2.map((dataPoint) => ({
+// x: new Date(dataPoint.x).toISOString(), // x value is already an ISO date string
+// y: dataPoint.y, // y value
+// }));
+// default:
+// console.error(
+// 'Invalid rawData2 provided. Expected an array of objects with "x" and "y" properties',
+// rawData2
+// );
+// return [];
+// }
+// const nivoData = rawData2?.map((dataPoint) => ({
+// x: dataPoint[0]?.x, // x value is already an ISO date string
+// y: dataPoint[0]?.y, // y value
+// }));
+
+// // Wrapping the data for a single series. You can add more series similarly
+// return [
+// {
+// id: 'Averaged Data',
+// color: '#4cceac',
+// data: nivoData,
+// },
+// ];
+// };
export const convertDataForNivo2 = (rawData2) => {
- if (!Array.isArray(rawData2) || rawData2?.length === 0) {
+ if (!Array.isArray(rawData2) || rawData2.length === 0) {
console.error('Invalid or empty rawData provided', rawData2);
return [];
}
- // console.log('rawData2: ', rawData2);
- // console.log('rawData2.data: ', rawData2[0]);
- // rawData is assumed to be an array of objects with 'label', 'x', and 'y' properties
- const nivoData = rawData2[0].map((dataPoint) => ({
- x: dataPoint.x, // x value is already an ISO date string
- y: dataPoint.y, // y value
- }));
+ // Assuming rawData2 is an array of objects with 'x' and 'y' properties
+ const nivoData = rawData2?.map((dataPoint) => {
+ // Ensure the 'x' value is in ISO date string format
+ const xValue =
+ dataPoint[0]?.x instanceof Date
+ ? dataPoint[0]?.x?.toISOString()
+ : dataPoint[0]?.x;
+ const yValue = dataPoint[0]?.y; // Assuming y value is directly usable
+
+ return { x: xValue, y: yValue };
+ });
// Wrapping the data for a single series. You can add more series similarly
return [
{
- id: 'Averaged Data',
- color: '#4cceac',
+ id: 'Your Data', // Replace with a meaningful id
+ color: 'hsl(252, 70%, 50%)', // Replace with color of your choice or logic for dynamic colors
data: nivoData,
},
];
@@ -128,3 +178,36 @@ export const formatDateToString = (date) => {
.toString()
.padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
};
+
+const useStyles = makeStyles((theme) => ({
+ tooltipTarget: {
+ cursor: 'pointer', // Change the cursor to indicate it's hoverable
+ },
+}));
+
+export const ChartTooltip = ({ point, lastData, hoveredData, latestData }) => {
+ const classes = useStyles();
+ if (!point) return null;
+ const formattedTime = hoveredData
+ ? new Date(hoveredData.x).toLocaleString()
+ : new Date((latestData || lastData).x).toLocaleString();
+
+ const formattedValue = `$${
+ hoveredData ? hoveredData.y : (latestData || lastData)?.y
+ }`;
+
+ return (
+ <>
+
+
+ {formattedTime}
+
+
+
+
+ {formattedValue}
+
+
+ >
+ );
+};
diff --git a/src/context/CollectionContext/CollectionContext.jsx b/src/context/CollectionContext/CollectionContext.jsx
index d87d1b8..28e7c8c 100644
--- a/src/context/CollectionContext/CollectionContext.jsx
+++ b/src/context/CollectionContext/CollectionContext.jsx
@@ -44,7 +44,7 @@ export const CollectionProvider = ({ children }) => {
const fetchWrapper = useFetchWrapper();
const user = cookies?.authUser;
- const userId = user?.id;
+ const userId = user?.userId;
// state for the collection context
const [allCollections, setAllCollections] = useState([]);
const [collectionData, setCollectionData] = useState(initialCollectionState);
@@ -600,10 +600,10 @@ export const CollectionProvider = ({ children }) => {
}, [selectedCollection?.chartData?.allXYValues]);
// This useEffect is for fetching collections when the user ID changes
useEffect(() => {
- if (userId) {
+ if (userId && !allCollections?.length) {
fetchAndSetCollections();
}
- }, [userId, fetchAndSetCollections]);
+ }, [userId, fetchAndSetCollections, allCollections]);
const contextValue = useMemo(
() => ({
allCollections,
diff --git a/src/context/CollectionContext/helpers.jsx b/src/context/CollectionContext/helpers.jsx
index 6ed660b..5c09506 100644
--- a/src/context/CollectionContext/helpers.jsx
+++ b/src/context/CollectionContext/helpers.jsx
@@ -326,6 +326,10 @@ export const handleCardAddition = (currentCards, cardToAdd) => {
const existingCard = currentCards[existingCardIndex];
existingCard.quantity = (existingCard.quantity || 0) + 1;
existingCard.totalPrice = existingCard.price * existingCard.quantity;
+ existingCard.chart_datasets = [
+ ...existingCard.chart_datasets,
+ createChartDataEntry(existingCard.totalPrice),
+ ];
// Update the card in the currentCards array
currentCards[existingCardIndex] = existingCard;
diff --git a/src/context/CollectionContext/useCollectionManager.jsx b/src/context/CollectionContext/useCollectionManager.jsx
new file mode 100644
index 0000000..461f07e
--- /dev/null
+++ b/src/context/CollectionContext/useCollectionManager.jsx
@@ -0,0 +1,39 @@
+import { useState, useEffect, useCallback } from 'react';
+import axios from 'axios'; // Make sure to install axios for HTTP requests
+
+// Custom hook for managing the collection
+function useCollectionManager(userId) {
+ const [collection, setCollection] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ // Fetch collection by user ID
+ const fetchCollection = useCallback(async () => {
+ setLoading(true);
+ try {
+ const response = await axios.get(`/api/collections/${userId}`);
+ setCollection(response.data); // Assume the data is the collection object
+ setLoading(false);
+ } catch (err) {
+ setError(err);
+ setLoading(false);
+ }
+ }, [userId]);
+
+ // Call fetchCollection when the component using this hook mounts
+ useEffect(() => {
+ fetchCollection();
+ }, [fetchCollection]);
+
+ // Define other CRUD operations similarly, e.g., addCard, removeCard, updateCollection, etc.
+
+ return {
+ collection,
+ loading,
+ error,
+ fetchCollection,
+ // add other methods here as you define them
+ };
+}
+
+export default useCollectionManager;
diff --git a/src/context/CombinedContext/CombinedProvider.jsx b/src/context/CombinedContext/CombinedProvider.jsx
index 79888dc..ce0fac6 100644
--- a/src/context/CombinedContext/CombinedProvider.jsx
+++ b/src/context/CombinedContext/CombinedProvider.jsx
@@ -22,7 +22,7 @@ export const CombinedProvider = ({ children }) => {
const [cookies] = useCookies(['authUser']);
const [state, setState] = useState(initialState);
const user = cookies?.authUser;
- const userId = user?.id;
+ const userId = user?.userId;
const {
selectedCollection,
updateCollection,
diff --git a/src/context/DeckContext/DeckContext.js b/src/context/DeckContext/DeckContext.js
index 5c60ea7..33075ef 100644
--- a/src/context/DeckContext/DeckContext.js
+++ b/src/context/DeckContext/DeckContext.js
@@ -23,13 +23,13 @@ export const DeckContext = createContext(defaultContextValue);
export const DeckProvider = ({ children }) => {
const [cookies] = useCookies(['authUser']);
+ const userId = cookies?.authUser?.userId;
const fetchWrapper = useFetchWrapper();
-
+ const [prevUserId, setPrevUserId] = useState(null);
const [deckData, setDeckData] = useState({});
const [allDecks, setAllDecks] = useState([]);
const [selectedDeck, setSelectedDeck] = useState({});
const [selectedCards, setSelectedCards] = useState(selectedDeck?.cards || []);
- const userId = cookies?.authUser?.id;
const fetchDecksForUser = useCallback(async () => {
if (!userId) {
@@ -46,15 +46,17 @@ export const DeckProvider = ({ children }) => {
}, [userId]);
const fetchAndSetDecks = useCallback(async () => {
try {
- const userDecks = await fetchDecksForUser();
+ const { message, data } = await fetchDecksForUser();
+ console.log('Response from server for fetch decks:', message, data);
- if (userDecks.data && userDecks.data.length > 0) {
- const uniqueDecks = removeDuplicateDecks(userDecks.data);
+ if (data && data?.length > 0) {
+ const uniqueDecks = removeDuplicateDecks(data);
setAllDecks((prevDecks) =>
removeDuplicateDecks([...prevDecks, ...uniqueDecks])
);
setDeckData(uniqueDecks[0] || {});
} else {
+ console.log('No decks found for user.', data.data);
// No decks found for user
const shouldCreateDeck = window.confirm(
'No decks found. Would you like to create a new one?'
@@ -130,7 +132,6 @@ export const DeckProvider = ({ children }) => {
console.error(`Failed to update deck in backend: ${error.message}`);
}
};
-
const createUserDeck = async (userId, newDeckInfo) => {
try {
const url = `${BASE_API_URL}/${userId}/decks/createDeck`;
@@ -141,10 +142,10 @@ export const DeckProvider = ({ children }) => {
const data = await fetchWrapper(url, 'POST', {
cards: [],
totalPrice: 0,
- description: newDeckInfo.updatedInfo.description || '',
- name: newDeckInfo.updatedInfo.name || '',
- tags: newDeckInfo.updatedInfo.tags || [],
- color: newDeckInfo.updatedInfo.color || '',
+ description: newDeckInfo?.description || '',
+ name: newDeckInfo?.name || '',
+ tags: newDeckInfo?.tags || [],
+ color: newDeckInfo?.color || '',
});
console.log('NEW DECK DATA:', data);
setDeckData(data.data);
@@ -185,7 +186,7 @@ export const DeckProvider = ({ children }) => {
if (!deckId) {
setSelectedDeck(allDecks[0]);
console.warn('No deck ID provided. Adding card to first deck in list.');
- deckId = allDecks[0]._id;
+ deckId = allDecks[0]?._id;
}
try {
// Update deck data locally
@@ -227,7 +228,7 @@ export const DeckProvider = ({ children }) => {
const updatedDeck = responseData.allDecks.find(
(deck) => deck._id === deckId
).cards;
- const updatedCards = updatedDeck.cards;
+ const updatedCards = updatedDeck?.cards;
setSelectedDeck({ ...selectedDeck, cards: updatedCards });
}
} catch (error) {
@@ -246,7 +247,7 @@ export const DeckProvider = ({ children }) => {
throw new Error('Deck not found locally');
}
- const originalCard = currentDeck.cards.find(
+ const originalCard = currentDeck?.cards?.find(
(card) => card.id === updatedCard.id
);
if (originalCard && originalCard.quantity === updatedCard.quantity) {
@@ -312,9 +313,14 @@ export const DeckProvider = ({ children }) => {
}
};
const getCardQuantity = (cardId) => {
- const foundCard = selectedDeck?.cards?.find((item) => item.id === cardId);
+ const foundCard = selectedDeck?.cards.find((item) => item.id === cardId);
return foundCard?.quantity || 0;
};
+ const shouldFetchDecks = (prevUserId, currentUserId) => {
+ // Fetch decks if there was no previous user and now there is one
+ // or if the user has changed
+ return (!prevUserId && currentUserId) || prevUserId !== currentUserId;
+ };
const contextValue = {
deckData,
@@ -349,8 +355,16 @@ export const DeckProvider = ({ children }) => {
useEffect(() => {
console.log('DECKCONTEXT:', contextValue);
- if (userId && typeof userId === 'string') {
+ }, [
+ contextValue.deckData,
+ contextValue.allDecks,
+ contextValue.selectedDeck,
+ contextValue.selectedCards,
+ ]);
+ useEffect(() => {
+ if (shouldFetchDecks(prevUserId, userId)) {
fetchAndSetDecks();
+ setPrevUserId(userId); // Update the previous userId
}
}, [userId, fetchAndSetDecks]);
diff --git a/src/context/FormContext/FormContext.jsx b/src/context/FormContext/FormContext.jsx
index 8c9406c..cd6399f 100644
--- a/src/context/FormContext/FormContext.jsx
+++ b/src/context/FormContext/FormContext.jsx
@@ -1,6 +1,10 @@
-import React, { createContext, useState, useContext } from 'react';
+import React, { createContext, useState, useContext, useEffect } from 'react';
import { useAuthContext } from '../AuthContext/authContext';
import { usePageContext } from '../PageContext/PageContext';
+import { useCardStore } from '../CardContext/CardStore';
+// import { params: initialState } from './search.json';
+// import as dif name from ./search.json
+import { initialState } from './search.json';
import { defaultContextValue } from './helpers';
// Define the context
@@ -32,6 +36,14 @@ const formValidations = {
// ... more validations
return errors;
},
+ updateCollectionForm: (values) => {
+ let errors = {};
+ // Example: Add validations specific to collection update form
+ if (!values.name) errors.name = 'Collection name is required';
+ if (!values.description) errors.description = 'Description is required';
+ // ... more validations
+ return errors;
+ },
// ...other form-specific validations
};
// Initial state for different forms
@@ -79,8 +91,21 @@ const initialFormStates = {
name: '',
description: '',
},
+ addCollectionForm: {
+ // New form with same initial values as updateCollectionForm
+ name: '',
+ description: '',
+ },
searchForm: {
searchTerm: '',
+ searchParams: {
+ name: '',
+ type: '',
+ race: '',
+ archetype: '',
+ attribute: '',
+ level: '',
+ },
},
customerInfoFields: {
firstName: '',
@@ -108,6 +133,7 @@ export const FormProvider = ({ children }) => {
setLoading, // Use setLoading instead of individual state setters
returnDisplay,
} = usePageContext();
+ // const { handleRequest } = useCardStore();
const [forms, setForms] = useState(initialFormStates);
const [currentForm, setCurrentForm] = useState({}); // For multiple forms
const [formErrors, setFormErrors] = useState(null); // For a single form
@@ -192,6 +218,12 @@ export const FormProvider = ({ children }) => {
setLoading('isFormDataLoading', false); // indicate form submission is done
};
+ // useEffect(() => {
+ // if (initialFormStates?.searchForm?.searchTerm) {
+ // handleRequest(initialFormStates?.searchForm?.searchTerm);
+ // }
+ // }, [returnDisplay]);
+
// Provide each form's data, handleChange, and handleSubmit through context
const contextValue = {
forms,
diff --git a/src/components/other/search/search.json b/src/context/FormContext/search.json
similarity index 100%
rename from src/components/other/search/search.json
rename to src/context/FormContext/search.json
diff --git a/src/context/Providers.jsx b/src/context/Providers.jsx
index cd20e95..1f84520 100644
--- a/src/context/Providers.jsx
+++ b/src/context/Providers.jsx
@@ -26,6 +26,7 @@ import {
useMode,
PageProvider,
ErrorBoundary,
+ CardImagesProvider,
// CardImagesProvider, // Uncomment if CardImagesProvider is used
} from '.'; // Ensure this path is correctly pointing to where your context providers are defined
@@ -40,11 +41,11 @@ const Providers = ({ children }) => {
-
-
-
-
-
+
+
+
+
+
@@ -52,24 +53,26 @@ const Providers = ({ children }) => {
- {/* */}
-
-
-
-
-
- {/* */}
-
-
- {children}{' '}
-
- {/* */}
-
-
-
-
-
- {/* */}
+
+
+
+
+
+
+ {/* */}
+
+
+ {children}{' '}
+
+ {/* */}
+
+
+
+
+
+
@@ -77,11 +80,11 @@ const Providers = ({ children }) => {
-
-
-
-
-
+
+
+
+
+
diff --git a/src/context/SideBarContext/SideBarProvider.jsx b/src/context/SideBarContext/SideBarProvider.jsx
index c12f22b..34fa916 100644
--- a/src/context/SideBarContext/SideBarProvider.jsx
+++ b/src/context/SideBarContext/SideBarProvider.jsx
@@ -1,10 +1,11 @@
import React, { createContext, useContext, useState } from 'react';
+import { useAuthContext } from '../AuthContext/authContext';
const SidebarContext = createContext();
export const SidebarProvider = ({ children }) => {
+ const { login, logout, isLoggedIn } = useAuthContext();
const [isOpen, setIsOpen] = useState(false);
- const [isLoggedIn, setisLoggedIn] = useState(false);
const [sidebarBackgroundColor, setSidebarBackgroundColor] =
useState('#FFFFFF');
const [sidebarImage, setSidebarImage] = useState(null);
@@ -13,14 +14,6 @@ export const SidebarProvider = ({ children }) => {
setIsOpen(!isOpen);
};
- const login = () => {
- setisLoggedIn(true);
- };
-
- const logout = () => {
- setisLoggedIn(false);
- };
-
return (
useContext(StatisticsContext);
export const StatisticsProvider = ({ children }) => {
const { timeRange } = useChartContext();
const { allCollections, allXYValues } = useCollectionStore();
+ const [selectedStat, setSelectedStat] = useState('');
+ // memoized status regarding the price history of a user's collection
const stats = useMemo(
() => calculateStatistics({ data: allXYValues }, timeRange, allCollections),
[allXYValues, timeRange]
);
+ // memoized stats regarding the price history of all of a user's collections
const statsByCollectionId = useMemo(() => {
- return allCollections.reduce((acc, collection) => {
+ return allCollections?.reduce((acc, collection) => {
// Assuming each collection has its own 'currentChartDataSets2'
const data = collection?.chartData?.allXYValues;
acc[collection._id] = calculateStatistics({ data }, timeRange);
return acc;
}, {});
}, [allCollections, timeRange]);
+ // Prepare markers for high and low points
+ const markers = [
+ {
+ axis: 'y',
+ value: statsByCollectionId?.highPoint,
+ lineStyle: { stroke: '#b0413e', strokeWidth: 2 },
+ legend: 'High Point',
+ legendOrientation: 'vertical',
+ },
+ {
+ axis: 'y',
+ value: statsByCollectionId?.lowPoint,
+ lineStyle: { stroke: '#b0413e', strokeWidth: 2 },
+ legend: 'Low Point',
+ legendOrientation: 'vertical',
+ },
+ {
+ axis: 'y',
+ value: statsByCollectionId?.average,
+ lineStyle: { stroke: '#b0413e', strokeWidth: 2 },
+ legend: 'Average',
+ legendOrientation: 'vertical',
+ },
+ ];
+
+ // Calculate the total value of all collections
+ const totalValue = allCollections?.reduce(
+ (acc, collection) => acc + collection.totalPrice,
+ 0
+ );
+ const topFiveCards = allCollections
+ .flatMap((collection) => collection.cards) // Flatten all cards into one array
+ .sort((a, b) => b.price - a.price) // Sort by price in descending order
+ .slice(0, 5);
+ const chartData = allCollections?.map((collection) => ({
+ id: collection.id,
+ value: collection.totalPrice,
+ label: collection.name,
+ }));
return (
-
+
{children}
);
diff --git a/src/context/UserContext/UserContext.js b/src/context/UserContext/UserContext.js
index 663e56b..c8204e6 100644
--- a/src/context/UserContext/UserContext.js
+++ b/src/context/UserContext/UserContext.js
@@ -12,18 +12,23 @@ import useFetchWrapper from '../hooks/useFetchWrapper';
export const UserContext = createContext();
export const UserProvider = ({ children }) => {
- const { authUser, user, setUser, isLoggedIn } = useAuthContext(); // Use the useAuthContext hook
+ const { setUser, isLoggedIn } = useAuthContext(); // Use the useAuthContext hook
+ const [cookies, setCookie] = useCookies(['authUser', 'user']);
const fetchWrapper = useFetchWrapper();
- const userId = authUser?.id;
-
- // const fetchUserData = useCallback(async () => {
- // // Get request to fetch user data
- // const endpoint = `/users/${user.id}/userData`;
- // const url = createUrl(endpoint);
- // const response = await useFetchWrapper(url);
- // // const response = await fetchWrapper.get(`/api/users/${userId}`);
- // // const userData = response.data;
- // }, []);
+ const authUser = cookies?.authUser;
+ const user = cookies?.user;
+ const userId = cookies?.authUser?.userId;
+ const fetchUserData = useCallback(async () => {
+ // Get request to fetch user data
+ const endpoint = `/users/${userId}/userData`;
+ const url = createUrl(endpoint);
+ const response = await fetchWrapper(url, 'GET');
+ const { message, data } = response;
+ console.log('Response from server for fetch user:', message, data);
+ // setUser(data.userDoc);
+ // const response = await fetchWrapper.get(`/api/users/${userId}`);
+ // const userData = response.data;
+ }, []);
const updateUser = async (updatedUser) => {
try {
@@ -31,8 +36,9 @@ export const UserProvider = ({ children }) => {
const endpoint = `users/${userId}/userData/update`;
const url = createUrl(endpoint);
const response = await fetchWrapper(url, 'PUT', updatedUser);
- const updatedUserResponse = response?.data?.user;
- setUser(updatedUserResponse);
+ const { message, data } = response;
+ console.log('Response from server for update user:', message, data);
+ setUser(data.updatedUserDoc);
console.log('User Data Sent to Server and Cookie Updated:', updatedUser);
} catch (error) {
console.error('Error updating user data:', error);
@@ -41,6 +47,7 @@ export const UserProvider = ({ children }) => {
useEffect(() => {
if (userId) {
+ console.log('User ID found, fetching user data...', cookies.user);
const updatedUser = {
...user,
userSecurityData: {
@@ -59,10 +66,11 @@ export const UserProvider = ({ children }) => {
return (
{children}
diff --git a/src/context/WithPageProvider.jsx b/src/context/WithPageProvider.jsx
index 526e307..cece8d3 100644
--- a/src/context/WithPageProvider.jsx
+++ b/src/context/WithPageProvider.jsx
@@ -1,13 +1,15 @@
import React from 'react';
-import { PageProvider } from '.';
+import { FormProvider, PageProvider } from '.';
// Higher Order Component for PageProvider
const WithPageProvider = (WrappedComponent) => {
const WithPageProvider = (props) => {
return (
-
-
-
+
+
+
+
+
);
};
return WithPageProvider;
diff --git a/src/context/hooks/useDialog.jsx b/src/context/hooks/useDialog.jsx
index 7f808f4..a0a27c8 100644
--- a/src/context/hooks/useDialog.jsx
+++ b/src/context/hooks/useDialog.jsx
@@ -1,32 +1,197 @@
-// useDialog.js
-import { useState, useCallback } from 'react';
-
-const useDialog = (handleSnackBar, onClose) => {
- const [isOpen, setIsOpen] = useState(false);
-
- const openDialog = useCallback(() => {
- setIsOpen(true);
- }, []);
-
- const closeDialog = useCallback(() => {
- setIsOpen(false);
- onClose?.();
- }, [onClose]);
-
- const handleCloseDialog = useCallback(
- (event, reason) => {
- if (
- reason &&
- (reason === 'backdropClick' || reason === 'escapeKeyDown')
- ) {
- handleSnackBar('Operation cancelled, no changes were made.');
+import { useState, useCallback, useEffect } from 'react';
+
+const useDialog = (handleSnackBar) => {
+ const [isLoginDialogOpen, setLoginDialogOpen] = useState(false);
+ const [dialogStatus, setDialogStatus] = useState({
+ isOpen: false,
+ event: '',
+ });
+ const [dialogs, setDialogs] = useState({});
+
+ // Function to open a dialog
+ const openDialog = useCallback(
+ (dialogName, action) => {
+ setDialogs((prevDialogs) => ({
+ ...prevDialogs,
+ [dialogName]: true,
+ }));
+ setLoginDialogOpen(true);
+ console.log(`${dialogName} Dialog attempting ${action}`);
+ handleSnackBar(`${dialogName} Dialog attempting ${action}`, 'info', 6000);
+ // console.log(`${dialogName} Dialog Status: Open`);
+ },
+ [handleSnackBar]
+ );
+
+ // Function to close a dialog
+ const closeDialog = useCallback(
+ (dialogName, action, reason) => {
+ if (reason === 'backdropClick' || reason === 'escapeKeyDown') {
+ handleSnackBar(
+ 'Operation cancelled, no changes were made.',
+ 'warning',
+ 6000
+ );
+ } else {
+ setLoginDialogOpen(false);
+ console.log(`${dialogName} Dialog attempting ${action}`);
+ handleSnackBar(
+ `${dialogName} Dialog attempting ${action}`,
+ 'info',
+ 6000
+ );
+ // console.log(`${dialogName} Dialog Status: Closed`);
}
- closeDialog();
+ setDialogs((prevDialogs) => ({
+ ...prevDialogs,
+ [dialogName]: false,
+ }));
},
- [closeDialog, handleSnackBar]
+ [handleSnackBar]
);
- return { isOpen, openDialog, handleCloseDialog, closeDialog };
+ // Track dialog status changes
+ useEffect(() => {
+ Object.entries(dialogs).forEach(([dialogName, isOpen]) => {
+ console.log(`${dialogName} Dialog Status: ${isOpen ? 'Open' : 'Closed'}`);
+ });
+ }, [dialogs]);
+
+ return {
+ isLoginDialogOpen,
+ openLoginDialog: () => openDialog('Login', 'Open'),
+ closeLoginDialog: (event, reason) => closeDialog('Login', 'Close', reason),
+ };
};
export default useDialog;
+// // console.log('isLoggedIn:', isLoggedIn);
+// console.log('isLoginDialogOpen:', isLoginDialogOpen);
+// // console.log('handleSnackBar:', handleSnackBar);
+// // useEffect(() => {
+// // if (!isLoggedIn && isLoginDialogOpen) {
+// // handleSnackBar('Please log in to continue', 'info');
+// // }
+// // }, [isLoggedIn, isLoginDialogOpen, handleSnackBar]);
+
+// // const handleToggleLoginDialog = useCallback(
+// // (event) => {
+// // if (event === 'Open') {
+// // setLoginDialogOpen(true);
+// // handleSnackBar('Opening login dialog...', 'info');
+// // } else if (event === 'Close') {
+// // setLoginDialogOpen(false);
+// // handleSnackBar('Closing login dialog...', 'info');
+// // } else {
+// // // Handle backdrop click or escape key down, or toggle dialog for other cases
+// // if (event === 'backdropClick' || event === 'escapeKeyDown') {
+// // handleSnackBar(
+// // 'Operation cancelled, no changes were made.',
+// // 'warning'
+// // );
+// // } else {
+// // setLoginDialogOpen((prev) => {
+// // const newStatus = !prev;
+// // handleSnackBar(
+// // newStatus ? 'Dialog opened' : 'Dialog closed',
+// // 'info'
+// // );
+// // return newStatus;
+// // });
+// // }
+// // }
+// // },
+// // [handleSnackBar]
+// // );
+// const openLoginDialog = useCallback(() => {
+// setLoginDialogOpen(true);
+// handleSnackBar('Login Dialog opened', 'info', 6000); // Notify on dialog open
+// }, [handleSnackBar]);
+
+// const closeLoginDialog = useCallback(
+// (event, reason) => {
+// if (
+// reason &&
+// (reason === 'backdropClick' || reason === 'escapeKeyDown')
+// ) {
+// handleSnackBar(
+// 'Operation cancelled, no changes were made.',
+// 'warning',
+// 6000
+// );
+// } else {
+// handleSnackBar('Login Dialog closed', 'info', 6000); // Notify on dialog close
+// }
+// setLoginDialogOpen(false);
+// },
+// [handleSnackBar]
+// );
+
+// useEffect(() => {
+// if (isLoginDialogOpen) {
+// setDialogStatus({ isOpen: true, event: 'Open' });
+// console.log('Dialog Successfully Opened');
+// }
+// if (!isLoginDialogOpen) {
+// setDialogStatus({ isOpen: false, event: 'Close' });
+// console.log('Dialog Successfully Closed');
+// }
+
+// if (dialogStatus.event === 'Open') {
+// handleSnackBar('Login Dialog opened', 'info', 6000); // Notify on dialog open
+// }
+// if (dialogStatus.event === 'Close') {
+// handleSnackBar('Login Dialog closed', 'info', 6000); // Notify on dialog close
+// }
+// });
+// const [isOpen, setIsOpen] = useState(false);
+
+// const openDialog = useCallback(() => {
+// setIsOpen(true);
+// }, []);
+
+// const closeDialog = useCallback(() => {
+// setIsOpen(false);
+// onClose?.();
+// }, [onClose]);
+
+// const openLoginDialog = useCallback(() => {
+// setLoginDialogOpen(true);
+// }, []);
+
+// const closeLoginDialog = useCallback(() => {
+// setLoginDialogOpen(false);
+// }, []);
+
+// toggleLoginDialog combines openLoginDialog and closeLoginDialog
+// const handleCloseDialog = useCallback(
+// (event, reason) => {
+// if (
+// reason &&
+// (reason === 'backdropClick' || reason === 'escapeKeyDown')
+// ) {
+// handleSnackBar('Operation cancelled, no changes were made.');
+// }
+// closeDialog();
+// },
+// [closeDialog, handleSnackBar]
+// );
+
+// example usage of handleToggleLoginDialog:
+//
+// const handleCloseLoginDialog = useCallback(
+// (event, reason) => {
+// if (
+// reason &&
+// (reason === 'backdropClick' || reason === 'escapeKeyDown')
+// ) {
+// handleSnackBar('Operation cancelled, no changes were made.');
+// }
+// toggleLoginDialog();
+// },
+// [toggleLoginDialog, handleSnackBar]
+// );
diff --git a/src/context/hooks/useFetchAndDisplayImage.jsx b/src/context/hooks/useFetchAndDisplayImage.jsx
new file mode 100644
index 0000000..7770f36
--- /dev/null
+++ b/src/context/hooks/useFetchAndDisplayImage.jsx
@@ -0,0 +1,29 @@
+import { useState, useEffect } from 'react';
+import { useCardImages } from '../CardImagesContext/CardImagesContext';
+
+function useFetchAndDisplayImage(imageUrl) {
+ const { downloadCardImage } = useCardImages();
+ const [imageSrc, setImageSrc] = useState(null);
+ const [error, setError] = useState(null);
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ if (!imageUrl) return;
+
+ setLoading(true); // Indicate the start of an image download
+ downloadCardImage(imageUrl) // This function should handle the API request
+ .then((downloadedImgUrl) => {
+ setImageSrc(downloadedImgUrl); // Update state with the new image URL
+ setLoading(false); // Indicate that the image download is complete
+ })
+ .catch((error) => {
+ console.error('Error fetching image:', error);
+ setError(error);
+ setLoading(false); // Indicate that the image download is complete even if there was an error
+ });
+ }, [imageUrl, downloadCardImage]); // Dependency array includes downloadCardImage to handle updates to the function
+
+ return { imageSrc, error, loading };
+}
+
+export default useFetchAndDisplayImage;
diff --git a/src/context/hooks/useResponsiveStyles.jsx b/src/context/hooks/useResponsiveStyles.jsx
index 415f953..6840597 100644
--- a/src/context/hooks/useResponsiveStyles.jsx
+++ b/src/context/hooks/useResponsiveStyles.jsx
@@ -45,7 +45,7 @@ const useResponsiveStyles = (theme) => {
}
};
- const getProductGridContainerStyle = () => ({
+ const getProductGridContainerStyle = (theme) => ({
maxWidth: 'lg',
maxHeight: '100%',
display: 'flex',
diff --git a/src/context/index.js b/src/context/index.js
index ddb2b1d..28f2546 100644
--- a/src/context/index.js
+++ b/src/context/index.js
@@ -37,6 +37,6 @@ export { AppContextProvider } from './AppContext/AppContextProvider';
export { PopoverProvider } from './PopoverContext/PopoverContext';
export { CronJobProvider } from './CronJobContext/CronJobContext';
export { StatisticsProvider } from './StatisticsContext/StatisticsContext';
-export { CardImagesProvider } from './CardImagesContext/CardImagesContext';
export { FormProvider } from './FormContext/FormContext';
export { PageProvider } from './PageContext/PageContext';
+export { CardImagesProvider } from './CardImagesContext/CardImagesContext';
diff --git a/src/components/cards/CardDetailsContainer.jsx b/src/layout/CardDetailsContainer.jsx
similarity index 95%
rename from src/components/cards/CardDetailsContainer.jsx
rename to src/layout/CardDetailsContainer.jsx
index 651a464..2587f16 100644
--- a/src/components/cards/CardDetailsContainer.jsx
+++ b/src/layout/CardDetailsContainer.jsx
@@ -8,9 +8,9 @@ import {
FaVenusMars,
} from 'react-icons/fa';
import { GiAxeSword } from 'react-icons/gi';
-import CardDetail from './CardDetail';
+import CardDetail from '../components/cards/CardDetail';
import { styled } from '@mui/system';
-import { useMode } from '../../context';
+import { useMode } from '../context';
const IconWrapper = styled('div')(({ theme }) => ({
display: 'flex',
diff --git a/src/components/other/dataDisplay/UserStats.jsx b/src/layout/UserStats.jsx
similarity index 76%
rename from src/components/other/dataDisplay/UserStats.jsx
rename to src/layout/UserStats.jsx
index 0cb073a..4943611 100644
--- a/src/components/other/dataDisplay/UserStats.jsx
+++ b/src/layout/UserStats.jsx
@@ -1,8 +1,8 @@
import React from 'react';
import { Box, Typography } from '@mui/material';
-import { useDeckStore } from '../../../context/DeckContext/DeckContext';
-import { useCartStore } from '../../../context/CartContext/CartContext';
-import { useCollectionStore } from '../../../context/CollectionContext/CollectionContext';
+import { useDeckStore } from '../context/DeckContext/DeckContext';
+import { useCartStore } from '../context/CartContext/CartContext';
+import { useCollectionStore } from '../context/CollectionContext/CollectionContext';
const UserStats = () => {
const { allDecks } = useDeckStore();
diff --git a/src/layout/cart/CartContent.js b/src/layout/cart/CartContent.js
index 6061312..c4a97d7 100644
--- a/src/layout/cart/CartContent.js
+++ b/src/layout/cart/CartContent.js
@@ -11,7 +11,7 @@ import { useMode } from '../../context';
const CartContent = () => {
const { theme } = useMode();
const { getProductGridContainerStyle } = useResponsiveStyles(theme);
- const containerStyles = getProductGridContainerStyle();
+ const containerStyles = getProductGridContainerStyle(theme);
const { cartData, isLoading } = useCartStore();
const renderCartItems = () => {
diff --git a/src/layout/collection/SelectCollection.jsx b/src/layout/collection/SelectCollection.jsx
index dc2f5c1..65e6c67 100644
--- a/src/layout/collection/SelectCollection.jsx
+++ b/src/layout/collection/SelectCollection.jsx
@@ -1,16 +1,14 @@
-// SelectCollection.jsx
import React, { useState, useCallback } from 'react';
-import { Box, Button, Grid, Paper, Typography } from '@mui/material';
+import { Box, Grid } from '@mui/material';
import SelectCollectionList from '../../components/grids/collectionGrids/SelectCollectionList';
-import CreateOrEditCollectionDialog from '../../components/dialogs/CreateOrEditCollectionDialog';
import { useCollectionStore } from '../../context/CollectionContext/CollectionContext';
import usePortfolioStyles from '../../context/hooks/usePortfolioStyles';
import { useMode, useStatisticsStore } from '../../context';
-import { PieChart } from '@mui/x-charts/PieChart';
import SelectCollectionHeader from '../../components/headings/collection/SelectCollectionHeader';
import PieChartStats from '../../components/other/dataDisplay/chart/PieChartStats';
import TotalValueOfCollectionsDisplay from '../../components/other/dataDisplay/TotalValueOfCollectionsDisplay';
import TopFiveExpensiveCards from '../../components/other/dataDisplay/TopFiveExpensiveCards';
+import CollectionDialog from '../../components/dialogs/CollectionDialog';
const SelectCollection = ({ handleSelectCollection }) => {
const { theme } = useMode();
@@ -19,75 +17,77 @@ const SelectCollection = ({ handleSelectCollection }) => {
const [isNew, setIsNew] = useState(false);
const { setSelectedCollection, selectedCollection, allCollections } =
useCollectionStore();
- const { statsByCollectionId } = useStatisticsStore();
const [isLoadingNewCollection, setIsLoadingNewCollection] = useState(false);
+ const { topFiveCards, totalValue, chartData } = useStatisticsStore();
const handleDialogToggle = useCallback(() => {
- setDialogOpen(!isDialogOpen);
- }, [isDialogOpen]);
+ setDialogOpen((prev) => !prev);
+ }, []);
const handleSave = useCallback(
(collection) => {
setSelectedCollection(collection);
- setIsLoadingNewCollection(true);
-
- // Simulate a delay for adding collection (replace with actual API call or logic)
- setTimeout(() => {
- setSelectedCollection(collection);
- setIsLoadingNewCollection(false); // Set loading state false once added
- setDialogOpen(false);
- }, 1000); // Simulate network or processing delay
+ setDialogOpen(false);
+ handleSelectCollection(collection);
},
- [setSelectedCollection]
+ [setSelectedCollection, handleSelectCollection]
);
+
const openNewDialog = () => {
setIsNew(true);
setDialogOpen(true);
};
- // Calculate the total value of all collections
- const totalValue = allCollections.reduce(
- (acc, collection) => acc + collection.totalPrice,
- 0
+
+ // Function to render the header
+ const renderHeader = () => (
+
);
- const topFiveCards = allCollections
- .flatMap((collection) => collection.cards) // Flatten all cards into one array
- .sort((a, b) => b.price - a.price) // Sort by price in descending order
- .slice(0, 5);
- const chartData = allCollections.map((collection) => ({
- id: collection.id,
- value: collection.totalPrice,
- label: collection.name,
- }));
- return (
-
-
-
-
-
-
-
-
-
-
- (
+
+
+
+
+
+ );
+
+ // Function to render the collection list
+ const renderCollectionList = () => (
+
+
);
+
+ // Function to render the dialog for collection
+ const renderCollectionDialog = () => (
+
+ );
+
+ return (
+
+ {renderHeader()}
+ {renderStatistics()}
+ {renderCollectionList()}
+ {renderCollectionDialog()}
+
+ );
};
export default SelectCollection;
diff --git a/src/pages/CartPage.js b/src/pages/CartPage.js
index c993f8a..10b36fe 100644
--- a/src/pages/CartPage.js
+++ b/src/pages/CartPage.js
@@ -16,18 +16,20 @@ const CartPage = () => {
cartData,
addOneToCart,
removeOneFromCart,
- fetchUserCart,
+ fetchCartForUser,
getTotalCost,
cartCardQuantity,
totalCost,
} = useCartStore();
const { loadingStatus, returnDisplay, setLoading } = usePageContext();
+ const calculateTotalPrice = getTotalCost();
+ // useEffect hook to fetch cart data for user
useEffect(() => {
const fetchData = async () => {
setLoading('isPageLoading', true);
try {
- await fetchUserCart(); // Assuming fetchUserCart updates cartData
+ await fetchCartForUser(); // Assuming fetchUserCart updates cartData
} catch (error) {
console.error('Error fetching cart data:', error);
} finally {
@@ -39,8 +41,7 @@ const CartPage = () => {
if (!cartData) {
fetchData();
}
- }, [cartData, fetchUserCart, setLoading]);
-
+ }, [cartData, fetchCartForUser]);
// Modify this function based on how your cart store manages items
const handleModifyItemInCart = async (cardId, operation) => {
try {
@@ -49,12 +50,63 @@ const CartPage = () => {
console.error('Failed to adjust quantity in cart:', e);
}
};
+ // Function to render the cart content grid
+ const renderCartContent = () => (
+
+
+
+ );
+ // Function to render the checkout and summary section
+ const renderCheckoutAndSummary = () => (
+
+
+
+
+
+
+ );
+ // Function to render the overall cart layout
+ const renderCartLayout = () => (
+
+
+
+ {renderCartContent()}
+ {renderCheckoutAndSummary()}
+
+
+
+ );
- const calculateTotalPrice = getTotalCost();
return (
{loadingStatus?.isPageLoading && returnDisplay()}
-
+ {loadingStatus?.isLoading && returnDisplay()}
{
backgroundColor: theme.palette.background.paper,
}}
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ {renderCartLayout()}
);
};
-
export default CartPage;
diff --git a/src/pages/CollectionPage.js b/src/pages/CollectionPage.js
index 4816f5c..5a5e086 100644
--- a/src/pages/CollectionPage.js
+++ b/src/pages/CollectionPage.js
@@ -34,36 +34,42 @@ const CollectionPage = () => {
}
}, []);
- // if (isLoading) displayLoadingIndicator();
- // if (pageError) displayErrorIndicator();
+ // Function to render the hero center
+ const renderHeroCenter = () =>
+ !isCollectionView && (
+
+ );
+
+ // Function to render the collection portfolio
+ const renderCollectionPortfolio = () => (
+
+ );
+
+ // Function to render the card dialog
+ const renderCardDialog = () =>
+ isModalOpen && (
+
+ );
- // const handleCollectionSelected = (selected) => {
- // setIsCollectionSelected(!!selected);
- // };
return (
{loadingStatus?.isPageLoading && returnDisplay()}
-
- {!isCollectionView && (
-
- )}
-
- {isModalOpen && (
-
- )}
+ {renderHeroCenter()}
+ {renderCollectionPortfolio()}
+ {renderCardDialog()}
);
};
diff --git a/src/pages/DeckBuilderPage.js b/src/pages/DeckBuilderPage.js
index e809312..072df15 100644
--- a/src/pages/DeckBuilderPage.js
+++ b/src/pages/DeckBuilderPage.js
@@ -69,39 +69,64 @@ const DeckBuilderPage = () => {
}
}, [userId]);
+ // Function to render the Hero banner
+ const renderHeroBanner = () => (
+
+
+
+
+
+ );
+
+ // Function to render the deck search section
+ const renderDeckSearch = () => (
+
+
+
+ );
+
+ // Function to render the deck display section
+ const renderDeckDisplay = () => (
+
+
+
+ );
+
+ // Function to render the deck builder's main content
+ const renderDeckBuilderContent = () => (
+
+
+
+ {renderDeckSearch()}
+ {renderDeckDisplay()}
+
+
+
+ );
+
+ // Function to render the card dialog
+ const renderCardDialog = () => {
+ return (
+ isModalOpen && (
+
+ )
+ );
+ };
+
return (
{loadingStatus?.isPageLoading && returnDisplay()}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {isModalOpen && (
-
- )}
+ {renderHeroBanner()}
+ {renderDeckBuilderContent()}
+ {renderCardDialog()}
);
diff --git a/src/pages/HomePage.js b/src/pages/HomePage.js
index 76eddbf..512190c 100644
--- a/src/pages/HomePage.js
+++ b/src/pages/HomePage.js
@@ -8,6 +8,8 @@ import {
Grid,
Box,
useMediaQuery,
+ Paper,
+ Stack,
} from '@mui/material';
import { useMode } from '../context/hooks/colormode';
import {
@@ -35,9 +37,9 @@ import { useTheme } from '@emotion/react';
import SingleCardAnimation from '../assets/animations/SingleCardAnimation';
import { useCollectionStore } from '../context';
import CardChart from '../tests/CardChart';
-import CardComponent from '../tests/CardComponent';
import useCardCronJob from '../tests/useCardCronJob';
-import initialCardData from '../tests/initialCardData';
+// import initialCardData from '../tests/initialCardData';
+import { styled } from '@mui/styles';
const AnimatedBox = animated(Box);
@@ -45,8 +47,11 @@ const HomePage = () => {
const { theme } = useMode();
const theme2 = useTheme();
const isSmUp = useMediaQuery(theme.breakpoints.up('sm'));
- const { cardData, startUpdates, pauseUpdates, resetData } =
- useCardCronJob(initialCardData);
+ const isMdUp = useMediaQuery(theme.breakpoints.up('md'));
+ const isLgUp = useMediaQuery(theme.breakpoints.up('lg'));
+ const { allCollections } = useCollectionStore();
+ const initialCardData = allCollections[0]?.cards[0];
+ const { cardData } = useCardCronJob(initialCardData);
const { isModalOpen, modalContent } = useContext(ModalContext);
const { selectedCollection } = useCollectionStore();
@@ -76,228 +81,199 @@ const HomePage = () => {
}
}, []);
- const titleStyles = {
- padding: theme.spacing(2), // consistent padding with the theme
- textAlign: 'center',
- color: theme.palette.backgroundD.contrastText,
- background: theme.palette.background.dark,
- borderRadius: theme.shape.borderRadius,
- margin: theme.spacing(2),
- // Add more styles as per the theme or design requirements
- };
-
- return (
-
-
-
-
-
-
-
-
- {introText.mainTitle}
-
-
- {introText.description}
-
-
-
-
-
- (
+
+
+
+ );
+ // Function to render tertiary content
+ const renderTertiaryContent = () => (
+
+
+
-
- Top Performing Cards
-
-
-
-
+
- {cardData && cardData?.dailyPriceHistory && (
-
- )}
+ {introText.description}
+
+
+
+ );
+ // Function to render secondary content
+ const renderSecondaryContent = () => (
+
+
+ {isMdUp && (
-
- {' '}
-
+ {/*
+ Top Performing Cards
+ */}
+
+
+ )}
+
+ {/* Chart and Card Components */}
- {/* This is your animation component */}
-
-
-
- {tiers.map((tier, index) => {
- const [tiltAnimation, setTiltAnimation] = useSpring(() => ({
+ >
+
+
+
+
+ );
+ // Function to render main content
+ const renderMainContent = () => (
+
+
+ {tiers.map((tier, index) => {
+ const [tiltAnimation, setTiltAnimation] = useSpring(() => ({
+ transform:
+ 'perspective(600px) rotateX(0deg) rotateY(0deg) scale(1)',
+ }));
+ const handleMouseEnter = () =>
+ setTiltAnimation({
transform:
- 'perspective(600px) rotateX(0deg) rotateY(0deg) scale(1)',
- }));
- const handleMouseEnter = () =>
- setTiltAnimation({
- transform:
- 'perspective(600px) rotateX(5deg) rotateY(5deg) scale(1.05)',
- });
+ 'perspective(600px) rotateX(5deg) rotateY(5deg) scale(1.05)',
+ });
- const handleMouseLeave = () =>
- setTiltAnimation({
- transform:
- 'perspective(600px) rotateX(0deg) rotateY(0deg) scale(1)',
- });
- return (
-
+ setTiltAnimation({
+ transform:
+ 'perspective(600px) rotateX(0deg) rotateY(0deg) scale(1)',
+ });
+ return (
+
+
-
-
-
-
-
- {tier.description.map((line, index) => (
-
- {line}
-
- ))}
-
-
-
- handleOpenModal(tier.title)}
- >
- {tier.buttonText}
-
-
-
-
-
- );
- })}
-
-
- {isModalOpen && (
-
- )}
- {detailsModalShow &&
}
+ backgroundColor: theme.palette.backgroundD.dark,
+ }} // Apply the background color here
+ />
+
+
+ {tier.description.map((line, index) => (
+
+ {line}
+
+ ))}
+
+
+
+ handleOpenModal(tier.title)}
+ >
+ {tier.buttonText}
+
+
+
+
+
+ );
+ })}
+
+
+ );
+ // Function to render dialogs (GenericCardDialog and DetailsModal)
+ const renderDialogs = () => {
+ return (
+ <>
+ {isModalOpen && (
+
+ )}
+ {detailsModalShow &&
}
+ >
+ );
+ };
+
+ return (
+
+
+ {/* Main Splash Page */}
+ {renderSplashPage()}
+
+ {/* Tertiary Content */}
+ {renderTertiaryContent()}
+
+ {/* Secondary Content */}
+ {renderSecondaryContent()}
+
+ {/* Main Content */}
+ {renderMainContent()}
+
+ {/* Dialogs */}
+ {renderDialogs()}
);
};
diff --git a/src/pages/StorePage.js b/src/pages/StorePage.js
index d53c777..faf3727 100644
--- a/src/pages/StorePage.js
+++ b/src/pages/StorePage.js
@@ -16,83 +16,86 @@ const StorePage = () => {
const [containerHeight, setContainerHeight] = useState(0);
const [searchBarFocused, setSearchBarFocused] = useState(false);
const { isModalOpen, modalContent } = useModalContext();
-
- const updateContainerHeight = (height) => {
- setContainerHeight(height);
- };
+ // Function to render the Hero section
+ const renderHero = () => (
+
+ );
+ // Function to render the search bar and product grid
+ const renderSearchAndProducts = () => (
+
+
+ setSearchBarFocused(true)}
+ onSearchBlur={() => setSearchBarFocused(false)}
+ />
+
+
+
+ );
+ // Function to render the bottom background box (for UI effects)
+ const renderBackgroundBox = () => (
+
+ );
return (
-
-
-
-
- setSearchBarFocused(true)}
- onSearchBlur={() => setSearchBarFocused(false)}
- />
-
-
-
+ {/* Main content rendering */}
+ {renderHero()}
+ {renderSearchAndProducts()}
+
+ {/* Modal for card details */}
+ {isModalOpen && (
+
+ )}
+
+ {/* Background box for additional UI */}
+ {renderBackgroundBox()}
- {isModalOpen && (
-
- )}
-
);
};
diff --git a/src/pages/otherPages/LoginPage.jsx b/src/pages/otherPages/LoginPage.jsx
index 43a2a6b..40a4dde 100644
--- a/src/pages/otherPages/LoginPage.jsx
+++ b/src/pages/otherPages/LoginPage.jsx
@@ -34,9 +34,10 @@ const defaultTheme = createTheme();
export default function LoginPage() {
const { theme } = useMode();
- const { isLoggedIn } = useAuthContext();
+ const [cookie, setCookie] = React.useState('isLoggedIn');
+ // const { isLoggedIn } = useAuthContext();
- if (isLoggedIn) return
;
+ if (cookie.isLoggedIn) return
;
return (
diff --git a/src/pages/otherPages/ProfilePage.js b/src/pages/otherPages/ProfilePage.js
index 9aec8df..343b31e 100644
--- a/src/pages/otherPages/ProfilePage.js
+++ b/src/pages/otherPages/ProfilePage.js
@@ -12,7 +12,7 @@ import {
} from '@mui/material';
import { Edit as EditIcon } from '@mui/icons-material';
import placeholder from '../../assets/images/placeholder.jpeg';
-import UserStats from '../../components/other/dataDisplay/UserStats';
+import UserStats from '../../layout/UserStats';
import { useUserContext } from '../../context/UserContext/UserContext';
import { useCookies } from 'react-cookie';
import ThemeToggleButton from '../../components/buttons/other/ThemeToggleButton';
diff --git a/src/pages/pageStyles/StyledComponents.jsx b/src/pages/pageStyles/StyledComponents.jsx
index d206770..9a2e441 100644
--- a/src/pages/pageStyles/StyledComponents.jsx
+++ b/src/pages/pageStyles/StyledComponents.jsx
@@ -18,33 +18,33 @@ export const AppContainer = styled('div')(({ theme }) => ({
height: '100vh',
// background: '#222',
}));
-const StyledContainer = styled(Box)(({ theme }) => ({
- display: 'flex',
- flexDirection: 'column',
- // alignSelf: 'start',
- alignItems: 'center',
- justifyContent: 'center',
- // marginLeft: theme.spacing(2),
- borderRadius: theme.shape.borderRadiusLarge,
- // marginRight: theme.spacing(2),
- // minHeight: '250vh',
- flexGrow: 1,
- // minHeight: '100%',
- background: '#333',
- // backgroundColor: '#f1f1f1',
- padding: {
- xs: 0,
- sm: theme.spacing(1),
- md: theme.spacing(2.5),
- lg: theme.spacing(2.5),
- },
- height: '100%',
- width: '100%',
-}));
+// const StyledContainer = styled(Box)(({ theme }) => ({
+// display: 'flex',
+// flexDirection: 'column',
+// // alignSelf: 'start',
+// alignItems: 'center',
+// justifyContent: 'center',
+// // marginLeft: theme.spacing(2),
+// borderRadius: theme.shape.borderRadiusLarge,
+// // marginRight: theme.spacing(2),
+// // minHeight: '250vh',
+// flexGrow: 1,
+// // minHeight: '100%',
+// background: '#333',
+// // backgroundColor: '#f1f1f1',
+// padding: {
+// xs: 0,
+// sm: theme.spacing(1),
+// md: theme.spacing(2.5),
+// lg: theme.spacing(2.5),
+// },
+// height: '100%',
+// width: '100%',
+// }));
-export const CollectionContainer = ({ children }) => {
- return {children};
-};
+// export const AppContainer = ({ children }) => {
+// return {children};
+// };
const StyledContainer2 = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
@@ -457,24 +457,39 @@ export const MainContentContainer = styled(Container)(({ theme }) => ({
margin: theme.spacing(4, 0), // added vertical spacing
}));
-export const SecondaryContentContainer = styled('div')(({ theme }) => ({
- background: theme.palette.backgroundD.dark,
- marginBottom: theme.spacing(2),
- width: '100%',
+export const SecondaryContentContainer = styled(Box)(({ theme }) => ({
+ display: 'flex',
flexDirection: 'row',
- boxShadow: theme.shadows[3],
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: '100%',
+ padding: theme.spacing(2),
+ background: theme.palette.backgroundD.dark,
borderRadius: theme.shape.borderRadius,
+ // justifyContent: 'center',
+ // // marginBottom: theme.spacing(2),
+ // width: '100%',
+ // height: '100%',
+ // flexDirection: 'row',
+ // boxShadow: theme.shadows[3],
+ // borderRadius: theme.shape.borderRadius,
transition: 'background-color 0.3s',
- position: 'relative',
- paddingTop: '0', // Remove top padding to align the animation at the top
- paddingBottom: '0', // Remove bottom padding
- minHeight: '70vh', // Adjust the height as needed to fit the animation
- display: 'flex',
- justifyContent: 'space-evenly', // Center the animation horizontally
- alignItems: 'center', // Center the animation vertically
- alignContent: 'center', // Center the animation vertically
- overflow: 'hidden', // Prevent overflow
+ // position: 'relative',
+ // paddingTop: theme.spacing(10), // Add top padding
+ // paddingBottom: theme.spacing(20), // Add bottom padding
+ // // marginBottom: theme.spacing(20),
+ // // paddingLeft: theme.spacing(10), // Add left padding
+ // // paddingRight: theme.spacing(10), // Add right padding
+ // minHeight: '70vh', // Adjust the height as needed to fit the animation
+ // maxHeight: '80vh',
+ // // alignItems: 'flex-start', // Center the animation vertically
+ // alignContent: 'center', // Center the animation vertically
+ // overflow: 'hidden', // Prevent overflow
+ // display: 'flex',
+ // alignItems: 'center', // Align children to the center horizontally
+ // justifyContent: 'space-between', // Distribute space between children
}));
+
export const TertiaryContentContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(3),
borderRadius: theme.shape.borderRadius,
diff --git a/src/tests/CardChart.jsx b/src/tests/CardChart.jsx
index b8ee511..818d850 100644
--- a/src/tests/CardChart.jsx
+++ b/src/tests/CardChart.jsx
@@ -8,136 +8,426 @@ import React, {
} from 'react';
import {
Box,
+ Button,
+ Card,
+ CardActions,
+ CardContent,
+ CardHeader,
Container,
Grid,
+ List,
+ ListItem,
Paper,
+ Typography,
styled,
useMediaQuery,
useTheme,
} from '@mui/material';
import CardLinearChart from './CardLinearChart';
-import { ErrorBoundary, useMode, usePageContext } from '../context';
-const ChartPaper = styled(Paper)(({ theme }) => ({
- borderRadius: theme.shape.borderRadius,
- boxShadow: theme.shadows[5],
- backgroundColor: theme.palette.background.paper,
- color: theme.palette.text.secondary,
+import {
+ ErrorBoundary,
+ useCollectionStore,
+ useMode,
+ usePageContext,
+} from '../context';
+import useCardCronJob from './useCardCronJob';
+import initialCardData from './initialCardData';
+import { format } from 'date-fns';
+import SingleCardAnimation from '../assets/animations/SingleCardAnimation';
+import LoadingCardAnimation from '../assets/animations/LoadingCardAnimation';
+import ImageDisplayFromHook from '../components/other/dataDisplay/ImageDisplayFromHook';
+
+// Adjust the padding, margin, and other styles as needed
+const ChartArea = styled(Box)(({ theme }) => ({
+ width: '100%',
+ height: '100%',
padding: theme.spacing(2),
display: 'flex',
- flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
- width: '100%',
- minHeight: '400px',
+ border: '1px solid #000',
+ borderRadius: '5px',
+}));
+// A square, responsive container for the chart
+const SquareChartContainer = styled(Box)(({ theme }) => ({
+ position: 'relative',
+ width: '100%', // 100% of the parent width
+ paddingTop: '100%', // Maintain aspect ratio (1:1)
overflow: 'hidden',
- margin: theme.spacing(2, 0),
+ '& > *': {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ },
}));
-const ResponsiveSquare = styled(Box)(({ theme }) => ({
- width: '100%',
- // paddingTop: '100%',
- backgroundColor: theme.palette.backgroundA.lightest,
- borderRadius: theme.shape.borderRadius,
- boxShadow: theme.shadows[5],
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- // width: isSmallScreen ? '100%' : '80%', // Adjust width based on screen size
- height: 'auto', // Adjust height as needed
- padding: theme.spacing(2), // Consistent padding
- overflow: 'auto', // Adds scroll to inner content if it overflows
-}));
+const CardChart = ({ cardData = initialCardData }) => {
+ // STYLING AND MEDIA QUERY HOOKS
+ const { theme } = useMode();
+ const theme2 = useTheme();
+ const isLgUp = useMediaQuery(theme.breakpoints.up('lg'));
+ const { selectedCollection, allCollections } = useCollectionStore();
+ const [imageUrl, setImageUrl] = useState(null);
+ const [error, setError] = useState(null);
-function handleThresholdUpdate(lastUpdateTime, setLastUpdateTime) {
- const currentTime = new Date().getTime();
- if (!lastUpdateTime || currentTime - lastUpdateTime >= 600000) {
- setLastUpdateTime(currentTime);
- return currentTime;
- }
- return lastUpdateTime;
-}
+ const { startUpdates, pauseUpdates, resetData } =
+ useCardCronJob(initialCardData);
+ const formatTimestamp = (timestamp) =>
+ format(new Date(timestamp), "MMM do, yyyy 'at' HH:mm");
-const CardChart = ({ cardData }) => {
+ const [chartDimensions, setChartDimensions] = useState({
+ width: 0,
+ height: 0,
+ });
const { setLoading, loadingStatus, returnDisplay } = usePageContext();
if (!cardData || !cardData?.dailyPriceHistory) {
setLoading('isLoading', true);
}
const safeCardData = cardData || { dailyPriceHistory: [] };
const dailyPriceHistory = safeCardData?.dailyPriceHistory;
- if (!dailyPriceHistory?.length) {
- setLoading('isLoading', true);
- }
useEffect(() => {
- if (dailyPriceHistory?.length) {
- setLoading('isLoading', false);
+ const shouldLoad =
+ !cardData ||
+ !cardData?.dailyPriceHistory ||
+ !cardData.dailyPriceHistory.length;
+ if (shouldLoad !== loadingStatus.isLoading) {
+ setLoading('isLoading', shouldLoad);
}
- }, [dailyPriceHistory]);
- // STYLING AND MEDIA QUERY HOOKS
- const { theme } = useMode();
- const theme2 = useTheme();
- const isMobile = useMediaQuery(theme2.breakpoints.down('sm'));
- const chartContainerRef = useRef(null);
- const [chartDimensions, setChartDimensions] = useState({
- width: 0,
- height: 0,
- });
-
- const calculateChartDimensions = useCallback(() => {
- if (chartContainerRef.current) {
- const width = chartContainerRef.current.offsetWidth;
- const minWidth = isMobile ? 300 : 400;
- const height = width * 0.7; // 70% of width
- const minHeight = minWidth * 0.7;
- setChartDimensions({ width, height, minWidth, minHeight });
- }
- }, []);
+ }, [cardData, loadingStatus.isLoading, setLoading]); // Add loadingStatus.isLoading to dependencies
+ // useEffect to set image state when cardData changes
useEffect(() => {
- const handleResize = () => calculateChartDimensions();
- window.addEventListener('resize', handleResize);
- calculateChartDimensions();
- return () => window.removeEventListener('resize', handleResize);
- }, [calculateChartDimensions]);
+ if (cardData?.imageUrl) {
+ setImageUrl(cardData?.image);
+ }
+ }, [cardData?.imageUrl]);
const chartData = useMemo(
() =>
- cardData?.dailyPriceHistory?.map((priceEntry) => ({
+ dailyPriceHistory?.map((priceEntry) => ({
x: priceEntry?.timestamp,
y: priceEntry?.num,
})),
- [cardData.dailyPriceHistory]
+ [dailyPriceHistory] // dependency array
);
-
+ // const chartData = useMemo(
+ // () =>
+ // cardData?.dailyPriceHistory?.map((entry) => ({
+ // x: format(new Date(entry.timestamp), "MMM do, yyyy 'at' HH:mm"),
+ // y: entry.num,
+ // })) || [],
+ // [cardData?.dailyPriceHistory]
+ // );
const nivoReadyData = useMemo(
- () => [{ id: cardData?.name, data: chartData }],
+ () => [
+ {
+ id: cardData?.name || 'default', // Fallback ID if cardData.name is not available
+ data: chartData,
+ },
+ ],
[chartData, cardData?.name]
);
+ // Ensure this effect doesn't set loading when not necessary
+ useEffect(() => {
+ if (nivoReadyData && nivoReadyData.length > 0 && loadingStatus.isLoading) {
+ setLoading('isLoading', false);
+ }
+ }, [nivoReadyData, setLoading, loadingStatus.isLoading]);
+ // Add responsive chart dimension handling
+ useEffect(() => {
+ // Example of setting dynamic chart dimensions (could be more complex based on container size)
+ const updateDimensions = () => {
+ const width = window.innerWidth < 500 ? window.innerWidth : 500; // or some other logic
+ const height = 300; // Fixed height or based on aspect ratio
+ setChartDimensions({ width, height });
+ };
- // // Return loading indicator, error message, or chart based on data status
- // if (!dailyPriceHistory?.length) {
- // return ; // or some placeholder text
- // }
+ window.addEventListener('resize', updateDimensions);
+ updateDimensions(); // Initial call
+
+ return () => {
+ window.removeEventListener('resize', updateDimensions); // Cleanup listener
+ };
+ }, []); // Empty array ensures this effect runs only once after initial render
+ // Simplified for demonstration
+ const renderLoadingAnimation = () => {
+ return (
+
+ );
+ };
+ const renderHeaderWithAnimation = () => {
+ return (
+
+
+ {/* Conditionally render animation based on size or other conditions */}
+ {isLgUp && renderLoadingAnimation()}
+
+ );
+ };
+ // const renderImageLoadTestWithErrorHandling = () => {
+ // const handleImageLoad = () => {
+ // console.log('Image loaded');
+ // };
+ // const handleImageError = () => {
+ // console.log('Image error');
+ // };
+ // return (
+ //
+ //
+ //
+ // );
+ // };
+ // const renderImageLoadTest = () => {
+ // if (!imageUrl && allCollections?.length > 0) return null;
+ // // if (!imageUrl && allCollections?.length > 0) {
+ // // setImageUrl(allCollections[0]?.cards[0]?.image);
+ // // }
+ // const handleImageLoad = () => {
+ // console.log('Image loaded');
+ // };
+ // const handleImageError = () => {
+ // console.log('Image error');
+ // };
+ // return (
+ //
+ // );
+ // };
+ // const renderLoadingAnimation = () => {
+ // if (isLgUp) {
+ // console.log('Loading animation');
+ // return (
+ //
+ // {/* Use LoadingCardAnimation with the correct props */}
+ //
+ //
+ // );
+ // }
+ // return null;
+ // };
return (
-
- {loadingStatus?.isLoading && returnDisplay()}
-
-
-
-
-
+
+
+
+ {renderHeaderWithAnimation()}
+
+ {/*
+ {renderImageLoadTest()}
+ */}
+
+
+ {loadingStatus?.isLoading ? (
+ returnDisplay()
+ ) : (
+
-
-
-
-
-
-
+
+ )}
+
+
+
+
+
+ {/* Card displaying data with responsive typography */}
+
+
+ {/* Responsive Button Styling */}
+ {/* Iterate through buttons to reduce redundancy */}
+ {['Start Updates', 'Pause Updates', 'Reset Data'].map(
+ (text, index) => (
+
+ )
+ )}
+
+
+
+
+
+ {cardData?.dailyPriceHistory?.map((entry, index) => (
+
+
+ Quantity: {cardData?.quantity}
+
+
+ Price: ${entry?.num}
+
+
+ {formatTimestamp(entry?.timestamp)}
+
+
+ ))}
+
+
+
+
+
);
};
diff --git a/src/tests/CardComponent.jsx b/src/tests/CardComponent.jsx
deleted file mode 100644
index 291e08f..0000000
--- a/src/tests/CardComponent.jsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import React from 'react';
-import useCardCronJob from './useCardCronJob'; // path to your hook
-import initialCardData from './initialCardData';
-import {
- Button,
- Card,
- CardContent,
- Typography,
- Box,
- List,
- ListItem,
-} from '@mui/material';
-import { useMode } from '../context';
-import { format } from 'date-fns';
-
-const CardComponent = () => {
- const { theme } = useMode();
- const { cardData, startUpdates, pauseUpdates, resetData } =
- useCardCronJob(initialCardData);
- // Format timestamp to be more human readable
- const formatTimestamp = (timestamp) =>
- format(new Date(timestamp), "MMM do, yyyy 'at' HH:mm");
-
- return (
-
-
-
-
- Card Cron Job Simulator
-
-
-
-
-
-
-
- {/* Displaying the card data for demonstration */}
-
-
- {/* Card Data */}
- {/*
- Card Data:
- */}
-
-
- Daily Price History:
-
-
- {cardData?.dailyPriceHistory?.map((entry, index) => (
-
-
- Quantity: {cardData?.quantity}
-
-
- Price: ${entry?.num}
-
-
- {formatTimestamp(entry?.timestamp)}
-
-
- ))}
-
-
-
-
-
-
- );
-};
-
-export default CardComponent;
diff --git a/src/tests/CardLinearChart.jsx b/src/tests/CardLinearChart.jsx
index 2b7413c..4e5ba41 100644
--- a/src/tests/CardLinearChart.jsx
+++ b/src/tests/CardLinearChart.jsx
@@ -1,4 +1,8 @@
-// ... (imports)
+import { Box, Tooltip, Typography, useMediaQuery } from '@mui/material';
+import { makeStyles, useTheme } from '@mui/styles';
+import { ResponsiveLine } from '@nivo/line';
+import { useCallback, useMemo, useState } from 'react';
+import { useMode } from '../context';
const useStyles = makeStyles((theme) => ({
axisLabel: {
position: 'absolute',
@@ -48,7 +52,14 @@ const CustomTooltip = ({ point }) => {
);
};
-
+const parseDate = (dateString) => {
+ const date = new Date(dateString);
+ if (isNaN(date.getTime())) {
+ console.error(`Invalid date: ${dateString}`);
+ return null; // or a sensible default, or throw an error, depending on your needs
+ }
+ return date;
+};
export const useEventHandlers = () => {
const [hoveredData, setHoveredData] = useState(null);
const handleMouseMove = useCallback((point) => {
@@ -57,62 +68,74 @@ export const useEventHandlers = () => {
const handleMouseLeave = useCallback(() => setHoveredData(null), []);
return { hoveredData, handleMouseMove, handleMouseLeave };
};
-import { Box, Tooltip, Typography, useMediaQuery } from '@mui/material';
-import { makeStyles, useTheme } from '@mui/styles';
-import { ResponsiveLine } from '@nivo/line';
-import { useCallback, useMemo, useState } from 'react';
-import { useMode } from '../context';
const CardLinearChart = ({ nivoReadyData, dimensions }) => {
- console.log('nivoReadyData', nivoReadyData);
- const { theme } = useMode(); // or useTheme() based on your context setup
+ const { theme } = useMode();
const classes = useStyles(theme);
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
- const [isZoomed, setIsZoomed] = useState(false); // If you need zoom functionality
- const { handleMouseMove, handleMouseLeave } = useEventHandlers();
+ // Ensure all data points have valid dates
+ const processedData = useMemo(() => {
+ return nivoReadyData?.map((series) => ({
+ ...series,
+ data: series?.data?.map((point) => ({
+ ...point,
+ x: parseDate(point?.x) || point?.x, // Use the parsed date or fallback to the original value
+ })),
+ }));
+ }, [nivoReadyData]);
+ // const { theme } = useMode();
+ // const classes = useStyles(theme);
+ // const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
+ // const [isZoomed, setIsZoomed] = useState(false);
+ // const { handleMouseMove, handleMouseLeave } = useEventHandlers();
- // Error handling for empty or invalid data
- if (
- !nivoReadyData ||
- nivoReadyData?.length === 0 ||
- !Array.isArray(nivoReadyData)
- ) {
- return No data available;
- }
+ // if (
+ // !nivoReadyData ||
+ // nivoReadyData.length === 0 ||
+ // !Array.isArray(nivoReadyData)
+ // ) {
+ // return No data available;
+ // }
- // Ensure all data points have valid dates
- nivoReadyData?.forEach((series) => {
- series?.data?.forEach((point) => {
- if (!(point?.x instanceof Date)) {
- // Convert to date or handle error
- console.warn('Invalid date found in chart data');
+ // // Correct the date parsing logic
+ // const processedData = nivoReadyData.map((series) => ({
+ // ...series,
+ // data: series.data.map((point) => ({
+ // ...point,
+ // x: parseDate(point.x) || point.x, // Use the parsed date or fallback to the original value
+ // })),
+ // }));
+
+ // // Ensure all data points have valid dates
+ // nivoReadyData?.forEach((series) => {
+ // series?.data?.forEach((point) => {
+ // const date = parseDate(point?.x);
+ // if (!(date instanceof Date)) {
+ // // Convert to date or handle error
+ // console.warn('Invalid date found in chart data', date);
- // Example of converting to date
- const date = new Date(point?.x);
- if (date instanceof Date && !isNaN(date)) {
- point.x = date;
- }
- }
- });
- });
+ // // Example of converting to date
+ // const date = new Date(date);
+ // if (date instanceof Date && !isNaN(date)) {
+ // point.x = date;
+ // }
+ // }
+ // });
+ // });
// Calculate chart properties based on nivoReadyData and dimensions
+ // Minimal chart properties for testing
const chartProps = useMemo(
() => ({
- data: nivoReadyData,
- margin: { top: 50, right: 110, bottom: 50, left: 60 },
+ data: processedData,
+ margin: { top: 20, right: 20, bottom: 20, left: 35 },
xScale: {
type: 'time',
format: 'time:%Y-%m-%dT%H:%M:%S.%LZ',
useUTC: false,
precision: 'second',
},
- xFormat: 'time:%Y-%m-%d %H:%M:%S',
- yScale: { type: 'linear', min: 'auto', max: 'auto' },
- animate: true,
- motionStiffness: 90,
- motionDamping: 15,
axisBottom: {
tickRotation: 0,
legend: 'Time',
@@ -123,28 +146,67 @@ const CardLinearChart = ({ nivoReadyData, dimensions }) => {
tickValues: 'every 2 days',
format: '%b %d',
},
- axisLeft: {
- orient: 'left',
- legend: 'Price',
- legendOffset: -40,
- legendPosition: 'middle',
- tickSize: 5,
- tickPadding: 5,
- },
- pointSize: 10,
- pointColor: { theme: 'background' },
- pointBorderWidth: 2,
- pointBorderColor: { from: 'serieColor' },
- useMesh: true,
+ // animate: true,
+ // motionStiffness: 90,
+ // motionDamping: 15,
+ // axisLeft: {
+ // orient: 'left',
+ // legend: 'Price',
+ // legendOffset: -40,
+ // legendPosition: 'middle',
+ // tickSize: 5,
+ // tickPadding: 5,
+ // },
+ // pointSize: 10,
+ // pointColor: { theme: 'background' },
+ // pointBorderWidth: 2,
+ // pointBorderColor: { from: 'serieColor' },
+ // useMesh: true,
enableSlices: 'x',
+ yScale: { type: 'linear', min: 'auto', max: 'auto' },
}),
- [nivoReadyData, theme, isMobile]
- ); // Add other dependencies as needed
+ [nivoReadyData, processedData]
+ );
+ // const chartProps = useMemo(
+ // () => ({
+ // data: processedData,
+ // margin: { top: 50, right: 110, bottom: 50, left: 60 },
+ // xScale: {
+ // type: 'time',
+ // format: 'time:%Y-%m-%dT%H:%M:%S.%LZ',
+ // useUTC: false,
+ // precision: 'second',
+ // },
+ // xFormat: 'time:%Y-%m-%d %H:%M:%S',
+ // yScale: { type: 'linear', min: 'auto', max: 'auto' },
+ // animate: true,
+ // motionStiffness: 90,
+ // motionDamping: 15,
+ // axisLeft: {
+ // orient: 'left',
+ // legend: 'Price',
+ // legendOffset: -40,
+ // legendPosition: 'middle',
+ // tickSize: 5,
+ // tickPadding: 5,
+ // },
+ // pointSize: 10,
+ // pointColor: { theme: 'background' },
+ // pointBorderWidth: 2,
+ // pointBorderColor: { from: 'serieColor' },
+ // useMesh: true,
+ // enableSlices: 'x',
+ // }),
+ // [processedData, theme, isMobile] // Add other dependencies as needed
+ // ); // Add other dependencies as needed
- // Responsive container
- const containerHeight = isMobile ? '200px' : dimensions.height || '300px';
- const containerWidth = '100%'; // Always take the full width of the parent
+ // console.log('Nivo Ready Data:', nivoReadyData);
+ // console.log('Processed Data:', processedData);
+ // console.log('Chart Dimensions:', dimensions);
+ if (!processedData || !processedData?.length) {
+ return No data available;
+ }
return (
{
const [cardData, setCardData] = useState(initialCardData);
const [intervalId, setIntervalId] = useState(null);
- const updateCardData = useCallback(() => {
- setCardData((currentCard) => {
- console.log('Updating card data:', cardData);
- console.log('Current card data:', currentCard);
- const newPriceHistory = {
- num: (Math.random() * 10).toFixed(2), // or however you calculate new price
- timestamp: new Date().toISOString(),
- };
+ // Function to update the card's daily price history
+ const updatePriceHistory = useCallback(() => {
+ setCardData((currentCardData) => {
+ if (!currentCardData) return null;
+ const lastPrice =
+ currentCardData?.dailyPriceHistory.slice(-1)[0]?.num ||
+ currentCardData?.price; // Get last price or default to initial price
+ const newDailyPriceHistory = [...currentCardData.dailyPriceHistory];
+
+ // Generate 10 new daily prices
+ for (let i = 0; i < 10; i++) {
+ const newPrice = generateNewPrice(lastPrice);
+ newDailyPriceHistory.push({
+ num: newPrice,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ // Ensure the dailyPriceHistory doesn't exceed 10 entries
+ const slicedHistory = newDailyPriceHistory.slice(-10);
return {
- ...currentCard,
- quantity: currentCard.quantity + 1, // Increment quantity or however you update
- dailyPriceHistory: [...currentCard.dailyPriceHistory, newPriceHistory],
+ ...currentCardData,
+ dailyPriceHistory: slicedHistory,
};
});
}, []);
+ // const updateCardData = useCallback(() => {
+ // setCardData((currentCard) => {
+ // console.log('Updating card data:', cardData);
+ // console.log('Current card data:', currentCard);
+ // const newPriceHistory = {
+ // num: (Math.random() * 10).toFixed(2), // or however you calculate new price
+ // timestamp: new Date().toISOString(),
+ // };
+
+ // return {
+ // ...currentCard,
+ // quantity: currentCard.quantity + 1, // Increment quantity or however you update
+ // dailyPriceHistory: [...currentCard.dailyPriceHistory, newPriceHistory],
+ // };
+ // });
+ // }, []);
+
+ // Simulate a cron job with useEffect and setInterval
+ useEffect(() => {
+ const intervalId = setInterval(() => {
+ updatePriceHistory(); // Update price history every interval
+ }, 120000); // Update every 5 seconds for example, adjust as needed
+
+ // Cleanup function to clear interval when component unmounts or updates
+ return () => clearInterval(intervalId);
+ }, [updatePriceHistory]);
+
const startUpdates = useCallback(() => {
console.log('Starting updates');
if (!intervalId) {
- const id = setInterval(updateCardData, 120000); // Update every 2 minutes
+ const id = setInterval(updatePriceHistory, 120000); // Update every 2 minutes, adjust as needed
setIntervalId(id);
}
- }, [updateCardData, intervalId]);
+ }, [updatePriceHistory, intervalId]);
const pauseUpdates = useCallback(() => {
console.log('Pausing updates');
@@ -38,20 +80,21 @@ const useCardCronJob = (initialCardData) => {
}, [intervalId]);
const resetData = useCallback(() => {
- console.log('Resetting data');
+ console.log('Resetting data to initial state');
setCardData(initialCardData);
pauseUpdates();
}, [initialCardData, pauseUpdates]);
- useEffect(() => {
- return () => {
- // Cleanup interval on component unmount
- if (intervalId) clearInterval(intervalId);
- };
- }, [intervalId]);
+ // useEffect(() => {
+ // return () => {
+ // // Cleanup interval on component unmount
+ // if (intervalId) clearInterval(intervalId);
+ // };
+ // }, [intervalId]);
return {
cardData,
+ updatePriceHistory,
startUpdates,
pauseUpdates,
resetData,