diff --git a/src/Component/Button/button.tsx b/src/Component/Button/button.tsx index 4420703..32b9254 100644 --- a/src/Component/Button/button.tsx +++ b/src/Component/Button/button.tsx @@ -4,20 +4,21 @@ import { buttonSize, buttonType } from "../../Styles/button"; import { ButtonStyleProps, ButtonProps } from "../../constants/interface"; const ButtonStyle = styled.button` - border-radius: 10px; + border-radius: 10px; + cursor: pointer; - ${(props) => buttonSize[props.$size]} - ${(props) => buttonType[props.$colorType]?.[props.$state]} + ${(props) => buttonSize[props.$size]} + ${(props) => buttonType[props.$colorType]?.[props.$state]} `; const Button = ({ onClick, children, ...styleprops }: ButtonProps) => { - return ( - <> - - {children} - - - ); + return ( + <> + + {children} + + + ); }; export default Button; diff --git a/src/Component/Chart/chart.tsx b/src/Component/Chart/chart.tsx index 038962a..af189ec 100644 --- a/src/Component/Chart/chart.tsx +++ b/src/Component/Chart/chart.tsx @@ -3,6 +3,7 @@ import ApexCharts from "apexcharts"; import { ChartStyle } from "./chartStyle"; // Assuming you have the styles defined in this file import { formatPrice } from "../../util/util"; import ModalOpen from "../Modal/modal"; +import { HiOutlineExternalLink } from "react-icons/hi"; // import { HiOutlineArrowTopRightOnSquare } from "react-icons/hi2"; @@ -178,9 +179,7 @@ const Chart = ({ data }: CandleData) => { fontSize: "15px", }} > -
- {/* */} -
+

