diff --git a/client/package-lock.json b/client/package-lock.json index 0ba4f0e5..1bd60132 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,11 +8,13 @@ "name": "vite-project", "version": "0.0.0", "dependencies": { + "@reduxjs/toolkit": "^1.9.5", "axios": "^1.5.0", "echarts": "^5.4.3", "echarts-for-react": "^3.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-redux": "^8.1.2", "styled-components": "^6.0.7" }, "devDependencies": { @@ -2487,6 +2489,38 @@ "node": ">= 8" } }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -2504,14 +2538,12 @@ "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { "version": "18.2.21", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -2522,7 +2554,7 @@ "version": "18.2.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*" } @@ -2530,8 +2562,7 @@ "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "node_modules/@types/semver": { "version": "7.5.1", @@ -2544,6 +2575,11 @@ "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.5.0.tgz", @@ -3823,6 +3859,19 @@ "node": ">=4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -3832,6 +3881,15 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -4387,6 +4445,49 @@ "react": "^18.2.0" } }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/react-redux": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.2.tgz", + "integrity": "sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -4408,6 +4509,22 @@ "node": ">=8.10.0" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -4472,6 +4589,11 @@ "jsesc": "bin/jsesc" } }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "node_modules/resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", @@ -4929,6 +5051,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/vite": { "version": "4.4.9", "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", diff --git a/client/package.json b/client/package.json index 204529df..51346128 100644 --- a/client/package.json +++ b/client/package.json @@ -10,11 +10,13 @@ "preview": "vite preview" }, "dependencies": { + "@reduxjs/toolkit": "^1.9.5", "axios": "^1.5.0", "echarts": "^5.4.3", "echarts-for-react": "^3.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-redux": "^8.1.2", "styled-components": "^6.0.7" }, "devDependencies": { diff --git a/client/src/components/StockOrderSection/Index.tsx b/client/src/components/StockOrderSection/Index.tsx index 818f83a5..3503d93c 100644 --- a/client/src/components/StockOrderSection/Index.tsx +++ b/client/src/components/StockOrderSection/Index.tsx @@ -1,14 +1,18 @@ import { styled } from "styled-components"; -import UpperBar from "./UpperBar"; import StockName from "./StockName"; import OrderRequest from "./OrderRequest"; import OrderResult from "./OrderResult"; +const titleText: string = "주식주문"; + const StockOrderSection = () => { return ( - + + {titleText} + + @@ -29,3 +33,31 @@ const Container = styled.aside` height: 100%; background-color: #ffffff; `; + +const UpperBar = styled.div` + position: relative; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + width: 100%; + min-height: 43px; + border-bottom: 1px solid black; +`; + +const Title = styled.h2` + font-size: 17px; + font-weight: 450; + color: #1c1c1c; +`; + +const CloseBtn = styled.button` + position: absolute; + right: 10px; + width: 28px; + height: 95%; + border: none; + font-size: 20px; + color: #525252; + background-color: #ffff; +`; diff --git a/client/src/components/StockOrderSection/OrderResult.tsx b/client/src/components/StockOrderSection/OrderResult.tsx index 37a721c9..61e87f86 100644 --- a/client/src/components/StockOrderSection/OrderResult.tsx +++ b/client/src/components/StockOrderSection/OrderResult.tsx @@ -30,7 +30,6 @@ const Title = styled.div` font-weight: 500; padding-left: 16px; padding-bottom: 16px; - /* border-bottom: 1px solid black; */ `; const OrderPending = styled.div` diff --git a/client/src/components/StockOrderSection/StockOrderBtn.tsx b/client/src/components/StockOrderSection/StockOrderBtn.tsx index 9ee8a4c9..725ef6db 100644 --- a/client/src/components/StockOrderSection/StockOrderBtn.tsx +++ b/client/src/components/StockOrderSection/StockOrderBtn.tsx @@ -1,17 +1,21 @@ +import { useSelector } from "react-redux"; import { styled } from "styled-components"; +import { StateProps } from "../../models/stateProps"; +import { OrderTypeProps } from "../../models/orderTypeProps"; const availableMoneyText01: string = "최대"; const availableMoneyText02: string = "원"; const totalAmountText01: string = "주문총액"; const totalAmountText02: string = "원"; -const orderBtnText: string = "매수"; // dummyData import { availableMoney } from "./dummyData"; const dummyAmount: string = "0"; +const dummyMoney = availableMoney.toLocaleString(); const StockOrderBtn = () => { - const dummyMoney = availableMoney.toLocaleString(); + const stockOrderType = useSelector((state: StateProps) => state.stockOrderType); + const orderBtnText: string = stockOrderType ? "매도" : "매수"; return ( @@ -25,7 +29,7 @@ const StockOrderBtn = () => {
{dummyAmount}
{totalAmountText02}
- {orderBtnText} + {orderBtnText}
); }; @@ -73,13 +77,13 @@ const TotalAmount = styled.div` } `; -const OrderBtn = styled.button` +const OrderBtn = styled.button` width: 100%; height: 32px; margin-top: 16px; border: none; border-radius: 0.25rem; - background-color: #ed2926; + background-color: ${(props) => (props.ordertype ? "#2679ed" : "#e22926")}; color: #ffffff; font-weight: 400; `; diff --git a/client/src/components/StockOrderSection/StockOrderSetting.tsx b/client/src/components/StockOrderSection/StockOrderSetting.tsx index 4c1a04ad..cff36549 100644 --- a/client/src/components/StockOrderSection/StockOrderSetting.tsx +++ b/client/src/components/StockOrderSection/StockOrderSetting.tsx @@ -1,4 +1,9 @@ +import { useSelector, useDispatch } from "react-redux"; +import { orderTypeBuying, orderTypeSelling } from "../../reducer/StockOrderType-Reducer"; import { styled } from "styled-components"; +import { StateProps } from "../../models/stateProps"; +import { OrderTypeProps } from "../../models/orderTypeProps"; + import PriceSetting from "./PriceSetting"; import VolumeSetting from "./VolumeSetteing"; import StockOrderBtn from "./StockOrderBtn"; @@ -7,12 +12,28 @@ const orderType01: string = "매수"; const orderType02: string = "매도"; const StockOrderSetting = () => { + const stockOrderType = useSelector((state: StateProps) => state.stockOrderType); + const dispatch = useDispatch(); + + const handleSetBuying = () => { + dispatch(orderTypeBuying()); + }; + + const handleSetSelling = () => { + dispatch(orderTypeSelling()); + }; + return ( -
{orderType01}
-
{orderType02}
+ + {orderType01} + + + {orderType02} +
+ @@ -22,6 +43,19 @@ const StockOrderSetting = () => { export default StockOrderSetting; +const TypeDividingLine = () => { + const stockOrderType = useSelector((state: StateProps) => state.stockOrderType); + + return ( + + + + + + ); +}; + +// component 생성 const Container = styled.div` width: 51%; height: 100%; @@ -32,14 +66,41 @@ const OrderType = styled.div` height: 32px; display: flex; flex-direction: row; +`; + +const Buying = styled.div` + flex: 1 0 0; + display: flex; + justify-content: center; + align-items: center; + height: 31px; + font-size: 14px; + color: ${(props) => !props.ordertype && "#e22926"}; +`; + +const Selling = styled.div` + flex: 1 0 0; + display: flex; + justify-content: center; + align-items: center; + height: 31px; + font-size: 14px; + color: ${(props) => props.ordertype && "#2679ed"}; +`; + +const DividingContainer = styled.div` + background-color: darkgray; +`; + +const DefaultLine = styled.div` + transform: translateX(${(props) => (props.ordertype ? "50%" : "0")}); + transition: transform 0.3s ease-in-out; + width: 100%; + height: 1.2px; +`; - & div { - flex: 1 0 0; - display: flex; - justify-content: center; - align-items: center; - height: 31px; - font-size: 14px; - border-bottom: 1px solid darkgray; - } +const DivdingLine = styled.div` + width: 50%; + height: 1.2px; + background-color: ${(props) => (props.ordertype ? "#2679ed" : "#e22926")}; `; diff --git a/client/src/components/StockOrderSection/StockPrice.tsx b/client/src/components/StockOrderSection/StockPrice.tsx index 324c61a9..89fa0251 100644 --- a/client/src/components/StockOrderSection/StockPrice.tsx +++ b/client/src/components/StockOrderSection/StockPrice.tsx @@ -43,7 +43,7 @@ const PriceInfo = (props: PriceInfoProps) => { // 11번째 가격 -> 렌더링 시 정중앙에 위치하도록 useEffect(() => { ref.current?.focus(); - ref.current?.scrollIntoView({ block: "center" }); + ref.current?.scrollIntoView({ behavior: "smooth", block: "center" }); }, []); if (index === 10) { diff --git a/client/src/components/StockOrderSection/UpperBar.tsx b/client/src/components/StockOrderSection/UpperBar.tsx deleted file mode 100644 index 5e76d089..00000000 --- a/client/src/components/StockOrderSection/UpperBar.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { styled } from "styled-components"; - -const titleText: string = "주식주문"; - -const UpperBar = () => { - return ( - - {titleText} - - - ); -}; - -export default UpperBar; - -const Container = styled.div` - position: relative; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - width: 100%; - min-height: 43px; - border-bottom: 1px solid black; -`; - -const Title = styled.h2` - font-size: 17px; - font-weight: 450; - color: #1c1c1c; -`; - -const CloseBtn = styled.button` - position: absolute; - right: 10px; - width: 28px; - height: 95%; - border: none; - font-size: 20px; - color: #525252; - background-color: #ffff; -`; diff --git a/client/src/main.tsx b/client/src/main.tsx index 95e2bdc2..dc075919 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -1,9 +1,13 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.tsx"; +import { Provider } from "react-redux"; +import store from "./store/config.ts"; ReactDOM.createRoot(document.getElementById("root")!).render( - + + + ); diff --git a/client/src/models/README.md b/client/src/models/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/client/src/models/orderTypeProps.ts b/client/src/models/orderTypeProps.ts new file mode 100644 index 00000000..c50c2081 --- /dev/null +++ b/client/src/models/orderTypeProps.ts @@ -0,0 +1,3 @@ +export interface OrderTypeProps { + ordertype: boolean; +} diff --git a/client/src/models/stateProps.ts b/client/src/models/stateProps.ts new file mode 100644 index 00000000..7ea6ad4b --- /dev/null +++ b/client/src/models/stateProps.ts @@ -0,0 +1,3 @@ +export interface StateProps { + stockOrderType: boolean; +} diff --git a/client/src/reducer/StockOrderType-Reducer.ts b/client/src/reducer/StockOrderType-Reducer.ts new file mode 100644 index 00000000..9999de9e --- /dev/null +++ b/client/src/reducer/StockOrderType-Reducer.ts @@ -0,0 +1,19 @@ +import { createSlice } from "@reduxjs/toolkit"; + +const initialState: boolean = false; + +const stockOrderTypeSlice = createSlice({ + name: "stockOrderType", + initialState: initialState, + reducers: { + orderTypeBuying: () => { + return false; + }, + orderTypeSelling: () => { + return true; + }, + }, +}); + +export const { orderTypeBuying, orderTypeSelling } = stockOrderTypeSlice.actions; +export const stockOrderTypeReducer = stockOrderTypeSlice.reducer; diff --git a/client/src/store/config.ts b/client/src/store/config.ts new file mode 100644 index 00000000..622cb788 --- /dev/null +++ b/client/src/store/config.ts @@ -0,0 +1,10 @@ +import { configureStore } from "@reduxjs/toolkit"; +import { stockOrderTypeReducer } from "../reducer/StockOrderType-Reducer"; + +const store = configureStore({ + reducer: { + stockOrderType: stockOrderTypeReducer, + }, +}); + +export default store;