From 956d90f7dd96dfe5ebe376cadea9000fe5b06439 Mon Sep 17 00:00:00 2001 From: FarmerJohnsBessie Date: Wed, 29 Jan 2025 16:45:24 -0800 Subject: [PATCH] revised a lot --- src/components/Match/MatchingModal.js | 75 +++++++++ src/components/{ => Profile}/BattleHistory.js | 2 +- src/components/{ => Profile}/FriendList.js | 6 +- src/components/{ => Profile}/Inventory.js | 4 +- src/components/{ => Profile}/Profile.js | 149 +++++++++--------- .../{ => Profile}/TournamentHistory.js | 2 +- src/context/AuthContext.js | 6 +- src/pages/GoalSettingPage.js | 14 +- src/pages/MatchingPage.js | 42 ++--- src/pages/ProfilePage.js | 83 +++++----- src/pages/TrainerPage.js | 4 +- src/pages/practice_test/PracticeTestPage.js | 23 ++- 12 files changed, 245 insertions(+), 165 deletions(-) create mode 100644 src/components/Match/MatchingModal.js rename src/components/{ => Profile}/BattleHistory.js (98%) rename src/components/{ => Profile}/FriendList.js (95%) rename src/components/{ => Profile}/Inventory.js (83%) rename src/components/{ => Profile}/Profile.js (64%) rename src/components/{ => Profile}/TournamentHistory.js (98%) diff --git a/src/components/Match/MatchingModal.js b/src/components/Match/MatchingModal.js new file mode 100644 index 0000000..ce941bb --- /dev/null +++ b/src/components/Match/MatchingModal.js @@ -0,0 +1,75 @@ +import React, { useEffect, useState } from 'react'; +import { Modal, Typography, Spin } from 'antd'; +import { LoadingOutlined } from '@ant-design/icons'; +import styled, { keyframes } from 'styled-components'; + +const { Text } = Typography; + +const spin = keyframes` + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +`; + +const StyledLoadingIcon = styled(LoadingOutlined)` + font-size: 48px; + color: #4C3D97; + margin-bottom: 24px; +`; + +const MatchingContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + padding: 24px; + text-align: center; +`; + +const LoadingText = styled(Text)` + font-size: 18px; + margin-top: 16px; + color: #4A4A4A; +`; + +const sentences = [ + "Searching for opponents...", + "Getting ready for the battle...", + "Preparing your duel...", + "Almost there...", + "Setting up your battle arena..." +]; + +const MatchingModal = ({ visible, onCancel }) => { + const [loadingMessage, setLoadingMessage] = useState(sentences[0]); + + useEffect(() => { + if (visible) { + const interval = setInterval(() => { + setLoadingMessage((prev) => { + const currentIndex = sentences.indexOf(prev); + return sentences[(currentIndex + 1) % sentences.length]; + }); + }, 3000); + return () => clearInterval(interval); + } + }, [visible]); + + return ( + + + + {loadingMessage} + + + ); +}; + +export default MatchingModal; \ No newline at end of file diff --git a/src/components/BattleHistory.js b/src/components/Profile/BattleHistory.js similarity index 98% rename from src/components/BattleHistory.js rename to src/components/Profile/BattleHistory.js index 7ad8dc4..5e5048f 100644 --- a/src/components/BattleHistory.js +++ b/src/components/Profile/BattleHistory.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { Layout, message, Spin, Table, Typography } from 'antd'; -import { useAuth } from '../context/AuthContext'; +import { useAuth } from '../../context/AuthContext'; import axios from 'axios'; import styled from 'styled-components'; import { Link } from 'react-router-dom'; diff --git a/src/components/FriendList.js b/src/components/Profile/FriendList.js similarity index 95% rename from src/components/FriendList.js rename to src/components/Profile/FriendList.js index 0efa0fb..54c73b2 100644 --- a/src/components/FriendList.js +++ b/src/components/Profile/FriendList.js @@ -2,10 +2,10 @@ import React, {useState, useEffect} from 'react'; import styled from 'styled-components'; import {List, Card, message} from 'antd'; import axios from 'axios'; -import {useAuth} from '../context/AuthContext'; +import {useAuth} from '../../context/AuthContext'; import {Link} from 'react-router-dom'; -import FriendRequest from './FriendRequest'; -import SearchUser from './SearchUser'; +import FriendRequest from '../FriendRequest'; +import SearchUser from '../SearchUser'; const Container = styled.div` background-color: #f0f2f5; diff --git a/src/components/Inventory.js b/src/components/Profile/Inventory.js similarity index 83% rename from src/components/Inventory.js rename to src/components/Profile/Inventory.js index 7ab199d..bca1866 100644 --- a/src/components/Inventory.js +++ b/src/components/Profile/Inventory.js @@ -1,7 +1,7 @@ import React from 'react'; import { Tabs } from 'antd'; -import Pets from './Inventory_pets'; -import Storage from './Inventory_storage'; +import Pets from '../Inventory_pets'; +import Storage from '../Inventory_storage'; const { TabPane } = Tabs; diff --git a/src/components/Profile.js b/src/components/Profile/Profile.js similarity index 64% rename from src/components/Profile.js rename to src/components/Profile/Profile.js index 544712d..2e0cf65 100644 --- a/src/components/Profile.js +++ b/src/components/Profile/Profile.js @@ -1,9 +1,16 @@ -import React, { useEffect, useState } from 'react'; -import axios from 'axios'; -import { useAuth } from "../context/AuthContext"; -import { Card, Form, Input, Button, message, Row, Col, Statistic } from "antd"; -import { UserOutlined, TrophyOutlined, DollarOutlined, StarOutlined, RiseOutlined, ThunderboltOutlined } from '@ant-design/icons'; -import { useNavigate } from 'react-router-dom'; +import React, {useEffect, useState} from 'react'; +import {useAuth} from "../../context/AuthContext"; +import {Card, Form, Input, Button, message, Row, Col, Statistic} from "antd"; +import { + UserOutlined, + TrophyOutlined, + DollarOutlined, + StarOutlined, + RiseOutlined, + ThunderboltOutlined +} from '@ant-design/icons'; +import {useNavigate} from 'react-router-dom'; +import api from "../api"; const gradientStyle = { background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', @@ -28,7 +35,7 @@ const iconStyle = { marginRight: '8px' }; -function Profile({ user_id = null }) { +function Profile({user_id = null}) { const [profile, setProfile] = useState({ user: { username: '', @@ -48,45 +55,46 @@ function Profile({ user_id = null }) { }); const [loadingProfile, setLoadingProfile] = useState(true); const [form] = Form.useForm(); - const { token, loading, user } = useAuth(); + const {loading, user} = useAuth(); const navigate = useNavigate(); const isOwnProfile = user_id === null; useEffect(() => { const fetchProfile = async () => { - if (!token) { - return; - } try { - const baseUrl = process.env.REACT_APP_API_URL; - const url = isOwnProfile ? `${baseUrl}/api/profile/` : `${baseUrl}/api/profile/view_profile/${user_id}/`; - const response = await axios.get(url, { - headers: { - 'Authorization': `Bearer ${token}` - } - }); - - const condensedBiography = response.data.biography.replace(/\n+/g, '\n'); - setProfile(prevProfile => ({ - ...prevProfile, - ...response.data, - biography: condensedBiography - })); + if (isOwnProfile) { + const response = await api.get(`api/profile/`); + const condensedBiography = response.data.biography.replace(/\n+/g, '\n'); + setProfile(prevProfile => ({ + ...prevProfile, + ...response.data, + biography: condensedBiography + })); + // Fetch Infinite Questions Statistics + const infiniteStatsResponse = await api.get(`api/infinite_questions_profile/`); - // Fetch Infinite Questions Statistics - const infiniteStatsResponse = await axios.get(`${baseUrl}/api/infinite_questions_profile/`, { - headers: { - 'Authorization': `Bearer ${token}` - } - }); + setStatistics(infiniteStatsResponse.data); - setStatistics(infiniteStatsResponse.data); + setLoadingProfile(false); - setLoadingProfile(false); + // Set form fields after data is fetched + form.setFieldsValue({biography: condensedBiography}); + }else{ + const response = await api.get(`api/profile/view_profile/${user_id}/`); + const profile = response.data.profile; + const statistic = response.data.statistics; + const condensedBiography = profile.biography.replace(/\n+/g, '\n'); + setProfile(prevProfile => ({ + ...prevProfile, + ...profile, + biography: condensedBiography + })); - // Set form fields after data is fetched - form.setFieldsValue({ biography: condensedBiography }); + setStatistics(statistic); + setLoadingProfile(false); + form.setFieldsValue({biography: condensedBiography}); + } } catch (err) { console.error('Error fetching profile:', err); message.error('Failed to load profile'); @@ -94,17 +102,12 @@ function Profile({ user_id = null }) { } }; if (!loading) fetchProfile(); - }, [user_id, token, loading, isOwnProfile, form]); + }, [user_id, loading, isOwnProfile, form]); const onFinish = async (values) => { if (!isOwnProfile) return; try { - const baseUrl = process.env.REACT_APP_API_URL; - const response = await axios.patch(`${baseUrl}/api/profile/update_biography/`, values, { - headers: { - 'Authorization': `Bearer ${token}` - } - }); + const response = await api.patch(`api/profile/update_biography/`, values); const condensedBiography = response.data.biography.replace(/\n+/g, '\n'); setProfile(prevProfile => ({ @@ -120,19 +123,13 @@ function Profile({ user_id = null }) { }; const sendFriendRequest = async () => { - if (!token) { + if (!user) { message.error('You must be logged in to send friend requests'); return; } try { - const baseUrl = process.env.REACT_APP_API_URL; - await axios.post(`${baseUrl}/api/profile/send_friend_request/`, - { to_user_id: user_id }, - { - headers: { - 'Authorization': `Bearer ${token}` - } - } + await api.post(`api/profile/send_friend_request/`, + {to_user_id: user_id} ); message.success('Friend request sent.'); } catch (error) { @@ -146,28 +143,29 @@ function Profile({ user_id = null }) { }; return ( -
+
{isOwnProfile ? "My Profile" : `${profile.user?.username}'s Profile`}} + title={{isOwnProfile ? "My Profile" : `${profile.user?.username}'s Profile`}} loading={loadingProfile} extra={!isOwnProfile && ( )} - style={{ ...cardStyle, ...gradientStyle }} - headStyle={{ ...gradientStyle, borderBottom: 'none' }} + style={{...cardStyle, ...gradientStyle}} + headStyle={{...gradientStyle, borderBottom: 'none'}} > Username} value={profile.user?.username} - prefix={} + prefix={} valueStyle={whiteTextStyle} /> @@ -175,7 +173,7 @@ function Profile({ user_id = null }) { Grade} value={profile.grade} - prefix={} + prefix={} valueStyle={whiteTextStyle} /> @@ -186,15 +184,15 @@ function Profile({ user_id = null }) { Elo Ratings} loading={loadingProfile} - style={{ ...cardStyle, ...gradientStyle }} - headStyle={{ ...gradientStyle, borderBottom: 'none' }} + style={{...cardStyle, ...gradientStyle}} + headStyle={{...gradientStyle, borderBottom: 'none'}} > Duel Elo} value={profile.elo_rating} - prefix={} + prefix={} valueStyle={whiteTextStyle} /> @@ -202,7 +200,7 @@ function Profile({ user_id = null }) { Singleplayer Elo} value={profile.sp_elo_rating} - prefix={} + prefix={} valueStyle={whiteTextStyle} /> @@ -212,17 +210,18 @@ function Profile({ user_id = null }) { {/* Infinite Questions Statistics Card */} {statistics && ( {isOwnProfile ? "My Stats" : `${profile.user?.username}'s Stats`}} + title={{isOwnProfile ? "My Stats" : `${profile.user?.username}'s Stats`}} loading={loadingProfile} - style={{ ...cardStyle, ...gradientStyle }} - headStyle={{ ...gradientStyle, borderBottom: 'none' }} + style={{...cardStyle, ...gradientStyle}} + headStyle={{...gradientStyle, borderBottom: 'none'}} > Coins} value={statistics.coins} - prefix={} + prefix={} valueStyle={whiteTextStyle} /> @@ -230,17 +229,17 @@ function Profile({ user_id = null }) { Level} value={statistics.level} - prefix={} + prefix={} valueStyle={whiteTextStyle} /> - + XP} value={statistics.xp} - prefix={} + prefix={} valueStyle={whiteTextStyle} /> @@ -248,7 +247,7 @@ function Profile({ user_id = null }) { Multiplier} value={statistics.total_multiplier?.toFixed(2)} - prefix={} + prefix={} valueStyle={whiteTextStyle} /> @@ -270,14 +269,14 @@ function Profile({ user_id = null }) { @@ -285,7 +284,7 @@ function Profile({ user_id = null }) { @@ -294,7 +293,7 @@ function Profile({ user_id = null }) { )} - {user?.is_admin && ( + {user?.is_admin && isOwnProfile &&( - + {isOwnProfile && ( + Friends} key="3"> + + + )} + {isOwnProfile && ( + Inventory} key="4"> + + + + )} + Tournament History} key="5"> + - )} - Tournament History} key="5"> - - - - -
+ + +
); } diff --git a/src/pages/TrainerPage.js b/src/pages/TrainerPage.js index 3b9c122..801a7cb 100644 --- a/src/pages/TrainerPage.js +++ b/src/pages/TrainerPage.js @@ -3,9 +3,7 @@ import React from 'react'; import {Layout, Divider} from 'antd'; import styled from 'styled-components'; import {useAuth} from '../context/AuthContext'; -// Importing components -// import WeeklyQuest from '../components/TrainerPageBoxes/QuestsSection'; -// import ModulesBoxComponent from '../components/TrainerPageBoxes/ModulesBox'; + import HeaderSection from '../components/TrainerPageBoxes/HeaderSection'; import UserStatisticsCard from '../components/TrainerPageBoxes/UserStatistics'; diff --git a/src/pages/practice_test/PracticeTestPage.js b/src/pages/practice_test/PracticeTestPage.js index fec8505..1facc2e 100644 --- a/src/pages/practice_test/PracticeTestPage.js +++ b/src/pages/practice_test/PracticeTestPage.js @@ -4,6 +4,7 @@ import { ClockCircleOutlined, BookOutlined, TrophyOutlined, RightOutlined, InfoC import {useNavigate, useLocation} from "react-router-dom"; import { useState, useEffect, useRef } from 'react'; import {useAuth} from "../../context/AuthContext"; +import api from "../../components/api"; const { Title, Paragraph, Text } = Typography; const { Header, Content } = Layout; @@ -14,7 +15,7 @@ const PracticeTestPage = () => { const location = useLocation(); const [isTourOpen, setIsTourOpen] = useState(false); const diagnosticCardRef = useRef(null); - const {user} = useAuth(); + const {user, setFirstLogin} = useAuth(); useEffect(() => { // Show tour if user is new, coming from goal setting, or if it's their first login @@ -62,6 +63,24 @@ const PracticeTestPage = () => { // Add more tests as needed ]; + const handleTourClose = async () => { + setIsTourOpen(false); + + // Only update if user is logged in and it's their first login + if (user?.is_first_login) { + try { + // Update backend + await api.post('api/profile/update_first_login/'); + + localStorage.setItem('is_first_login', 'false'); + // Update user context if needed + setFirstLogin(); + } catch (error) { + console.error('Failed to update first login status:', error); + } + } + }; + return ( {/* Updated Hero Section */} @@ -294,7 +313,7 @@ const PracticeTestPage = () => { setIsTourOpen(false)} + onClose={handleTourClose} steps={tourSteps} mask={true} />