diff --git a/src/Component/Dropdown/Dropdown.tsx b/src/Component/Dropdown/Dropdown.tsx index bbb4544..96839fa 100644 --- a/src/Component/Dropdown/Dropdown.tsx +++ b/src/Component/Dropdown/Dropdown.tsx @@ -4,78 +4,78 @@ import { useState } from "react"; import { DropdownProps } from "../../constants/interface"; const DropdownContainer = styled.div` - display: flex; - flex-direction: row; - gap: 5px; + display: flex; + flex-direction: row; `; const DropdownBox = styled.option` - width: 75px; - font-size: 10px; - color: black; - text-align: start; - padding: 8px; + width: 75px; + font-size: 10px; + color: black; + text-align: start; + padding: 8px; - &:hover { - color: ${Colors.main}; - background-color: #f5f4fb; - } + &:hover { + color: ${Colors.main}; + background-color: #f5f4fb; + } `; const DropdownListBox = styled.select` - display: flex; - flex-direction: column; - justify-content: center; - border: 1px solid ${Colors.main}; - border-radius: 5px; - background-color: white; - top: 35px; - z-index: 1; - padding: 10px 5px; - height: 35px; - width: 85px; - text-align: center; - text-justify: center; - margin-bottom: 1rem; - font-size: 12px; + display: flex; + flex-direction: column; + justify-content: center; + border: 1px solid ${Colors.main}; + border-radius: 5px; + background-color: white; + top: 35px; + z-index: 1; + padding: 10px 5px; + height: 35px; + width: 85px; + text-align: center; + text-justify: center; + margin-bottom: 1rem; + font-size: 12px; + cursor: pointer; - &::-webkit-scrollbar { - display: none; - } + &::-webkit-scrollbar { + display: none; + } `; const Dropdown: React.FC = ({ dropList, onSelect }) => { - const [isOpen, setIsOpen] = useState(false); - const [value, setValue] = useState(dropList[0]); + const [isOpen, setIsOpen] = useState(false); + const [value, setValue] = useState(dropList[0]); - // const handleToggle = () => { - // setIsOpen((prev) => !prev); - // }; + // const handleToggle = () => { + // setIsOpen((prev) => !prev); + // }; - const handleSelect = (event: React.ChangeEvent) => { - const selectedValue = event.target.value; - setValue(selectedValue); - setIsOpen(false); - onSelect(selectedValue); - }; + const handleSelect = (event: React.ChangeEvent) => { + const selectedValue = event.target.value; + setValue(selectedValue); + setIsOpen(false); + onSelect(selectedValue); + }; - return ( - - - {dropList.map((el) => ( - - {el === "capital" - ? "시가총액순" - : el === "trade" - ? "거래량순" - : el === "change" - ? "등락율순" - : el} - - ))} - - - ); + return ( + + + {dropList.map((el) => ( + + {el === "capital" + ? "시가총액순" + : el === "trade" + ? "거래량순" + : el === "change" + ? "등락율순" + : el} + + ))} + + + ); }; export default Dropdown; diff --git a/src/Component/Game/GameHeader.tsx b/src/Component/Game/GameHeader.tsx index 26f5c21..b96e231 100644 --- a/src/Component/Game/GameHeader.tsx +++ b/src/Component/Game/GameHeader.tsx @@ -25,7 +25,10 @@ const GameHeader: React.FC = ({ text }) => { return ( <>{text} - {rule === "gameStocks" || rule === "total" || rule === "rank" ? ( + {rule === "gameStocks" || + rule === "result" || + rule === "rank" || + rule === "info" ? ( <> ) : ( diff --git a/src/Component/Game/GameMoney.tsx b/src/Component/Game/GameMoney.tsx index f89a1bc..f9cef8f 100644 --- a/src/Component/Game/GameMoney.tsx +++ b/src/Component/Game/GameMoney.tsx @@ -4,162 +4,167 @@ import { holding } from "../../constants/interface"; import { useState, useEffect } from "react"; import axios from "axios"; import { usePortfolioStore } from "../../store/usePortfolioStore"; +import { useGameStore } from "../../store/useGameStore"; import { useNavigate } from "react-router-dom"; import GameViewTab from "./gameViewTab"; const GameMoney = ({ - setBudget, - budget, + setBudget, + budget, }: { - setBudget: (budget: number) => void; - budget: number; + setBudget: (budget: number) => void; + budget: number; }) => { - const nav = useNavigate(); - - const [nickname, setNickname] = useState(""); // 닉네임 - const [total, setTotal] = useState(0); // 총 평가금액 - const [changeFromLast, setChangeFromLast] = useState(0); // 작년 대비 금액 - const [changeFromStart, setChangeFromStart] = useState(0); // 전체 대비 금액 - const [changeRateFromLast, setChangeRateFromLast] = useState(0); // 작년 대비 변동률 - const [changeRateFromStart, setChangeRateFromStart] = useState(0); // 전체 대비 변동률 - const [holdingList, setHoldingList] = useState([]); // 20xx년 주식가격 - const [showTable, setShowTable] = useState(false); - - const check = usePortfolioStore((state) => state.check); - - useEffect(() => { - axios - .get(`${process.env.REACT_APP_API_URL}/v1/game/user`, { - withCredentials: true, - headers: { - "Content-Type": "application/json", - }, - }) - .then((res) => { - if (res.status === 200) { - setNickname(res.data.nickname); - setBudget(res.data.budget); - setTotal(res.data.total); - setChangeFromLast(res.data.changeFromLast); - setChangeFromStart(res.data.changeFromStart); - setChangeRateFromLast(res.data.changeRateFromLast); - setChangeRateFromStart(res.data.changeRateFromStart); - setHoldingList(res.data.holdingList); - } else if (res.status === 401) { - alert("401"); - } - }) - .catch((e) => { - nav("/error"); - }); - }, [check]); - - const handleShowTable = () => { - setShowTable((prev) => !prev); - }; - - return ( - - - {nickname} 님의 계좌잔고 💰 - - - 거래가능금액 -
{formatPrice(budget)} 원
-
-
- - 총 평가금액 -
{formatPrice(total)} 원
-
- - 작년 대비 -
0 - ? "red" - : changeRateFromLast < 0 - ? "blue" - : "black", - }} - > - {formatPrice(changeFromLast)} 원 ({changeRateFromLast}%) -
-
- - 전체 대비 -
0 - ? "red" - : changeRateFromStart < 0 - ? "blue" - : "black", - }} - > - {formatPrice(changeFromStart)} 원 ({changeRateFromStart}%) -
-
- - - - - {showTable && } -
- ); + const nav = useNavigate(); + + const [nickname, setNickname] = useState(""); // 닉네임 + const [total, setTotal] = useState(0); // 총 평가금액 + const [changeFromLast, setChangeFromLast] = useState(0); // 작년 대비 금액 + const [changeFromStart, setChangeFromStart] = useState(0); // 전체 대비 금액 + const [changeRateFromLast, setChangeRateFromLast] = useState(0); // 작년 대비 변동률 + const [changeRateFromStart, setChangeRateFromStart] = useState(0); // 전체 대비 변동률 + const [holdingList, setHoldingList] = useState([]); // 20xx년 주식가격 + const [showTable, setShowTable] = useState(false); + + const check = usePortfolioStore((state) => state.check); + + // 페이지 유효성 검사 + const setCheckYear = useGameStore((state) => state.setCheckYear); + + useEffect(() => { + axios + .get(`${process.env.REACT_APP_API_URL}/v1/game/user`, { + withCredentials: true, + headers: { + "Content-Type": "application/json", + }, + }) + .then((res) => { + if (res.status === 200) { + setCheckYear(res.data.year); + setNickname(res.data.nickname); + setBudget(res.data.budget); + setTotal(res.data.total); + setChangeFromLast(res.data.changeFromLast); + setChangeFromStart(res.data.changeFromStart); + setChangeRateFromLast(res.data.changeRateFromLast); + setChangeRateFromStart(res.data.changeRateFromStart); + setHoldingList(res.data.holdingList); + } else if (res.status === 401) { + alert("401"); + } + }) + .catch((e) => { + nav("/error"); + }); + }, [check]); + + const handleShowTable = () => { + setShowTable((prev) => !prev); + }; + + return ( + + + {nickname} 님의 계좌잔고 💰 + + + 거래가능금액 +
{formatPrice(budget)} 원
+
+
+ + 총 평가금액 +
{formatPrice(total)} 원
+
+ + 작년 대비 +
0 + ? "red" + : changeRateFromLast < 0 + ? "blue" + : "black", + }} + > + {formatPrice(changeFromLast)} 원 ({changeRateFromLast}%) +
+
+ + 전체 대비 +
0 + ? "red" + : changeRateFromStart < 0 + ? "blue" + : "black", + }} + > + {formatPrice(changeFromStart)} 원 ({changeRateFromStart}%) +
+
+ + + + + {showTable && } +
+ ); }; const GameMoneyContainer = styled.div` - width: 85%; - height: auto; - border-radius: 20px; - box-shadow: 3px 3px 3px rgb(213, 213, 213); - padding: 20px 10px; - position: relative; - margin-top: 20px; - margin-bottom: 20px; + width: 85%; + height: auto; + border-radius: 20px; + box-shadow: 3px 3px 3px rgb(213, 213, 213); + padding: 20px 10px; + position: relative; + margin-top: 20px; + margin-bottom: 20px; `; const GameMoneyTitle = styled.div` - margin-bottom: 30px; + margin-bottom: 30px; `; const GameMoneyBalance = styled.div` - display: flex; - margin: 15px 0; - flex-direction: row; + display: flex; + margin: 15px 0; + flex-direction: row; `; const BalanceDetailButton = styled.div` - position: absolute; - top: 118px; - right: 20px; + position: absolute; + top: 118px; + right: 20px; `; const BalanceTitle = styled.div` - color: #6c6c6c; - width: 110px; + color: #6c6c6c; + width: 110px; `; const Button = styled.button` - width: 78px; - height: 40px; - border-radius: 50px; - border: none; - background-color: #f1f1f1; - cursor: pointer; - - &:hover { - background-color: #615efc; - color: white; - font-weight: 600; - } + width: 78px; + height: 40px; + border-radius: 50px; + border: none; + background-color: #f1f1f1; + cursor: pointer; + + &:hover { + background-color: #615efc; + color: white; + font-weight: 600; + } `; export default GameMoney; diff --git a/src/Component/Game/GameNews/gameNewsItem.tsx b/src/Component/Game/GameNews/gameNewsItem.tsx deleted file mode 100644 index 169681f..0000000 --- a/src/Component/Game/GameNews/gameNewsItem.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const GameNewsItem = () => { - return
게임용 뉴스 아이템
; -}; - -export default GameNewsItem; diff --git a/src/Component/Game/GameNews/gmaeNewsList.tsx b/src/Component/Game/GameNews/gmaeNewsList.tsx deleted file mode 100644 index 8455d30..0000000 --- a/src/Component/Game/GameNews/gmaeNewsList.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const GameNewsList = () => { - return
게임용 뉴스 리스으
; -}; - -export default GameNewsList; diff --git a/src/Component/Game/GameTradeSwipe.tsx b/src/Component/Game/GameTradeSwipe.tsx index 1b79302..241b746 100644 --- a/src/Component/Game/GameTradeSwipe.tsx +++ b/src/Component/Game/GameTradeSwipe.tsx @@ -9,270 +9,270 @@ import { formatPrice } from "../../util/gameUtil"; import styled from "styled-components"; interface GameTradeSwipeProps { - onClose: () => void; - isVisible: boolean; - year: string; - budget: number; + onClose: () => void; + isVisible: boolean; + year: string; + budget: number; } const GameTradeSwipe = ({ - onClose, - isVisible, - year, - budget, + onClose, + isVisible, + year, + budget, }: GameTradeSwipeProps) => { - const { stockData } = useStock(); - const [selectedStock, setSelectedStock] = useState<{ - stockId: number; - name: string; - } | null>(null); - const [quantity, setQuantity] = useState(1); - const [acting, setActing] = useState<"BUY" | "SELL">(); - const [isModalOpen, setIsModalOpen] = useState(false); - const [stockOptions, setStockOptions] = useState< - { stockId: number; name: string }[] - >([]); - const [currentPrice, setCurrentPrice] = useState(null); + const { stockData } = useStock(); + const [selectedStock, setSelectedStock] = useState<{ + stockId: number; + name: string; + } | null>(null); + const [quantity, setQuantity] = useState(1); + const [acting, setActing] = useState<"BUY" | "SELL">(); + const [isModalOpen, setIsModalOpen] = useState(false); + const [stockOptions, setStockOptions] = useState< + { stockId: number; name: string }[] + >([]); + const [currentPrice, setCurrentPrice] = useState(null); - const check = usePortfolioStore((state) => state.check); - const setCheck = usePortfolioStore((state) => state.setCheck); + const check = usePortfolioStore((state) => state.check); + const setCheck = usePortfolioStore((state) => state.setCheck); - // 모달 외부 클릭 감지하기 위해 ref 생성 - const modalRef = useRef(null); + // 모달 외부 클릭 감지하기 위해 ref 생성 + const modalRef = useRef(null); - // 모달 외부 클릭 시 닫기 처리 - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if ( - modalRef.current && - !modalRef.current.contains(event.target as Node) - ) { - onClose(); - } - }; - - document.addEventListener("mousedown", handleClickOutside); - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [onClose]); - - const handleStockChange = (direction: "left" | "right") => { - if (!selectedStock || stockOptions.length === 0) return; - const currentIndex = stockOptions.findIndex( - (stock) => stock.name === selectedStock.name - ); - const newIndex = - direction === "left" - ? (currentIndex - 1 + stockOptions.length) % stockOptions.length - : (currentIndex + 1) % stockOptions.length; - setSelectedStock(stockOptions[newIndex]); + // 모달 외부 클릭 시 닫기 처리 + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + modalRef.current && + !modalRef.current.contains(event.target as Node) + ) { + onClose(); + } }; - const handleQuantityChange = (direction: "left" | "right") => { - setQuantity((prev) => { - if (direction === "left" && prev > 1) { - return prev - 1; - } else if (direction === "right") { - return prev + 1; - } - return prev; - }); + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); }; + }, [onClose]); - const handleTradeConfirm = async () => { - const data = { - stockId: selectedStock?.stockId || 0, - quantity, - acting, - }; + const handleStockChange = (direction: "left" | "right") => { + if (!selectedStock || stockOptions.length === 0) return; + const currentIndex = stockOptions.findIndex( + (stock) => stock.name === selectedStock.name + ); + const newIndex = + direction === "left" + ? (currentIndex - 1 + stockOptions.length) % stockOptions.length + : (currentIndex + 1) % stockOptions.length; + setSelectedStock(stockOptions[newIndex]); + }; - try { - const res = await axios.post( - `${process.env.REACT_APP_API_URL}/v1/game/stock`, - data, - { withCredentials: true } - ); - if (res.status === 200) { - setCheck(!check); - swal({ title: "거래 성공", icon: "success" }); - setIsModalOpen(false); - setSelectedStock(stockOptions[0]); - setQuantity(1); - onClose(); - } - } catch (error: any) { - const errorMessage = - error.response?.data.message || "서버 오류가 발생했습니다."; - swal({ - title: "거래 실패", - text: errorMessage, - icon: "error", - }).then(() => { - setIsModalOpen(false); - // 모달을 닫지 않음, 에러 후 닫지 않는 로직 유지 - }); - } - }; + const handleQuantityChange = (direction: "left" | "right") => { + setQuantity((prev) => { + if (direction === "left" && prev > 1) { + return prev - 1; + } else if (direction === "right") { + return prev + 1; + } + return prev; + }); + }; - const openTradeConfirmModal = (action: "BUY" | "SELL") => { - setActing(action); - setIsModalOpen(true); + const handleTradeConfirm = async () => { + const data = { + stockId: selectedStock?.stockId || 0, + quantity, + acting, }; - const handleQuantityInputChange = (value: number) => { - setQuantity(value); - }; + try { + const res = await axios.post( + `${process.env.REACT_APP_API_URL}/v1/game/stock`, + data, + { withCredentials: true } + ); + if (res.status === 200) { + setCheck(!check); + swal({ title: "거래 성공", icon: "success" }); + setIsModalOpen(false); + setSelectedStock(stockOptions[0]); + setQuantity(1); + onClose(); + } + } catch (error: any) { + const errorMessage = + error.response?.data.message || "서버 오류가 발생했습니다."; + swal({ + title: "거래 실패", + text: errorMessage, + icon: "error", + }).then(() => { + setIsModalOpen(false); + // 모달을 닫지 않음, 에러 후 닫지 않는 로직 유지 + }); + } + }; - useEffect(() => { - if (stockData) { - const options = stockData.map((stock) => ({ - stockId: stock.stockId, - name: stock.name, - })); - setStockOptions(options); - if (!selectedStock && options.length > 0) { - setSelectedStock(options[0]); - setCurrentPrice(stockData[0].current); - } - } - if (selectedStock && stockData) { - const selectedStockData = stockData.find( - (stock) => stock.stockId === selectedStock.stockId - ); - setCurrentPrice(selectedStockData?.current || null); - } - }, [stockData, selectedStock]); + const openTradeConfirmModal = (action: "BUY" | "SELL") => { + setActing(action); + setIsModalOpen(true); + }; - return ( -
- - -
- { - setSelectedStock(stockOptions[0]); - setQuantity(1); - onClose(); - }} - > -
- 주식 거래하기 - handleStockChange("left")} - onRightClick={() => handleStockChange("right")} - currentPrice={currentPrice} // 현재 가격 전달 - /> - handleQuantityChange("left")} - onRightClick={() => handleQuantityChange("right")} - onQuantityChange={handleQuantityInputChange} // 수량 직접 입력 핸들러 전달 - /> -
거래가능금액: {formatPrice(budget)}
-
- 총 합계:{" "} - {currentPrice && quantity - ? formatPrice(currentPrice * quantity) - : 0} -
- - openTradeConfirmModal("SELL")} - disabled={quantity === 0} // 수량이 0이거나 빈 값일 때 비활성화 - > - 팔기 - - openTradeConfirmModal("BUY")} - disabled={quantity === 0} // 수량이 0이거나 빈 값일 때 비활성화 - > - 사기 - - -
-
- setIsModalOpen(false)} - stock={selectedStock?.name || ""} - quantity={quantity} - acting={acting!} - /> -
- ); + const handleQuantityInputChange = (value: number) => { + setQuantity(value); + }; + + useEffect(() => { + if (stockData) { + const options = stockData.map((stock) => ({ + stockId: stock.stockId, + name: stock.name, + })); + setStockOptions(options); + if (!selectedStock && options.length > 0) { + setSelectedStock(options[0]); + setCurrentPrice(stockData[0].current); + } + } + if (selectedStock && stockData) { + const selectedStockData = stockData.find( + (stock) => stock.stockId === selectedStock.stockId + ); + setCurrentPrice(selectedStockData?.current || null); + } + }, [stockData, selectedStock]); + + return ( +
+ + +
+ { + setSelectedStock(stockOptions[0]); + setQuantity(1); + onClose(); + }} + > +
+ 주식 거래하기 + handleStockChange("left")} + onRightClick={() => handleStockChange("right")} + currentPrice={currentPrice} // 현재 가격 전달 + /> + handleQuantityChange("left")} + onRightClick={() => handleQuantityChange("right")} + onQuantityChange={handleQuantityInputChange} // 수량 직접 입력 핸들러 전달 + /> +
거래가능금액: {formatPrice(budget)}
+
+ 총 합계:{" "} + {currentPrice && quantity + ? formatPrice(currentPrice * quantity) + : 0} +
+ + openTradeConfirmModal("SELL")} + disabled={quantity === 0} // 수량이 0이거나 빈 값일 때 비활성화 + > + 팔기 + + openTradeConfirmModal("BUY")} + disabled={quantity === 0} // 수량이 0이거나 빈 값일 때 비활성화 + > + 사기 + + +
+
+ setIsModalOpen(false)} + stock={selectedStock?.name || ""} + quantity={quantity} + acting={acting!} + /> +
+ ); }; const SwipeModal = styled.div<{ isOpen: boolean }>` - width: 90%; - position: fixed; - left: 50%; - bottom: ${(props) => (props.isOpen ? "0" : "-100%")}; - max-height: ${(props) => (props.isOpen ? "70vh" : "0")}; - height: 70vh; - max-width: 440px; - box-shadow: 0px -2px 10px rgba(0, 0, 0, 0.1); - transition: bottom 0.7 ease, max-height 0.7s ease; - z-index: 1; - overflow: hidden; - border-top-left-radius: 20px; - border-top-right-radius: 20px; - transform: translateX(-50%); - background-color: #f7f7f7; + width: 90%; + position: fixed; + left: 50%; + bottom: ${(props) => (props.isOpen ? "0" : "-100%")}; + max-height: ${(props) => (props.isOpen ? "70vh" : "0")}; + height: 70vh; + max-width: 440px; + box-shadow: 0px -2px 10px rgba(0, 0, 0, 0.1); + transition: bottom 0.7s ease, max-height 0.7s ease; + z-index: 1; + overflow: hidden; + border-top-left-radius: 20px; + border-top-right-radius: 20px; + transform: translateX(-50%); + background-color: #f7f7f7; `; const SwipeContainer = styled.div` - padding: 20px; - background-color: #f7f7f7; - overflow-y: auto; - height: 100%; - display: flex; - flex-direction: column; + padding: 20px; + background-color: #f7f7f7; + overflow-y: auto; + height: 100%; + display: flex; + flex-direction: column; `; const CloseButton = styled.button` - width: 100px; - height: 10px; - border-radius: 10px; - border: none; - background-color: #dfdfdf; - cursor: pointer; + width: 100px; + height: 10px; + border-radius: 10px; + border: none; + background-color: #dfdfdf; + cursor: pointer; `; const Title = styled.span` - font-size: 28px; - font-weight: 700; - color: #615efc; - margin-top: 30px; + font-size: 28px; + font-weight: 700; + color: #615efc; + margin-top: 30px; `; const TradeButtonGroup = styled.div` - display: flex; - justify-content: center; - align-items: center; - gap: 20px; - margin: 20px 0; + display: flex; + justify-content: center; + align-items: center; + gap: 20px; + margin: 20px 0; `; const TradeButton = styled.button` - width: 100px; - height: 40px; - border-radius: 15px; - background: ${({ disabled }) => - disabled - ? "radial-gradient(circle, #cccccc 0%, #e0e0e0 100%)" - : "radial-gradient(circle, #4834d4 0%, #686de0 100%)"}; - color: #fff; - border: none; - font-size: 14px; + width: 100px; + height: 40px; + border-radius: 15px; + background: ${({ disabled }) => + disabled + ? "radial-gradient(circle, #cccccc 0%, #e0e0e0 100%)" + : "radial-gradient(circle, #4834d4 0%, #686de0 100%)"}; + color: #fff; + border: none; + font-size: 14px; `; export default GameTradeSwipe; diff --git a/src/Component/Game/Rank/rankBox.tsx b/src/Component/Game/Rank/rankBox.tsx index 7c91040..d3fdf31 100644 --- a/src/Component/Game/Rank/rankBox.tsx +++ b/src/Component/Game/Rank/rankBox.tsx @@ -3,7 +3,7 @@ import * as S from "./rankBoxStyle"; import gold from "../../../img/goldMedal.png"; import silver from "../../../img/silverMedal.png"; import bronze from "../../../img/bronzeMedal.png"; -import { formatPrice, formatRate } from "../../../util/util"; +import { formatPrice, formatRateColor } from "../../../util/gameUtil"; const RankBox: React.FC = ({ nickname, @@ -24,7 +24,9 @@ const RankBox: React.FC = ({ {nickname} {formatPrice(total)} - {formatRate(profitRate)} % + + {profitRate.toFixed(1)} % + ); diff --git a/src/Component/Game/Rank/rankBoxStyle.ts b/src/Component/Game/Rank/rankBoxStyle.ts index f1424f0..0b243b7 100644 --- a/src/Component/Game/Rank/rankBoxStyle.ts +++ b/src/Component/Game/Rank/rankBoxStyle.ts @@ -4,14 +4,12 @@ import { Colors } from "../../../Styles/Colors"; export const Container = styled.div` display: flex; flex-direction: row; - /* justify-content: start; */ align-items: center; border-radius: 20px; margin-bottom: 1rem; padding: 0.8rem 1rem; box-shadow: 0px 6px 5px rgb(213, 213, 213); border: 1px solid rgb(213, 213, 213); - /* min-width: 95%; */ white-space: nowrap; gap: 1rem; `; @@ -45,7 +43,6 @@ export const Budget = styled.div` `; export const Rate = styled.div` - color: ${Colors.red}; font-family: "SCDream3"; text-align: right; width: 100px; diff --git a/src/Component/GoogleLogin/loginStyle.ts b/src/Component/GoogleLogin/loginStyle.ts index ccd0cde..625b733 100644 --- a/src/Component/GoogleLogin/loginStyle.ts +++ b/src/Component/GoogleLogin/loginStyle.ts @@ -35,4 +35,5 @@ export const Description = styled.p` export const Button = styled.button` border: none; background: none; + cursor: pointer; `; diff --git a/src/Component/Layout/Header/Header.tsx b/src/Component/Layout/Header/Header.tsx index a22ddbd..c6640c4 100644 --- a/src/Component/Layout/Header/Header.tsx +++ b/src/Component/Layout/Header/Header.tsx @@ -11,6 +11,7 @@ const HeaderStyle = styled.header` align-items: center; margin: 10px 0px; padding: 0px 20px; + cursor: pointer; @media (max-width: 768px) { width: 100%; height: 100%; diff --git a/src/Component/Layout/Header/Profile.tsx b/src/Component/Layout/Header/Profile.tsx index cf5d556..f2117a1 100644 --- a/src/Component/Layout/Header/Profile.tsx +++ b/src/Component/Layout/Header/Profile.tsx @@ -10,6 +10,7 @@ const ProfileContainer = styled.div` display: flex; align-items: center; gap: 1rem; + cursor: pointer; `; const ImgStyle = styled.img` @@ -17,6 +18,7 @@ const ImgStyle = styled.img` height: 35px; border: #615efc 1px solid; border-radius: 50%; + cursor: pointer; `; const LogoutBox = styled.div` @@ -31,6 +33,7 @@ const LogoutBox = styled.div` position: absolute; top: 40px; right: 40px; + cursor: pointer; `; const Profile = () => { diff --git a/src/Component/Layout/NavBar/NavBar.tsx b/src/Component/Layout/NavBar/NavBar.tsx index a867d84..c617edd 100644 --- a/src/Component/Layout/NavBar/NavBar.tsx +++ b/src/Component/Layout/NavBar/NavBar.tsx @@ -49,6 +49,7 @@ const NavBoxStyle = styled.div<{ $isActive?: boolean }>` justify-content: center; align-items: center; gap: 10px; + cursor: pointer; &:hover ${ImgStyle}, &:hover ${TextStyle} { color: ${Colors.main}; /* Change both icon and text color on hover */ diff --git a/src/Component/News/NewsItem.tsx b/src/Component/News/NewsItem.tsx index 59b8a1e..c3643b2 100644 --- a/src/Component/News/NewsItem.tsx +++ b/src/Component/News/NewsItem.tsx @@ -1,4 +1,5 @@ import { NewsProps } from "../../constants/interface"; +import { HiOutlineExternalLink } from "react-icons/hi"; const NewsItem: React.FC = ({ code, @@ -30,6 +31,7 @@ const NewsItem: React.FC = ({

{date}

+ ); }; diff --git a/src/Component/News/NewsList.tsx b/src/Component/News/NewsList.tsx index 4c141d4..174bfab 100644 --- a/src/Component/News/NewsList.tsx +++ b/src/Component/News/NewsList.tsx @@ -9,57 +9,56 @@ import { useLocation, useNavigate } from "react-router-dom"; import NoNews from "./\bnoNews"; const ListWrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - padding: 20px; - gap: 10px; + display: flex; + flex-direction: column; + align-items: center; + padding: 20px; + gap: 10px; + height: 400px; + overflow: scroll; + scrollbar-width: none; `; const NewsList: React.FC = () => { - // 나만의 뉴스 데이터 - const [news, setNews] = useState([]); - const navigate = useNavigate(); - const location = useLocation(); - // const change = usePortfolioStore((state) => state.change); + // 나만의 뉴스 데이터 + const [news, setNews] = useState([]); + const navigate = useNavigate(); + const location = useLocation(); + // const change = usePortfolioStore((state) => state.change); - useEffect(() => { - axios - .get( - `${process.env.REACT_APP_API_URL}/v1/news/user`, + useEffect(() => { + axios + .get( + `${process.env.REACT_APP_API_URL}/v1/news/user`, - { - withCredentials: true, - headers: { - "Content-Type": "application/json", - }, - } - ) - .then((res) => { - if (res.status === 200) { - setNews(res.data); - } else if (res.status === 204) { - setNews([]); - } else if (res.status === 401) { - navigate("/noLogin"); - } - }) - .catch((e) => {}); - }, [location, navigate]); + { + withCredentials: true, + headers: { + "Content-Type": "application/json", + }, + } + ) + .then((res) => { + if (res.status === 200) { + setNews(res.data); + } else if (res.status === 204) { + setNews([]); + } else if (res.status === 401) { + navigate("/noLogin"); + } + }) + .catch((e) => {}); + }, [location, navigate]); - return ( -
- - {news.length > 0 ? ( - news.map((news: NewsProps) => ( - - )) - ) : ( - - )} - -
- ); + return ( + + {news.length > 0 ? ( + news.map((news: NewsProps) => ) + ) : ( + + )} + + ); }; export default NewsList; diff --git a/src/Component/SearchBar/SearchBar.tsx b/src/Component/SearchBar/SearchBar.tsx index eea7b4c..3b69167 100644 --- a/src/Component/SearchBar/SearchBar.tsx +++ b/src/Component/SearchBar/SearchBar.tsx @@ -18,13 +18,20 @@ const SearchBar: React.FC = () => { }; const handleSearch = (e: React.ChangeEvent) => { - const input = e.target.value; + let input = e.target.value; + const regExp = /[ \{\}\[\]\/?.,;:|\)*~`!^\-_+┼<>@\#$%&\'\"\\\(\=]/gi; + if (regExp.test(input)) { + input = input.replace(regExp, ""); + } + // else { setKeyword(input); + // } }; useEffect(() => { if (keyword.trim() === "") { - setList([]); // Clear the list if the keyword is empty + setList([]); + return; } axios .get( @@ -60,6 +67,7 @@ const SearchBar: React.FC = () => { onChange={handleSearch} onFocus={searching} onBlur={searching} + value={keyword} /> {isOpen && ( diff --git a/src/Game/Loading/progressBar.tsx b/src/Game/Loading/progressBar.tsx index a3e0e89..940ab2c 100644 --- a/src/Game/Loading/progressBar.tsx +++ b/src/Game/Loading/progressBar.tsx @@ -1,9 +1,11 @@ import styled from "styled-components"; const Container = styled.div` - width: 100%; + width: 70%; max-width: 400px; - margin: 15px auto; + /* margin: 15px auto; */ + margin: 0.1rem 1rem 1rem 0.5rem; + background-color: rgba(255, 255, 255, 0.1); border-radius: 50px; padding: 5px; @@ -28,6 +30,7 @@ const Text = styled.span` font-weight: bold; z-index: 1; font-size: 12px; + margin-left: 0%.8; `; interface ProgressBarProps { diff --git a/src/Game/Shop/shop.tsx b/src/Game/Shop/shop.tsx index 5dc6b2b..b4d90b4 100644 --- a/src/Game/Shop/shop.tsx +++ b/src/Game/Shop/shop.tsx @@ -174,39 +174,54 @@ const Shop: React.FC = ({ selectedStock, budget, setBudget }) => { ) : ( -
+

Skrr News



-

+

{selectedStockData ? selectedStockData.name : ""}{" "} {""} {selectedTab.level} @@ -216,7 +231,7 @@ const Shop: React.FC = ({ selectedStock, budget, setBudget }) => {

{ return ( setOnModal(true)}> -

+

룰 설명

- + {onModal && ( diff --git a/src/Pages/404/noUser.tsx b/src/Pages/404/noUser.tsx index 296e704..e323774 100644 --- a/src/Pages/404/noUser.tsx +++ b/src/Pages/404/noUser.tsx @@ -2,8 +2,6 @@ import { useNavigate } from "react-router-dom"; import styled from "styled-components"; import { GoAlert } from "react-icons/go"; import { Colors } from "../../Styles/Colors"; -import Header from "../../Component/Layout/Header/Header"; -import NavBar from "../../Component/Layout/NavBar/NavBar"; const Box = styled.div` height: 60vh; diff --git a/src/Pages/game/infoPage.tsx b/src/Pages/game/infoPage.tsx index 3ad2be0..a086451 100644 --- a/src/Pages/game/infoPage.tsx +++ b/src/Pages/game/infoPage.tsx @@ -1,14 +1,54 @@ -import { useParams } from "react-router-dom"; +import { useParams, useNavigate } from "react-router-dom"; import ExchangeStoreMain from "../../Game/Shop/exchangeStoreMain"; import GameHeader from "../../Component/Game/GameHeader"; +import { GoAlert } from "react-icons/go"; +import { Colors } from "../../Styles/Colors"; +import Button from "../../Component/Button/button"; +import { useGameStore } from "../../store/useGameStore"; const InfoPage = () => { const { year } = useParams<{ year: string }>(); + const yearValue = year || "2014"; + const yearNumber = parseInt(yearValue, 10); + const checkYear = useGameStore((state) => state.checkYear); + const nav = useNavigate(); + return ( -
- - -
+ <> + {yearNumber !== checkYear ? ( +
+ +

정상적인 접근 경로가 아닙니다

+
+ ) : ( +
+ + +
+ )} + ); }; diff --git a/src/Pages/game/playPage.tsx b/src/Pages/game/playPage.tsx index ec36cca..c904916 100644 --- a/src/Pages/game/playPage.tsx +++ b/src/Pages/game/playPage.tsx @@ -1,5 +1,4 @@ -import { useNavigate, useParams, useLocation } from "react-router-dom"; -import { useEffect } from "react"; +import { useNavigate, useParams } from "react-router-dom"; import GameButtons from "../../Component/Game/GameButtons"; import GameHeader from "../../Component/Game/GameHeader"; import GameMoney from "../../Component/Game/GameMoney"; @@ -13,159 +12,194 @@ import "./gameStyle.css"; import HappyNewYearModal from "./HappyNewYearModal"; import { useStock } from "../../store/stockContext"; import { usePortfolioStore } from "../../store/usePortfolioStore"; +import { useGameStore } from "../../store/useGameStore"; import ProgressBar from "../../Game/Loading/progressBar"; +import { GoAlert } from "react-icons/go"; +import { Colors } from "../../Styles/Colors"; +import Button from "../../Component/Button/button"; const Container = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; `; const PlayPage = () => { - const { year } = useParams<{ year: string }>(); - const { stockData, setStockData } = useStock(); - const nav = useNavigate(); - const yearValue = year || "2014"; - const [isTradeModalVisible, setIsTradeModalVisible] = useState(false); - const [isPassModalVisible, setIsPassModalVisible] = useState(false); - const [isHappyNewYearModal, setIsHappyNewYearModal] = useState(false); - const [budget, setBudget] = useState(0); - const [loading, setLoading] = useState(false); - - const check = usePortfolioStore((state) => state.check); - const setCheck = usePortfolioStore((state) => state.setCheck); - - const startYear = 2014; - const lastYear = 2023; - - // 년도 진행률 표시 - const calculateProgress = () => { - return ( - ((parseInt(yearValue, 10) - startYear) / (lastYear - startYear)) * - 100 + const { year } = useParams<{ year?: string }>(); + const { stockData, setStockData } = useStock(); + const nav = useNavigate(); + const yearValue = year || "2014"; + // 페이지 유효성 검사를 위한 변수 + const yearNumber = parseInt(yearValue, 10); + const [isTradeModalVisible, setIsTradeModalVisible] = useState(false); + const [isPassModalVisible, setIsPassModalVisible] = useState(false); + const [isHappyNewYearModal, setIsHappyNewYearModal] = useState(false); + const [budget, setBudget] = useState(0); + const [loading, setLoading] = useState(false); + + const checkYear = useGameStore((state) => state.checkYear); + const check = usePortfolioStore((state) => state.check); + const setCheck = usePortfolioStore((state) => state.setCheck); + + const startYear = 2014; + const lastYear = 2023; + + // 년도 진행률 표시 + const calculateProgress = () => { + return ( + ((parseInt(yearValue, 10) - startYear) / (lastYear - startYear)) * 100 + ); + }; + + const [progress, setProgress] = useState(calculateProgress); + + // 거래하기 모달 핸들러 + const openTradeModal = () => { + setIsTradeModalVisible(true); + }; + + const closeTradeModal = () => { + setIsTradeModalVisible(false); + }; + + // 넘어가기 모달 핸들러 + const openPassModal = () => { + setIsPassModalVisible(true); + }; + + const closePassModal = () => { + setIsPassModalVisible(false); + }; + + // 넘어가기 버튼 누르면 중간결과 호출 + const handleConfirmPass = async () => { + try { + setLoading(true); + setIsHappyNewYearModal(true); + setIsPassModalVisible(false); + + if (year === "2023") { + nav("/game/result/total"); + } else { + const response = await axios.get( + `${process.env.REACT_APP_API_URL}/v1/game/interim`, + { + withCredentials: true, + headers: { + "Content-Type": "application/json", + }, + } ); - }; - - const [progress, setProgress] = useState(calculateProgress); - - // 거래하기 모달 핸들러 - const openTradeModal = () => { - setIsTradeModalVisible(true); - }; - - const closeTradeModal = () => { - setIsTradeModalVisible(false); - }; - - // 넘어가기 모달 핸들러 - const openPassModal = () => { - setIsPassModalVisible(true); - }; - - const closePassModal = () => { - setIsPassModalVisible(false); - }; - - // 넘어가기 버튼 누르면 중간결과 호출 - const handleConfirmPass = async () => { - try { - setLoading(true); - setIsHappyNewYearModal(true); - setIsPassModalVisible(false); - - if (year === "2023") { - console.log("게임 끝"); - nav("/game/result/total"); - } else { - const response = await axios.get( - `${process.env.REACT_APP_API_URL}/v1/game/interim`, - { - withCredentials: true, - headers: { - "Content-Type": "application/json", - }, - } - ); - if (response.status === 200) { - if ( - (parseInt(yearValue, 10) + 1).toString() === - response.data.year.toString() - ) { - const updatedStockList = response.data.stockList; - setStockData(updatedStockList); - } - - // API 완료 후 모달 닫고 페이지 이동 - setIsHappyNewYearModal(false); - const nextYear = (parseInt(yearValue, 10) + 1).toString(); - nav(`/game/play/${nextYear}`); - setCheck(!check); - setProgress( - ((parseInt(nextYear, 10) - startYear) / - (lastYear - startYear)) * - 100 - ); - } - } - } catch (error) { - console.error(error); - } finally { - setLoading(false); - closePassModal(); + if (response.status === 200) { + if ( + (parseInt(yearValue, 10) + 1).toString() === + response.data.year.toString() + ) { + const updatedStockList = response.data.stockList; + setStockData(updatedStockList); + } + + // API 완료 후 모달 닫고 페이지 이동 + setIsHappyNewYearModal(false); + const nextYear = (parseInt(yearValue, 10) + 1).toString(); + nav(`/game/play/${nextYear}`); + setCheck(!check); + setProgress( + ((parseInt(nextYear, 10) - startYear) / (lastYear - startYear)) * + 100 + ); } - }; - - return ( + } + } catch (error) { + console.error(error); + } finally { + setLoading(false); + closePassModal(); + } + }; + + return ( + <> + {yearNumber !== checkYear ? ( +
+ +

정상적인 접근 경로가 아닙니다

+
+ ) : ( - - -
-
-

- 게임 진행도 -

-
- -
- - - - - - + + +
+ {/*
*/} + {/*

+ 게임 진행도 +

*/} + {/*
*/} + +
+ + + + + +
- ); + )} + + ); }; export default PlayPage; diff --git a/src/Pages/game/rank.tsx b/src/Pages/game/rank.tsx index 72c8eab..1a17210 100644 --- a/src/Pages/game/rank.tsx +++ b/src/Pages/game/rank.tsx @@ -4,7 +4,15 @@ import { RankDataProps, RankListProps } from "../../constants/interface"; import axios from "axios"; import RankList from "../../Component/Game/Rank/rankList"; import BentoBar from "../../Game/Main/BentoBar/bentoBar"; -import swal from "sweetalert"; +import styled from "styled-components"; + +const TitleContainer = styled.div` + display: flex; + flex-direction: row; + margin-top: 1rem; + border-bottom: 1px solid black; + padding-bottom: 0.5rem; +`; const Rank = () => { const [rankList, setRankList] = useState([]); @@ -29,6 +37,12 @@ const Rank = () => { return (
+ +

순위

+

닉네임

+

수익금

+

수익률

+
diff --git a/src/Pages/game/totalResult.tsx b/src/Pages/game/totalResult.tsx index fb5be86..d0ca08b 100644 --- a/src/Pages/game/totalResult.tsx +++ b/src/Pages/game/totalResult.tsx @@ -22,6 +22,7 @@ const SkrrContainer = styled.div` display: flex; flex-direction: row; position: relative; + margin-top: 1.5rem; `; const ImgStyle = styled.img` @@ -38,9 +39,9 @@ const BtnBox = styled.div` flex-direction: row; align-items: center; gap: 1rem; - margin-top: 0.5rem; margin-left: auto; margin-right: 2.5rem; + margin-top: 1rem; `; const NickBox = styled.div` @@ -106,7 +107,13 @@ const TotalResult = () => { ) .then((res) => { if (res.status === 200) { - nav("/game/rank"); + { + swal({ + title: "랭킹 등록 완료!", + text: "랭킹이 등록되었습니다.", + icon: "success", + }); + } } else { { swal({ @@ -128,10 +135,7 @@ const TotalResult = () => { - -

{user}

-

의 점수를

-
+ {user}