diff --git a/client/src/components/EntireList/EntireList.tsx b/client/src/components/EntireList/EntireList.tsx index 0fcf056..aa8957b 100644 --- a/client/src/components/EntireList/EntireList.tsx +++ b/client/src/components/EntireList/EntireList.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from "react"; import styled from "styled-components"; +import { motion } from "framer-motion"; import Header from "./Header"; import StockItem from "./StockItem"; import useCompanyData from "../../hooks/useCompanyData"; @@ -7,19 +8,6 @@ import { useSelector } from "react-redux"; // πŸ‘ˆ μΆ”κ°€ import { StateProps } from "../../models/stateProps"; // πŸ‘ˆ μΆ”κ°€ import useGetCash from "../../hooks/useGetCash"; -/* - πŸ”΄ μˆ˜μ •μ‚¬ν•­ - 1) λΆˆν•„μš”ν•œ μ»΄ν¬λ„ŒνŠΈ μ‚¬μš© : Divider μ»΄ν¬λ„ŒνŠΈ - -> ꡬ뢄선 (border) μ„€μ •μœ„ν•΄ μƒμ„±ν•œ κ²ƒμœΌλ‘œ λ³΄μ΄λ‚˜, μƒμœ„ μ»΄ν¬λ„ŒνŠΈ λ„ˆλΉ„ μ„€μ •μœΌλ‘œ ν•΄κ²° κ°€λŠ₯ (λΆˆν•„μš”ν•˜μ—¬ μ£Όμ„μ²˜λ¦¬) - -> μ‚­μ œν•˜λŠ” 게 쒋을 것 κ°™μŒ - - 2) ν˜„κΈˆ λ³΄μœ λŸ‰ μ»΄ν¬λ„ŒνŠΈ 쑰건뢀 λ Œλ”λ§ λ³€κ²½ (둜그인 ν•„μš”ν•œ μ„œλΉ„μŠ€ X -> 둜그인 ν•΄μ•Ό 화면에 λ‚˜νƒ€λ‚˜λ„λ‘) - - 3) header μ»¨ν…Œμ΄λ„ˆ λΆ€λΆ„ height 43px둜 κ³ μ • - - 4) κΈˆμ•‘μ— νšŒκ³„ λ‹¨μœ„ (toLocaleString) 적용 -> 이λ₯Ό μœ„ν•΄ useEffect, useState ν™œμš© - */ - const holdingAmountText = "보유 ν˜„κΈˆ"; const amountUnit = "원"; @@ -51,29 +39,28 @@ const EntireList: React.FC = ({ currentListType, onChangeListTy // πŸ”΄ return ( - - -
- - {/* */} - - {/* {isLogin == 0 ? (둜그인이 ν•„μš”ν•œ μ„œλΉ„μŠ€ μž…λ‹ˆλ‹€.) : (ν˜„κΈˆ λ³΄μœ λŸ‰: {holdingsAmount}원)} */} - - {isLogin === 1 && ( - <> -
{holdingAmountText}
-
- {holdingCash} {amountUnit} -
- - )} -
-
- {/* */} - - {isLoading ?
: isError ?
Error fetching data
: companiesList.map((company) => )} -
- + + + +
+ + + + {isLogin === 1 && ( + <> +
{holdingAmountText}
+
+ {holdingCash} {amountUnit} +
+ + )} +
+
+ + {isLoading ?
: isError ?
Error fetching data
: companiesList.map((company) => )} +
+ + ); }; diff --git a/client/src/components/HoldingList/HoldingList.tsx b/client/src/components/HoldingList/HoldingList.tsx index 18d03ea..76c4fa4 100644 --- a/client/src/components/HoldingList/HoldingList.tsx +++ b/client/src/components/HoldingList/HoldingList.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import styled from "styled-components"; +import { motion } from "framer-motion"; import Header from "./Header"; import StockItem from "./StockItem"; import useGetStockHolds from "../../hooks/useGetStockholds"; @@ -38,38 +39,40 @@ const HoldingList: React.FC = ({ currentListType, onChangeListTy }; return ( - - -
- - {/* */} - - -
{evalutationProfitText}
-
- {totalEvaluationProfit.toLocaleString()} {profitUnit} -
-
-
- {/* */} - - {isLogin === 0 ? ( - - ) : isLoading || isCompanyDataLoading ? ( -
- ) : isError || isCompanyDataError ? ( -
Error fetching data
- ) : ( - Array.isArray(stockHolds) && - stockHolds.length > 0 && // 여기에 쑰건을 μΆ”κ°€ν•©λ‹ˆλ‹€ - stockHolds.map((stockHold: StockItemProps["stockData"]) => { - const matchedCompany = companyData ? companyData.find((company) => company.companyId === stockHold.companyId) : undefined; - - return matchedCompany ? : null; - }) - )} -
- + + + +
+ + {/* */} + + +
{evalutationProfitText}
+
+ {totalEvaluationProfit.toLocaleString()} {profitUnit} +
+
+
+ {/* */} + + {isLogin === 0 ? ( + + ) : isLoading || isCompanyDataLoading ? ( +
+ ) : isError || isCompanyDataError ? ( +
Error fetching data
+ ) : ( + Array.isArray(stockHolds) && + stockHolds.length > 0 && // 여기에 쑰건을 μΆ”κ°€ν•©λ‹ˆλ‹€ + stockHolds.map((stockHold: StockItemProps["stockData"]) => { + const matchedCompany = companyData ? companyData.find((company) => company.companyId === stockHold.companyId) : undefined; + + return matchedCompany ? : null; + }) + )} +
+ + ); }; diff --git a/client/src/components/Logins/EmailLogin.tsx b/client/src/components/Logins/EmailLogin.tsx index a62ac21..4e8fc9d 100644 --- a/client/src/components/Logins/EmailLogin.tsx +++ b/client/src/components/Logins/EmailLogin.tsx @@ -1,6 +1,7 @@ import axios from "axios"; import styled from "styled-components"; import React, { useState } from "react"; +import { motion } from "framer-motion"; import { setLoginState } from "../../reducer/member/loginSlice"; import { useDispatch } from "react-redux"; import setAutoLogoutAlarm from "../../utils/setAutoLogoutAlarm"; @@ -72,20 +73,22 @@ const EmailLoginModal: React.FC = ({ onClose, onSignup }) return ( - - × - {titleText} - - handleEnterPress(event, "password")} /> - - handleEnterPress(event, "loginButton")} /> - {generalError && {generalError}} - {findPasswordText} - {loginButtonText} - - {noAccountText} {registerButtonText} - - + + + × + {titleText} + + handleEnterPress(event, "password")} /> + + handleEnterPress(event, "loginButton")} /> + {generalError && {generalError}} + {findPasswordText} + {loginButtonText} + + {noAccountText} {registerButtonText} + + + ); }; diff --git a/client/src/components/Logins/OAuthLogin.tsx b/client/src/components/Logins/OAuthLogin.tsx index 606d6bd..6f284e1 100644 --- a/client/src/components/Logins/OAuthLogin.tsx +++ b/client/src/components/Logins/OAuthLogin.tsx @@ -1,8 +1,8 @@ - import React from "react"; import styled from "styled-components"; import GoogleLoginButton from "./GoogleLoginButton"; import KakaoLoginButton from "./KakaoLoginButton"; +import { motion } from "framer-motion"; const OAuthLoginModal: React.FC = ({ onClose, onEmailLoginClick, onEmailSignupClick }) => { const titleText = "둜그인"; @@ -16,17 +16,19 @@ const OAuthLoginModal: React.FC = ({ onClose, onEmailLoginClick return ( - - × - {titleText} - - - {orText} - - {emailLoginText} - {emailSignupText} - - + + + × + {titleText} + + + {orText} + + {emailLoginText} + {emailSignupText} + + + ); }; @@ -35,12 +37,11 @@ export default OAuthLoginModal; //λ³€μˆ˜ νƒ€μž… interface LoginModalProps { - onClose: () => void; onEmailLoginClick: () => void; onEmailSignupClick: () => void; - onWatchListClick?: () => void; // '?'λ₯Ό μ‚¬μš©ν•˜μ—¬ μ„ νƒμ μœΌλ‘œ λ§Œλ“­λ‹ˆλ‹€. - onHoldingsClick?: () => void; // '?'λ₯Ό μ‚¬μš©ν•˜μ—¬ μ„ νƒμ μœΌλ‘œ λ§Œλ“­λ‹ˆλ‹€. + onWatchListClick?: () => void; // '?'λ₯Ό μ‚¬μš©ν•˜μ—¬ μ„ νƒμ μœΌλ‘œ λ§Œλ“­λ‹ˆλ‹€. + onHoldingsClick?: () => void; // '?'λ₯Ό μ‚¬μš©ν•˜μ—¬ μ„ νƒμ μœΌλ‘œ λ§Œλ“­λ‹ˆλ‹€. } // λͺ¨λ‹¬μ°½ 띄웠을 λ•Œ λ°°κ²½ μŠ€νƒ€μΌ diff --git a/client/src/components/MarketComponents/MarketKospiChart.tsx b/client/src/components/MarketComponents/MarketKospiChart.tsx index aa33798..b0e21b1 100644 --- a/client/src/components/MarketComponents/MarketKospiChart.tsx +++ b/client/src/components/MarketComponents/MarketKospiChart.tsx @@ -7,7 +7,9 @@ import axios from "axios"; const MarketkospiChart = () => { const [kospiData, setKospiData] = useState([]); - const { data, isLoading, error } = useQuery("kospi", getKospiData, {}); + const { data, isLoading, error } = useQuery("kospi", getKospiData, { + staleTime: Infinity, + }); useEffect(() => { if (data) { diff --git a/client/src/components/Profile/CashModal.tsx b/client/src/components/Profile/CashModal.tsx index 92df8cc..5912600 100644 --- a/client/src/components/Profile/CashModal.tsx +++ b/client/src/components/Profile/CashModal.tsx @@ -1,141 +1,142 @@ -import React, { useState } from 'react'; -import styled from 'styled-components'; -import { useDispatch} from 'react-redux'; // <-- Import useSelector -import { useCreateCash, useResetCash } from '../../hooks/useCash'; -import useGetCash from '../../hooks/useGetCash'; -import useGetCashId from '../../hooks/useGetCashId'; -import { setCashId, setMoney } from '../../reducer/cash/cashSlice'; - - +import React, { useState } from "react"; +import styled from "styled-components"; +import { motion } from "framer-motion"; +import { useDispatch } from "react-redux"; // <-- Import useSelector +import { useCreateCash, useResetCash } from "../../hooks/useCash"; +import useGetCash from "../../hooks/useGetCash"; +import useGetCashId from "../../hooks/useGetCashId"; +import { setCashId, setMoney } from "../../reducer/cash/cashSlice"; const CashModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { - - // μƒνƒœ 및 λ³€μˆ˜ μ΄ˆκΈ°ν™” - const titleText = "ν˜„κΈˆμƒμ„±/μž¬μƒμ„±"; - const cashCreationPlaceholder = "1λ°±λ§Œμ›~5얡원 사이λ₯Ό μž…λ ₯ν•˜μ„Έμš”"; - const createCashButtonText = "ν˜„κΈˆ 생성"; - const cashInputPlaceholder = "1λ°±λ§Œμ›~5얡원 사이λ₯Ό μž…λ ₯ν•˜μ„Έμš”"; - const resetButtonText = "ν˜„κΈˆ μž¬μƒμ„±"; - // const refreshButtonText ="μƒˆλ‘œκ³ μΉ¨"; - - const [errorMessage, setErrorMessage] = useState(null); // μ—λŸ¬ λ©”μ‹œμ§€ μƒνƒœ λ³€μˆ˜ μΆ”κ°€ - - const dispatch = useDispatch(); - - // useGetCash 훅을 μ‚¬μš©ν•˜μ—¬ ν˜„κΈˆ λ³΄μœ λŸ‰ κ°€μ Έμ˜€κΈ° - const { cashData: holdingsAmount } = useGetCash(); // πŸ‘ˆ useGetCash 훅을 μ‚¬μš©ν•˜μ—¬ ν˜„κΈˆ λ³΄μœ λŸ‰ 데이터λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€. - - // useGetCashId 훅을 μ‚¬μš©ν•˜μ—¬ cashId κ°€μ Έμ˜€κΈ° - const { cashData: cashId, cashError } = useGetCashId(); - - const createCashMutation = useCreateCash(); - const updateCashMutation = useResetCash(); - - const [cashInput, setCashInput] = useState('0'); - const [initialAmount, setInitialAmount] = useState('0'); // ν˜„κΈˆ 생성을 μœ„ν•œ μƒνƒœ λ³€μˆ˜ - - // ν˜„κΈˆ 생성 및 cashId μ „μ—­ μ €μž₯ ν•¨μˆ˜ - const handleCreateCash = () => { - createCashMutation.mutate(initialAmount, { - onSuccess: () => { - window.location.reload(); - }, - onError: (error) => { - // μ—λŸ¬ 처리 - const err = error as Error; - setErrorMessage(err?.message || "ν˜„κΈˆ 생성에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."); - } - }); - }; - - // μž…λ ₯ν•œ κΈˆμ•‘μœΌλ‘œ ν˜„κΈˆ 리셋 ν•¨μˆ˜ - const handleCashReset = () => { - if (cashId) { - const numericCashAmount = cashInput; - updateCashMutation.mutate({ money: numericCashAmount }, { - onSuccess: () => { - dispatch(setMoney(numericCashAmount)); - dispatch(setCashId(cashId)); - window.location.reload(); - }, - onError: (error) => { - // μ—λŸ¬ 처리 - const err = error as Error; - setErrorMessage(err?.message || "ν˜„κΈˆ μž¬μƒμ„±μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."); - } - }); - } else { - console.error("cashId is null or not a valid number."); + // μƒνƒœ 및 λ³€μˆ˜ μ΄ˆκΈ°ν™” + const titleText = "ν˜„κΈˆμƒμ„±/μž¬μƒμ„±"; + const cashCreationPlaceholder = "1λ°±λ§Œμ›~5얡원 사이λ₯Ό μž…λ ₯ν•˜μ„Έμš”"; + const createCashButtonText = "ν˜„κΈˆ 생성"; + const cashInputPlaceholder = "1λ°±λ§Œμ›~5얡원 사이λ₯Ό μž…λ ₯ν•˜μ„Έμš”"; + const resetButtonText = "ν˜„κΈˆ μž¬μƒμ„±"; + // const refreshButtonText ="μƒˆλ‘œκ³ μΉ¨"; + + const [errorMessage, setErrorMessage] = useState(null); // μ—λŸ¬ λ©”μ‹œμ§€ μƒνƒœ λ³€μˆ˜ μΆ”κ°€ + + const dispatch = useDispatch(); + + // useGetCash 훅을 μ‚¬μš©ν•˜μ—¬ ν˜„κΈˆ λ³΄μœ λŸ‰ κ°€μ Έμ˜€κΈ° + const { cashData: holdingsAmount } = useGetCash(); // πŸ‘ˆ useGetCash 훅을 μ‚¬μš©ν•˜μ—¬ ν˜„κΈˆ λ³΄μœ λŸ‰ 데이터λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€. + + // useGetCashId 훅을 μ‚¬μš©ν•˜μ—¬ cashId κ°€μ Έμ˜€κΈ° + const { cashData: cashId, cashError } = useGetCashId(); + + const createCashMutation = useCreateCash(); + const updateCashMutation = useResetCash(); + + const [cashInput, setCashInput] = useState("0"); + const [initialAmount, setInitialAmount] = useState("0"); // ν˜„κΈˆ 생성을 μœ„ν•œ μƒνƒœ λ³€μˆ˜ + + // ν˜„κΈˆ 생성 및 cashId μ „μ—­ μ €μž₯ ν•¨μˆ˜ + const handleCreateCash = () => { + createCashMutation.mutate(initialAmount, { + onSuccess: () => { + window.location.reload(); + }, + onError: (error) => { + // μ—λŸ¬ 처리 + const err = error as Error; + setErrorMessage(err?.message || "ν˜„κΈˆ 생성에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."); + }, + }); + }; + + // μž…λ ₯ν•œ κΈˆμ•‘μœΌλ‘œ ν˜„κΈˆ 리셋 ν•¨μˆ˜ + const handleCashReset = () => { + if (cashId) { + const numericCashAmount = cashInput; + updateCashMutation.mutate( + { money: numericCashAmount }, + { + onSuccess: () => { + dispatch(setMoney(numericCashAmount)); + dispatch(setCashId(cashId)); + window.location.reload(); + }, + onError: (error) => { + // μ—λŸ¬ 처리 + const err = error as Error; + setErrorMessage(err?.message || "ν˜„κΈˆ μž¬μƒμ„±μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."); + }, } - }; - - const validateCashInput = (inputValue: string) => { - const numericValue = parseInt(inputValue.replace(/,/g, ''), 10); - if (isNaN(numericValue) || numericValue < 1000000 || numericValue > 500000000) { - setErrorMessage("100λ§Œμ—μ„œ 5μ–΅ μ‚¬μ΄μ˜ 수λ₯Ό μž…λ ₯ν•˜μ„Έμš”"); - } else { - setErrorMessage(null); - } - }; + ); + } else { + console.error("cashId is null or not a valid number."); + } + }; + + const validateCashInput = (inputValue: string) => { + const numericValue = parseInt(inputValue.replace(/,/g, ""), 10); + if (isNaN(numericValue) || numericValue < 1000000 || numericValue > 500000000) { + setErrorMessage("100λ§Œμ—μ„œ 5μ–΅ μ‚¬μ΄μ˜ 수λ₯Ό μž…λ ₯ν•˜μ„Έμš”"); + } else { + setErrorMessage(null); + } + }; - const handleEnterPress = (event: React.KeyboardEvent, action: () => void) => { - if (event.key === 'Enter') { - action(); - } - }; + const handleEnterPress = (event: React.KeyboardEvent, action: () => void) => { + if (event.key === "Enter") { + action(); + } + }; -return ( + return ( + - × - {titleText} - - {/* cashIdκ°€ μ—†κ±°λ‚˜ cashId μš”μ²­μ— 였λ₯˜κ°€ 있으면 ν˜„κΈˆ 생성 UI ν‘œμ‹œ */} - {(!cashId || cashError) && ( -
- { - setInitialAmount(e.target.value); - validateCashInput(e.target.value); // μž…λ ₯ κ°’ λ³€κ²½ μ‹œ μœ νš¨μ„± 검사 - }} - placeholder={cashCreationPlaceholder} - onKeyDown={(event) => handleEnterPress(event, handleCreateCash)} - /> - - {createCashButtonText} -
- )} - - {/* cashIdκ°€ 있으면 ν˜„κΈˆ 리셋 UI ν‘œμ‹œ */} - {cashId && !cashError && ( -
- { - setCashInput(e.target.value); - validateCashInput(e.target.value); // μž…λ ₯ κ°’ λ³€κ²½ μ‹œ μœ νš¨μ„± 검사 - }} - placeholder={cashInputPlaceholder} - onKeyDown={(event) => handleEnterPress(event, handleCashReset)} - /> - - {resetButtonText} -
- )} + × + {titleText} + {/* cashIdκ°€ μ—†κ±°λ‚˜ cashId μš”μ²­μ— 였λ₯˜κ°€ 있으면 ν˜„κΈˆ 생성 UI ν‘œμ‹œ */} + {(!cashId || cashError) && (
- - ν˜„κΈˆ λ³΄μœ λŸ‰: {holdingsAmount}원 - {errorMessage && {errorMessage}} - + { + setInitialAmount(e.target.value); + validateCashInput(e.target.value); // μž…λ ₯ κ°’ λ³€κ²½ μ‹œ μœ νš¨μ„± 검사 + }} + placeholder={cashCreationPlaceholder} + onKeyDown={(event) => handleEnterPress(event, handleCreateCash)} + /> + + {createCashButtonText}
+ )} + + {/* cashIdκ°€ 있으면 ν˜„κΈˆ 리셋 UI ν‘œμ‹œ */} + {cashId && !cashError && ( +
+ { + setCashInput(e.target.value); + validateCashInput(e.target.value); // μž…λ ₯ κ°’ λ³€κ²½ μ‹œ μœ νš¨μ„± 검사 + }} + placeholder={cashInputPlaceholder} + onKeyDown={(event) => handleEnterPress(event, handleCashReset)} + /> + + {resetButtonText} +
+ )} + +
+ + ν˜„κΈˆ λ³΄μœ λŸ‰: {holdingsAmount}원{errorMessage && {errorMessage}} + +
+
-); - + ); }; export default CashModal; @@ -159,7 +160,7 @@ const ModalContainer = styled.div` background-color: white; padding: 20px; width: 400px; - height:230px; + height: 230px; border-radius: 10px; display: flex; flex-direction: column; @@ -176,57 +177,57 @@ const CloseButton = styled.button` position: absolute; top: 10px; right: 10px; - background: #FFFFFF; - border-radius:5px; + background: #ffffff; + border-radius: 5px; border: 1px solid lightgray; font-size: 1.5rem; cursor: pointer; `; const StyledButton = styled.button` - padding: 10px 15px; - background-color: white; - color: darkslategray; - border: 1px solid darkslategray; - border-radius: 5px; - margin-bottom:5px; - cursor: pointer; - - //ν˜Έλ²„ μ‹œ νšŒμƒ‰ - &:hover { - background-color: #f2f2f2; - } + padding: 10px 15px; + background-color: white; + color: darkslategray; + border: 1px solid darkslategray; + border-radius: 5px; + margin-bottom: 5px; + cursor: pointer; + + //ν˜Έλ²„ μ‹œ νšŒμƒ‰ + &:hover { + background-color: #f2f2f2; + } `; const CashInput = styled.input` - padding: 10px; - border: 1px solid lightgray; - border-radius: 5px; - margin-right: 10px; + padding: 10px; + border: 1px solid lightgray; + border-radius: 5px; + margin-right: 10px; `; const ReceiveButton = styled(StyledButton)``; const CashCreationInput = styled.input` - padding: 10px; - border: 1px solid lightgray; - border-radius: 5px; - margin-right: 10px; + padding: 10px; + border: 1px solid lightgray; + border-radius: 5px; + margin-right: 10px; `; const CreateCashButton = styled(StyledButton)``; const Content = styled.p` - margin: 15px 0; // 간격 μ‘°μ • - font-size: 1.1rem; // 폰트 크기 증가 - line-height: 1.5; - color: #555; // 색상 λ³€κ²½ - text-align: center; // ν…μŠ€νŠΈ 쀑앙 μ •λ ¬ + margin: 15px 0; // 간격 μ‘°μ • + font-size: 1.1rem; // 폰트 크기 증가 + line-height: 1.5; + color: #555; // 색상 λ³€κ²½ + text-align: center; // ν…μŠ€νŠΈ 쀑앙 μ •λ ¬ `; // μ—λŸ¬ λ©”μ‹œμ§€ μŠ€νƒ€μΌλ§ const ErrorMessage = styled.div` - color: red; - font-size: 0.8rem; - margin-top: 5px; + color: red; + font-size: 0.8rem; + margin-top: 5px; `; diff --git a/client/src/components/Profile/memberInfoModal.tsx b/client/src/components/Profile/memberInfoModal.tsx index 84b4645..ababce3 100644 --- a/client/src/components/Profile/memberInfoModal.tsx +++ b/client/src/components/Profile/memberInfoModal.tsx @@ -1,11 +1,11 @@ -import React from 'react'; -import styled from 'styled-components'; -import { useGetMemberInfo } from '../../hooks/useGetMemberInfo.ts'; +import React from "react"; +import styled from "styled-components"; +import { motion } from "framer-motion"; +import { useGetMemberInfo } from "../../hooks/useGetMemberInfo.ts"; const MemberInfoModal: React.FC = ({ onClose }) => { - // memberId 값을 useGetMemberInfo 훅에 μ „λ‹¬ν•˜μ—¬ νšŒμ› 정보λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€. - const { data: memberInfo } = useGetMemberInfo(); + const { data: memberInfo } = useGetMemberInfo(); const titleText = "νšŒμ›μ •λ³΄"; const nameText = "이름: "; @@ -15,38 +15,48 @@ const MemberInfoModal: React.FC = ({ onClose }) => { const formatDate = (dateString: string) => { const date = new Date(dateString); const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, '0'); // 월은 0λΆ€ν„° μ‹œμž‘ν•˜λ―€λ‘œ 1을 λ”ν•΄μ€λ‹ˆλ‹€. - const day = String(date.getDate()).padStart(2, '0'); - const hours = String(date.getHours()).padStart(2, '0'); - const minutes = String(date.getMinutes()).padStart(2, '0'); + const month = String(date.getMonth() + 1).padStart(2, "0"); // 월은 0λΆ€ν„° μ‹œμž‘ν•˜λ―€λ‘œ 1을 λ”ν•΄μ€λ‹ˆλ‹€. + const day = String(date.getDate()).padStart(2, "0"); + const hours = String(date.getHours()).padStart(2, "0"); + const minutes = String(date.getMinutes()).padStart(2, "0"); return `${year}-${month}-${day} ${hours}:${minutes}`; -} + }; return ( - - - × - {titleText} - {memberInfo ? ( -
- {nameText}{memberInfo.name} - {emailText}{memberInfo.email} - {createdAtText}{formatDate(memberInfo.createdAt)} -
- ) : ( - Data not available - )} -
-
+ + + + × + {titleText} + {memberInfo ? ( +
+ + {nameText} + {memberInfo.name} + + + {emailText} + {memberInfo.email} + + + {createdAtText} + {formatDate(memberInfo.createdAt)} + +
+ ) : ( + Data not available + )} +
+
+
); }; export default MemberInfoModal; - interface MemberInfoModalProps { - onClose: () => void; + onClose: () => void; } // Styled Components Definitions: @@ -68,7 +78,7 @@ const ModalContainer = styled.div` background-color: white; padding: 20px; width: 400px; - height:230px; + height: 230px; border-radius: 10px; display: flex; flex-direction: column; @@ -85,18 +95,17 @@ const CloseButton = styled.button` position: absolute; top: 10px; right: 10px; - background: #FFFFFF; - border-radius:5px; + background: #ffffff; + border-radius: 5px; border: 1px solid lightgray; font-size: 1.5rem; cursor: pointer; `; const Content = styled.p` - margin: 15px 0; // 간격 μ‘°μ • - font-size: 1.1rem; // 폰트 크기 증가 - line-height: 1.5; - color: #555; // 색상 λ³€κ²½ - text-align: center; // ν…μŠ€νŠΈ 쀑앙 μ •λ ¬ + margin: 15px 0; // 간격 μ‘°μ • + font-size: 1.1rem; // 폰트 크기 증가 + line-height: 1.5; + color: #555; // 색상 λ³€κ²½ + text-align: center; // ν…μŠ€νŠΈ 쀑앙 μ •λ ¬ `; - diff --git a/client/src/components/Profile/memberWithdrawalModal.tsx b/client/src/components/Profile/memberWithdrawalModal.tsx index 6c0a3e6..495e425 100644 --- a/client/src/components/Profile/memberWithdrawalModal.tsx +++ b/client/src/components/Profile/memberWithdrawalModal.tsx @@ -1,61 +1,56 @@ -import React, { useState } from 'react'; -import styled from 'styled-components'; -import { useDeleteMember } from '../../hooks/useDeleteMembers'; - +import React, { useState } from "react"; +import styled from "styled-components"; +import { motion } from "framer-motion"; +import { useDeleteMember } from "../../hooks/useDeleteMembers"; const MemberWithdrawalModal: React.FC = ({ onClose, onErrorVisibility }) => { + const [inputString, setInputString] = useState(""); + const [errorMsg, setErrorMsg] = useState(null); + const deleteMemberMutation = useDeleteMember(); + + const withdrawalTitle = "νƒˆν‡΄ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?"; + const inputStringLabel = "λ‹€μŒ λ¬Έμžμ—΄μ„ μž…λ ₯ν•΄μ£Όμ„Έμš”: Codestates"; + const incorrectStringMsg = "λ¬Έμžμ—΄μ΄ μΌμΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€!"; + const withdrawalButtonText = "νšŒμ›νƒˆν‡΄"; + + const handleWithdrawal = () => { + if (inputString === "Codestates") { + deleteMemberMutation.mutate(); + } else { + setErrorMsg(incorrectStringMsg); + onErrorVisibility(true); // μ—λŸ¬κ°€ λ°œμƒν–ˆμ„ λ•Œ 높이λ₯Ό 280px둜 쑰절 + } + }; - const [inputString, setInputString] = useState(''); - const [errorMsg, setErrorMsg] = useState(null); - const deleteMemberMutation = useDeleteMember(); - - - const withdrawalTitle = "νƒˆν‡΄ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?"; - const inputStringLabel = "λ‹€μŒ λ¬Έμžμ—΄μ„ μž…λ ₯ν•΄μ£Όμ„Έμš”: Codestates"; - const incorrectStringMsg = "λ¬Έμžμ—΄μ΄ μΌμΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€!"; - const withdrawalButtonText = "νšŒμ›νƒˆν‡΄"; - - const handleWithdrawal = () => { - if (inputString === "Codestates") { - deleteMemberMutation.mutate(); - } else { - setErrorMsg(incorrectStringMsg); - onErrorVisibility(true); // μ—λŸ¬κ°€ λ°œμƒν–ˆμ„ λ•Œ 높이λ₯Ό 280px둜 쑰절 - } - }; - - const handleEnterPress = (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { - handleWithdrawal(); - } - }; - - return ( - - - × - {withdrawalTitle} - - setInputString(e.target.value)} onKeyDown={handleEnterPress} /> - - {errorMsg && {errorMsg}} - - {withdrawalButtonText} - - - ); + const handleEnterPress = (event: React.KeyboardEvent) => { + if (event.key === "Enter") { + handleWithdrawal(); + } + }; + + return ( + + + + × + {withdrawalTitle} + + setInputString(e.target.value)} onKeyDown={handleEnterPress} /> + {errorMsg && {errorMsg}} + {withdrawalButtonText} + + + + ); }; export default MemberWithdrawalModal; - interface MemberWithdrawalModalProps { - onClose: () => void; - onErrorVisibility: (visibility: boolean) => void; - + onClose: () => void; + onErrorVisibility: (visibility: boolean) => void; } - // Styled Components Definitions: const ModalBackground = styled.div` @@ -76,49 +71,48 @@ const ModalContainer = styled.div` background-color: white; padding: 20px; width: 400px; - height:230px; + height: 230px; border-radius: 10px; display: flex; flex-direction: column; align-items: center; `; - const CloseButton = styled.button` position: absolute; top: 10px; right: 10px; - background: #FFFFFF; - border-radius:5px; + background: #ffffff; + border-radius: 5px; border: 1px solid lightgray; font-size: 1.5rem; cursor: pointer; `; const Title = styled.h2` - font-size: 1.6rem; - margin-bottom: 10px; - font-weight: 400; + font-size: 1.6rem; + margin-bottom: 10px; + font-weight: 400; `; const Label = styled.label` - margin: 15px 0; // 간격 μ‘°μ • - font-size: 1.1rem; // 폰트 크기 증가 - line-height: 1.5; - color: #555; // 색상 λ³€κ²½ - text-align: center; // ν…μŠ€νŠΈ 쀑앙 μ •λ ¬ + margin: 15px 0; // 간격 μ‘°μ • + font-size: 1.1rem; // 폰트 크기 증가 + line-height: 1.5; + color: #555; // 색상 λ³€κ²½ + text-align: center; // ν…μŠ€νŠΈ 쀑앙 μ •λ ¬ `; const PasswordInput = styled.input` - width: 100%; - padding: 5px; - border: 1px solid lightgray; - border-radius: 5px; - margin-bottom: 10px; + width: 100%; + padding: 5px; + border: 1px solid lightgray; + border-radius: 5px; + margin-bottom: 10px; `; const MessageWrapper = styled.div` - height: 10px; // μ—λŸ¬ λ©”μ‹œμ§€ 곡간을 μ’€ 더 ν™•λ³΄ν•©λ‹ˆλ‹€. + height: 10px; // μ—λŸ¬ λ©”μ‹œμ§€ 곡간을 μ’€ 더 ν™•λ³΄ν•©λ‹ˆλ‹€. width: 100%; display: flex; justify-content: center; @@ -126,20 +120,20 @@ const MessageWrapper = styled.div` `; const WithdrawalButton = styled.button` - padding: 10px 20px; - background-color: darkslategray; - color: white; - border: 1px solid lightslategray; - border-radius: 5px; - cursor: pointer; - //ν˜Έλ²„ μ‹œ 밝게 - &:hover { - background-color: rgba(47, 79, 79, 0.8); - } + padding: 10px 20px; + background-color: darkslategray; + color: white; + border: 1px solid lightslategray; + border-radius: 5px; + cursor: pointer; + //ν˜Έλ²„ μ‹œ 밝게 + &:hover { + background-color: rgba(47, 79, 79, 0.8); + } `; const ErrorMessage = styled.p` color: red; font-size: 0.8rem; - margin-bottom:5px; -`; \ No newline at end of file + margin-bottom: 5px; +`; diff --git a/client/src/components/Signups/EmailCertify.tsx b/client/src/components/Signups/EmailCertify.tsx index 8deefb0..08df81c 100644 --- a/client/src/components/Signups/EmailCertify.tsx +++ b/client/src/components/Signups/EmailCertify.tsx @@ -1,6 +1,7 @@ // /client/src/components/Signups/EmailCertify.tsx import axios from "axios"; import React, { useState } from "react"; +import { motion } from "framer-motion"; import styled from "styled-components"; // κ³ μ • λ¬Έμžμ—΄μ„ μ •μ˜ @@ -24,7 +25,7 @@ const EmailVerificationModal: React.FC = ({ onClose // μ—”ν„°ν‚€λ₯Ό λˆŒλ €μ„ λ•Œμ˜ ν•Έλ“€λŸ¬ const handleKeyPress = (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { + if (event.key === "Enter") { handleNextStepClick(); } }; @@ -86,23 +87,25 @@ const EmailVerificationModal: React.FC = ({ onClose return ( - - × - {strings.titleText} - - - - - {strings.codeHintText} - - - - - - {showAgreementError && {strings.agreementError}} - {strings.nextStepText} - {errorMessage && {errorMessage}} - + + + × + {strings.titleText} + + + + + {strings.codeHintText} + + + + + + {showAgreementError && {strings.agreementError}} + {strings.nextStepText} + {errorMessage && {errorMessage}} + + ); }; diff --git a/client/src/components/Signups/EmailSignup.tsx b/client/src/components/Signups/EmailSignup.tsx index 549b942..11bc507 100644 --- a/client/src/components/Signups/EmailSignup.tsx +++ b/client/src/components/Signups/EmailSignup.tsx @@ -2,6 +2,7 @@ import axios from "axios"; import styled from "styled-components"; import React, { useState } from "react"; +import { motion } from "framer-motion"; import { useDispatch } from "react-redux"; import { setEmailForVerification } from "../../reducer/member/memberInfoSlice"; @@ -11,7 +12,7 @@ const strings = { emailLabelText: "이메일", requestVerificationText: "이메일 μΈμ¦μš”μ²­", invalidEmailText: "μœ νš¨ν•˜μ§€ μ•Šμ€ μ΄λ©”μΌμž…λ‹ˆλ‹€", - emailSend : "이메일이 μ „μ†‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€" + emailSend: "이메일이 μ „μ†‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€", }; const EmailSignupModal: React.FC = ({ onClose, onRequestVerification }) => { @@ -34,7 +35,7 @@ const EmailSignupModal: React.FC = ({ onClose, onRequestV // 이메일 μž…λ ₯μ°½μ—μ„œ μ—”ν„°ν‚€λ₯Ό λˆŒλ €μ„ λ•Œμ˜ ν•Έλ“€λŸ¬ const handleKeyPress = (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { + if (event.key === "Enter") { handleVerificationRequest(); } }; @@ -45,7 +46,7 @@ const EmailSignupModal: React.FC = ({ onClose, onRequestV setIsInvalidEmail(true); return; } - + try { const response = await axios.post( "http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com:8080/email/send", @@ -61,7 +62,7 @@ const EmailSignupModal: React.FC = ({ onClose, onRequestV if (response.status === 200) { dispatch(setEmailForVerification(email)); onRequestVerification(email); - setEmailSent(true); // 이 λΆ€λΆ„ μΆ”κ°€ + setEmailSent(true); // 이 λΆ€λΆ„ μΆ”κ°€ } else if (response.status === 400) { setErrorMessage(response.data.message); } else if (response.status === 500) { @@ -83,16 +84,18 @@ const EmailSignupModal: React.FC = ({ onClose, onRequestV return ( - - Γ— - {strings.titleText} - - - {emailSent && {strings.emailSend}} - {isInvalidEmail && {strings.invalidEmailText}} - {strings.requestVerificationText} - {errorMessage && {errorMessage}} - + + + Γ— + {strings.titleText} + + + {emailSent && {strings.emailSend}} + {isInvalidEmail && {strings.invalidEmailText}} + {strings.requestVerificationText} + {errorMessage && {errorMessage}} + + ); }; @@ -191,7 +194,7 @@ const SignupButton = styled.button` } `; const SuccessMessage = styled.p` - color: #e22926; // 빨간색 + color: #e22926; // 빨간색 margin-top: 5px; font-size: 0.8rem; -`; \ No newline at end of file +`; diff --git a/client/src/components/Signups/Guide.tsx b/client/src/components/Signups/Guide.tsx index fd84bf6..ca334a1 100644 --- a/client/src/components/Signups/Guide.tsx +++ b/client/src/components/Signups/Guide.tsx @@ -1,5 +1,6 @@ import React from "react"; import styled from "styled-components"; +import { motion } from "framer-motion"; const STRINGS = { GUIDE_TITLE: "StockHolm κ°€μ΄λ“œ", @@ -16,12 +17,14 @@ const GuideModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { return ( - - × - {STRINGS.GUIDE_TITLE} - {STRINGS.LOGIN_GUIDE} - {STRINGS.CASH_GUIDE} - + + + × + {STRINGS.GUIDE_TITLE} + {STRINGS.LOGIN_GUIDE} + {STRINGS.CASH_GUIDE} + + ); }; diff --git a/client/src/components/Signups/Password.tsx b/client/src/components/Signups/Password.tsx index c87b93b..1165346 100644 --- a/client/src/components/Signups/Password.tsx +++ b/client/src/components/Signups/Password.tsx @@ -2,6 +2,7 @@ import axios from "axios"; import React, { useState, useRef } from "react"; import styled from "styled-components"; +import { motion } from "framer-motion"; import { useDispatch } from "react-redux"; import { setMemberInfo } from "../../reducer/member/memberInfoSlice"; @@ -25,10 +26,10 @@ const PasswordSettingModal: React.FC = ({ onClose, on const [isSubmitting, setIsSubmitting] = useState(false); const dispatch = useDispatch(); - // 각 μž…λ ₯ ν•„λ“œμ— λŒ€ν•œ refλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. - const passwordRef = useRef(null); - const confirmPasswordRef = useRef(null); - const nameRef = useRef(null); + // 각 μž…λ ₯ ν•„λ“œμ— λŒ€ν•œ refλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. + const passwordRef = useRef(null); + const confirmPasswordRef = useRef(null); + const nameRef = useRef(null); //λΉ„λ°€λ²ˆν˜Έ μž…λ ₯μ°½ const handlePasswordChange = (e: React.ChangeEvent) => { setPassword(e.target.value); @@ -54,9 +55,7 @@ const PasswordSettingModal: React.FC = ({ onClose, on const [generalError, setGeneralError] = useState(""); const handleEnterPress = (event: React.KeyboardEvent, target?: "confirmPassword" | "name" | "confirmButton") => { - - - if (event.key === 'Enter') { + if (event.key === "Enter") { if (target === "confirmPassword") { confirmPasswordRef.current?.focus(); } else if (target === "name") { @@ -68,12 +67,11 @@ const PasswordSettingModal: React.FC = ({ onClose, on }; const handleConfirmClick = async () => { - if (isSubmitting) { // 이미 μš”μ²­ 쀑이라면 μΆ”κ°€ μš”μ²­μ„ 방지 return; } - setIsSubmitting(true); // μš”μ²­ μ‹œμž‘ 전에 μƒνƒœλ₯Ό 'μš”μ²­ 쀑'으둜 μ„€μ • + setIsSubmitting(true); // μš”μ²­ μ‹œμž‘ 전에 μƒνƒœλ₯Ό 'μš”μ²­ 쀑'으둜 μ„€μ • //λΉ„λ°€λ²ˆν˜Έ μœ νš¨μ„± μ—λŸ¬ λ©”μ‹œμ§€ if (!isValidPassword(password)) { setPasswordError(strings.passwordError); @@ -122,7 +120,7 @@ const PasswordSettingModal: React.FC = ({ onClose, on // μš”μ²­ μ™„λ£Œ ν›„ μƒνƒœλ₯Ό 'μš”μ²­ 쀑 μ•„λ‹˜'으둜 μ„€μ • setIsSubmitting(false); } catch (error) { - setIsSubmitting(false); // μ˜ˆμ™Έ λ°œμƒ μ‹œ μƒνƒœλ₯Ό 'μš”μ²­ 쀑 μ•„λ‹˜'으둜 μ„€μ • + setIsSubmitting(false); // μ˜ˆμ™Έ λ°œμƒ μ‹œ μƒνƒœλ₯Ό 'μš”μ²­ 쀑 μ•„λ‹˜'으둜 μ„€μ • if (axios.isAxiosError(error)) { console.error("Error during data submission:", error); if (error.response) { @@ -136,24 +134,26 @@ const PasswordSettingModal: React.FC = ({ onClose, on return ( - - × - {strings.titleText} - - handleEnterPress(event, "confirmPassword")}/> - {passwordError && {passwordError}} - - - handleEnterPress(event, "name")}/> - {confirmPasswordError && {confirmPasswordError}} - - - handleEnterPress(event, "confirmButton")}/> - - {generalError && {generalError}} - - {strings.confirmButtonText} - + + + × + {strings.titleText} + + handleEnterPress(event, "confirmPassword")} /> + {passwordError && {passwordError}} + + + handleEnterPress(event, "name")} /> + {confirmPasswordError && {confirmPasswordError}} + + + handleEnterPress(event, "confirmButton")} /> + + {generalError && {generalError}} + + {strings.confirmButtonText} + + ); }; diff --git a/client/src/components/Signups/Welcome.tsx b/client/src/components/Signups/Welcome.tsx index 041813b..91d29b4 100644 --- a/client/src/components/Signups/Welcome.tsx +++ b/client/src/components/Signups/Welcome.tsx @@ -1,5 +1,6 @@ import React from "react"; import styled from "styled-components"; +import { motion } from "framer-motion"; import { useSelector } from "react-redux"; import StockHolmLogo from "../../asset/logos/StockHolmLogo.png"; @@ -31,20 +32,22 @@ const Welcome: React.FC = ({ onClose }) => { return ( - - - {WELCOME_TEXT} - {memberInfo?.name}λ‹˜! - {" "} - {/* Display the member's name */} - - {JOINED_DATE_TEXT} - {memberInfo?.createdAt && formatDate(memberInfo.createdAt)} - {" "} - {/* Display the member's createdAt */} - - {START_TEXT} - + + + + {WELCOME_TEXT} + {memberInfo?.name}λ‹˜! + {" "} + {/* Display the member's name */} + + {JOINED_DATE_TEXT} + {memberInfo?.createdAt && formatDate(memberInfo.createdAt)} + {" "} + {/* Display the member's createdAt */} + + {START_TEXT} + + ); }; diff --git a/client/src/components/StockOrderSection/OrderResult.tsx b/client/src/components/StockOrderSection/OrderResult.tsx index c773492..b45f396 100644 --- a/client/src/components/StockOrderSection/OrderResult.tsx +++ b/client/src/components/StockOrderSection/OrderResult.tsx @@ -72,12 +72,7 @@ const OrderResult = () => { const orderTime = `${year}-${month}-${date} ${hour}:${minute}`; return ( - + ); diff --git a/client/src/components/watchlist/WatchList.tsx b/client/src/components/watchlist/WatchList.tsx index 916f64e..14838a2 100644 --- a/client/src/components/watchlist/WatchList.tsx +++ b/client/src/components/watchlist/WatchList.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from "react"; import styled from "styled-components"; +import { motion } from "framer-motion"; import StockSearchComponent from "./StockSearchComponent.tsx"; import Header from "./Header.tsx"; import StockItem from "./StockItem.tsx"; @@ -32,27 +33,29 @@ const WatchList: React.FC = ({ currentListType, onChangeListType setStarredCompanyIds((prevState) => prevState.filter((id) => id !== deletedCompanyId)); }; return ( - - -
- - - - - - - - {isLoading ? ( -
Loading...
- ) : isError ? ( -
Error fetching data
- ) : loginStatus === 1 ? ( - companiesList.filter((company) => starredCompanyIds.includes(company.companyId)).map((company) => ) - ) : ( - - )} -
- + + + +
+ + + + + + + + {isLoading ? ( +
Loading...
+ ) : isError ? ( +
Error fetching data
+ ) : loginStatus === 1 ? ( + companiesList.filter((company) => starredCompanyIds.includes(company.companyId)).map((company) => ) + ) : ( + + )} +
+ + ); };