diff --git a/client/package-lock.json b/client/package-lock.json index a94245e2..52674147 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@hyunbinseo/holidays-kr": "^2.2024.4", + "@react-oauth/google": "^0.11.1", "@reduxjs/toolkit": "^1.9.5", "axios": "^1.5.0", "boxicons": "^2.1.4", @@ -2488,6 +2489,15 @@ "node": ">= 8" } }, + "node_modules/@react-oauth/google": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.11.1.tgz", + "integrity": "sha512-tywZisXbsdaRBVbEu0VX6dRbOSL2I6DgY97woq5NMOOOz+xtDsm418vqq+Vx10KMtra3kdHMRMf0hXLWrk2RMg==", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "1.9.5", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", diff --git a/client/package.json b/client/package.json index d0929574..ce22f234 100644 --- a/client/package.json +++ b/client/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@hyunbinseo/holidays-kr": "^2.2024.4", + "@react-oauth/google": "^0.11.1", "@reduxjs/toolkit": "^1.9.5", "axios": "^1.5.0", "boxicons": "^2.1.4", diff --git a/client/src/components/Logins/GoogleSignin.tsx b/client/src/components/Logins/GoogleSignin.tsx new file mode 100644 index 00000000..88c9e51a --- /dev/null +++ b/client/src/components/Logins/GoogleSignin.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { GoogleOAuthProvider, GoogleLogin, useGoogleOneTapLogin } from '@react-oauth/google'; +import { useDispatch } from 'react-redux'; +import { setLoginState } from '../../reducer/member/loginSlice'; + +const GoogleSignInComponent: React.FC = () => { + + const dispatch = useDispatch(); // Redux의 dispatch 함수를 사용하기 위해 가져옵니다. + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleSuccess = (credentialResponse: any) => { + console.log(credentialResponse); + + const token = credentialResponse.token; // 실제 응답에서 토큰의 경로가 어떤지 확인하고 수정해야 합니다. + localStorage.setItem('authToken', token); // 토큰을 localStorage에 저장 + + // 로그인 성공 시 전역 상태를 업데이트합니다. + dispatch(setLoginState({ + memberId: credentialResponse.memberId, // memberId는 예시입니다. 실제 값에 맞게 수정해야 합니다. + isLoggedIn: 1, + })); + }; + + const handleError = () => { + console.log('Login Failed'); + }; + + // One-tap 로그인 (선택적) + useGoogleOneTapLogin({ + onSuccess: handleSuccess, + onError: handleError, + }); + + return ( + + + + ); +}; + +export default GoogleSignInComponent; + diff --git a/client/src/components/Logins/OAuthLogin.tsx b/client/src/components/Logins/OAuthLogin.tsx index 7d3fc473..1ebbd4a9 100644 --- a/client/src/components/Logins/OAuthLogin.tsx +++ b/client/src/components/Logins/OAuthLogin.tsx @@ -1,6 +1,6 @@ import React from 'react'; import styled from 'styled-components'; - +import GoogleLogin from './GoogleSignin.tsx' import kakaoLogo from '../../asset/images/KakaoLogo.svg'; import axios from 'axios'; @@ -34,7 +34,7 @@ const OAuthLoginModal: React.FC = ({ onClose, onEmailLoginClick × {titleText} - + {kakaoLoginText} diff --git a/client/src/components/MarketComponents/index.tsx b/client/src/components/MarketComponents/index.tsx index ec96dfef..5422abba 100644 --- a/client/src/components/MarketComponents/index.tsx +++ b/client/src/components/MarketComponents/index.tsx @@ -69,6 +69,7 @@ const NewsListLink = { const Market = styled.div` text-align: center; + height: 600px; `; const News = styled.div` display: flex; diff --git a/client/src/components/Profile/cashModal.tsx b/client/src/components/Profile/cashModal.tsx index 866ff083..02195472 100644 --- a/client/src/components/Profile/cashModal.tsx +++ b/client/src/components/Profile/cashModal.tsx @@ -1,18 +1,25 @@ import React, { useState } from 'react'; import styled from 'styled-components'; import { useSelector, useDispatch } from 'react-redux'; -import { useCreateCash, useGetCash, useUpdateCash } from '../../hooks/useCash'; +import { useCreateCash, useGetCash, useResetCash } from '../../hooks/useCash'; import { RootState } from '../../store/config'; import { setCashId, setCashAmount } from '../../reducer/cash/cashSlice'; const CashModal: React.FC = ({ onClose }) => { + + const titleText = "현금"; + const cashCreationPlaceholder = "생성할 현금 입력"; + const createCashButtonText = "현금 생성"; + const cashInputPlaceholder = "현금 입력"; + const resetButtonText = "리셋"; + const dispatch = useDispatch(); const cashId = useSelector((state: RootState) => state.cash.cashId); const cashAmount = useSelector((state: RootState) => state.cash.cashAmount) || 0; const createCashMutation = useCreateCash(); const { data: cashData, isLoading } = useGetCash(cashId); - const updateCashMutation = useUpdateCash(); + const updateCashMutation = useResetCash(); const [cashInput, setCashInput] = useState(''); const [initialAmount, setInitialAmount] = useState(0); // 현금 생성을 위한 상태 변수 @@ -31,26 +38,27 @@ const CashModal: React.FC = ({ onClose }) => { dispatch(setCashAmount(cashData.data.cash)); } - // 현금 충전 및 충전된 현금량 전역 저장 - const handleCashReceive = () => { - if (cashId && cashAmount !== null) { +// 현금을 입력한 금액으로 리셋하는 함수 + const handleCashReset = () => { + if (cashId) { const numericCashId = parseInt(cashId, 10); // cashId를 숫자로 변환 const numericCashAmount = Number(cashInput); // cashInput을 숫자로 변환 updateCashMutation.mutate({ cashId: numericCashId, cashAmount: numericCashAmount }, { onSuccess: () => { - dispatch(setCashAmount((prevCash: number) => prevCash ? prevCash + numericCashAmount : numericCashAmount)); + dispatch(setCashAmount(numericCashAmount)); // 현금 금액을 입력한 금액으로 리셋 } }); } else { - console.error("cashId or cashAmount is null or not a valid number."); + console.error("cashId is null or not a valid number."); } }; + return ( × - 현금 + {titleText} {/* 현금 생성 입력창 및 버튼 추가 */}
@@ -58,9 +66,9 @@ const CashModal: React.FC = ({ onClose }) => { type="number" value={initialAmount} onChange={e => setInitialAmount(Number(e.target.value))} - placeholder="생성할 현금 입력" + placeholder={cashCreationPlaceholder} /> - 현금 생성 + {createCashButtonText}

현재 현금: {isLoading ? 'Loading...' : cashAmount.toLocaleString()}

@@ -69,16 +77,15 @@ const CashModal: React.FC = ({ onClose }) => { type="number" value={cashInput} onChange={e => setCashInput(e.target.value)} - placeholder="현금 입력" + placeholder={cashInputPlaceholder} /> - 받기 + {resetButtonText}
); }; - interface CashModalProps { onClose: () => void; cashId: string | null; diff --git a/client/src/components/communityComponents/index.tsx b/client/src/components/communityComponents/index.tsx index b4c53db8..d5c2f02d 100644 --- a/client/src/components/communityComponents/index.tsx +++ b/client/src/components/communityComponents/index.tsx @@ -312,6 +312,7 @@ const TimeLine = styled.div` flex-direction: column; align-content: space-around; flex-wrap: wrap; + max-height:600px; `; //게시글 삭제 const Delete = styled.div` diff --git a/client/src/components/watchlist/Holdings.tsx b/client/src/components/watchlist/Holdings.tsx index 6c51b0cd..5f6f3857 100644 --- a/client/src/components/watchlist/Holdings.tsx +++ b/client/src/components/watchlist/Holdings.tsx @@ -33,9 +33,9 @@ const Holdings: React.FC = ({ currentListType, onChangeListType } )} - + 평가 수익금: +5,000,000원 {/* 임의의 평가 수익금 */} - + {holdingsData.map(stock => ( <> @@ -88,10 +88,11 @@ const getColorByChange = (change: string) => { }; const HoldingsContainer = styled.div` - padding: 16px; + padding: 8px 0px; `; const Header = styled.div` + padding:0px 16px; display: flex; align-items: center; position: relative; @@ -186,17 +187,32 @@ const getColorByChange = (change: string) => { cursor: pointer; // 마우스 포인터 변경 추가 `; - const Divider = styled.div` - height: 1px; - background-color: #aaa; // 회색으로 설정 - margin: 8px 0; // 상하 여백 추가 + const Divider1 = styled.div` + margin:0px; + padding:0px; + width: 100%; + height: 11px; + display: flex; + flex-direction: row; + border-bottom: 1px solid #2f4f4f; `; - + + const Divider2 = styled.div` + margin:0px; + padding:0px; + width: 100%; + height: 4.5px; + display: flex; + flex-direction: row; + border-bottom: 1px solid #2f4f4f; +`; + + const EvaluationProfit = styled.div` font-size: 16px; font-weight: bold; - margin: 10px 0; + margin: 8px 0; text-align: center; color: red; // 수익금이 플러스일 경우 초록색으로 표시 `; @@ -238,7 +254,7 @@ const ColoredDetailData = styled.span.attrs<{ value: string }>(({ value }) => ({ `; const ThickDivider = styled.div` - height: 2px; + height: 3px; background-color: #aaa; margin: 8px 0; `; diff --git a/client/src/components/watchlist/WatchList.tsx b/client/src/components/watchlist/WatchList.tsx index 5fcee4b4..18ccca84 100644 --- a/client/src/components/watchlist/WatchList.tsx +++ b/client/src/components/watchlist/WatchList.tsx @@ -39,9 +39,9 @@ const WatchList: React.FC = ({ currentListType, onChangeListType )} - + { /* 종목 추가 로직 */ }}>종목 추가 - + {favoriteStocks.map(stock => ( @@ -77,7 +77,7 @@ const getColorByChange = (change: string) => { }; const WatchListContainer = styled.div` - padding: 16px; + padding: 8px 0px; `; const Header = styled.div` @@ -175,14 +175,27 @@ const StockChange = styled.span.attrs<{ change: string }>(({ change }) => ({ cursor: pointer; `; -const Divider = styled.div` - height: 1px; - background-color: #aaa; - margin: 8px 0; +const Divider1 = styled.div` + margin:0px; + padding:0px; + width: 100%; + height: 11px; + display: flex; + flex-direction: row; + border-bottom: 1px solid #2f4f4f; +`; +const Divider2 = styled.div` + margin:0px; + padding:0px; + width: 100%; + height: 4px; + display: flex; + flex-direction: row; + border-bottom: 1px solid #2f4f4f; `; const AddStockButton = styled.button` - padding: 10px 20px; + padding: 10px; border: none; background-color: transparent; cursor: pointer; diff --git a/client/src/hooks/useCash.ts b/client/src/hooks/useCash.ts index ade93b0b..7df13135 100644 --- a/client/src/hooks/useCash.ts +++ b/client/src/hooks/useCash.ts @@ -29,6 +29,6 @@ export const useGetCash = (cashId: string | null) => { } -export const useUpdateCash = () => { - return useMutation((data: { cashId: number, cashAmount: number }) => axios.post(`http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com/cash/${data.cashId}`, { cash: data.cashAmount })); +export const useResetCash = () => { + return useMutation((data: { cashId: number, cashAmount: number }) => axios.patch(`http://ec2-13-125-246-160.ap-northeast-2.compute.amazonaws.com/cash/${data.cashId}`, { cash: data.cashAmount })); } \ No newline at end of file diff --git a/client/src/page/MainPage.tsx b/client/src/page/MainPage.tsx index 68746222..5d6b34c8 100644 --- a/client/src/page/MainPage.tsx +++ b/client/src/page/MainPage.tsx @@ -16,11 +16,8 @@ import Holdings from "../components/watchlist/Holdings"; // Assuming you have a import CompareChartSection from "../components/CompareChartSection/Index"; import StockOrderSection from "../components/StockOrderSection/Index"; import Welcome from "../components/Signups/Welcome"; - import ProfileModal from "../components/Profile/profileModal"; - import { StateProps } from "../models/stateProps"; - import { TabContainerPage } from "./TabPages/TabContainerPage"; const MainPage = () => { @@ -200,7 +197,7 @@ const MainPage = () => { }} /> )} - {isProfileModalOpen && setProfileModalOpen(false)} />} //프로필 모달 컴포넌트 렌더링 + {isProfileModalOpen && setProfileModalOpen(false)} />} ); diff --git a/client/src/reducer/member/loginSlice.ts b/client/src/reducer/member/loginSlice.ts index fd1fdf2f..558b0408 100644 --- a/client/src/reducer/member/loginSlice.ts +++ b/client/src/reducer/member/loginSlice.ts @@ -4,16 +4,16 @@ const loginSlice = createSlice({ name: 'login', initialState: { memberId: null, - isLoggedOut: 1 + isLoggedIn: 1 }, reducers: { setLoginState: (state, action) => { state.memberId = action.payload; - state.isLoggedOut = 0; + state.isLoggedIn = 1; }, setLogoutState: (state) => { state.memberId = null; - state.isLoggedOut = 1; + state.isLoggedIn = 0; }, updateMemberId: (state, action) => { state.memberId = action.payload;