)}
@@ -112,9 +121,9 @@ export default function BuySection({ code, detailInfo }: BuySectionProps) {
수량
setCount(+e.target.value)}
+ onChange={handleCountChange}
className='flex-1 py-1 rounded-lg'
min={1}
/>
diff --git a/FE/src/components/StocksDetail/TradeSection/SellSection.tsx b/FE/src/components/StocksDetail/TradeSection/SellSection.tsx
index eeb8f1cf..c94035c0 100644
--- a/FE/src/components/StocksDetail/TradeSection/SellSection.tsx
+++ b/FE/src/components/StocksDetail/TradeSection/SellSection.tsx
@@ -1,7 +1,7 @@
import Lottie from 'lottie-react';
import emptyAnimation from 'assets/emptyAnimation.json';
import { useQuery } from '@tanstack/react-query';
-import { getSellPossibleStockCnt } from 'service/assets';
+import { getSellInfo } from 'service/assets';
import { ChangeEvent, FocusEvent, FormEvent, useRef, useState } from 'react';
import { StockDetailType } from 'types';
import useAuthStore from 'store/authStore';
@@ -19,12 +19,13 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) {
const { data, isLoading, isError } = useQuery(
['detail', 'sellPosiible', code],
- () => getSellPossibleStockCnt(code),
+ () => getSellInfo(code),
+ { staleTime: 1000 },
);
const [currPrice, setCurrPrice] = useState
(stck_prpr);
const { isLogin } = useAuthStore();
- const [count, setCount] = useState(0);
+ const [count, setCount] = useState(1);
const [upperLimitFlag, setUpperLimitFlag] = useState(false);
const [lowerLimitFlag, setLowerLimitFlag] = useState(false);
@@ -38,14 +39,26 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) {
if (isError) return error
;
const quantity = data.quantity;
+ const avg_price = data.avg_price;
+
+ const pl = (+currPrice - avg_price) * count;
+ const totalPrice = +currPrice * count;
+ const plRate = ((pl / totalPrice) * 100).toFixed(2);
+
const handlePriceChange = (e: ChangeEvent) => {
- if (!isNumericString(e.target.value)) return;
+ const s = e.target.value.replace(/,/g, '');
+ if (!isNumericString(s)) return;
+ setCurrPrice(s);
+ };
- setCurrPrice(e.target.value);
+ const handleCountChange = (e: ChangeEvent) => {
+ const s = e.target.value;
+ if (!isNumericString(s)) return;
+ setCount(+s);
};
const handlePriceInputBlur = (e: FocusEvent) => {
- const n = +e.target.value;
+ const n = +e.target.value.replace(/,/g, '');
if (n > +stck_mxpr) {
if (timerRef.current) {
clearTimeout(timerRef.current);
@@ -91,15 +104,6 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) {
const handleSell = async (e: FormEvent) => {
e.preventDefault();
- // const price = +currPrice * count;
-
- // if (price > data.cash_balance) {
- // setLackAssetFlag(true);
- // timerRef.current = window.setTimeout(() => {
- // setLackAssetFlag(false);
- // }, 2000);
- // return;
- // }
toggleModal();
};
@@ -124,14 +128,14 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) {
매도 가격
+
이 주식의 최소 가격은 {(+stck_llam).toLocaleString()}입니다.
)}
@@ -143,9 +147,9 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) {
수량
setCount(+e.target.value)}
+ onChange={handleCountChange}
onBlur={handleCntInputBlur}
className='flex-1 py-1 rounded-lg'
min={1}
@@ -162,12 +166,27 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) {
+
+
+
예상 수익률
+
+ {plRate}%
+
+
+
+
+
+
예상 손익
+
{pl.toLocaleString()}원
+
+
총 매도 금액
-
{(+currPrice * count).toLocaleString()}원
+
{totalPrice.toLocaleString()}원
-
diff --git a/FE/src/page/StocksDetail.tsx b/FE/src/page/StocksDetail.tsx
index e30f2e11..42e336eb 100644
--- a/FE/src/page/StocksDetail.tsx
+++ b/FE/src/page/StocksDetail.tsx
@@ -13,7 +13,7 @@ export default function StocksDetail() {
const { data, isLoading } = useQuery(
['stocks', code],
() => getStocksByCode(code!),
- { enabled: !!code },
+ { staleTime: 1000, enabled: !!code },
);
if (!code) return
Non code
;
diff --git a/FE/src/service/assets.ts b/FE/src/service/assets.ts
index 98596288..bb60b8c9 100644
--- a/FE/src/service/assets.ts
+++ b/FE/src/service/assets.ts
@@ -29,9 +29,9 @@ export async function getCash(): Promise<{ cash_balance: number }> {
});
}
-export async function getSellPossibleStockCnt(
+export async function getSellInfo(
code: string,
-): Promise<{ quantity: number }> {
+): Promise<{ quantity: number; avg_price: number }> {
const url = import.meta.env.PROD
? `${import.meta.env.VITE_API_URL}/assets/stocks/${code}`
: `/api/assets/stocks/${code}`;
@@ -42,7 +42,7 @@ export async function getSellPossibleStockCnt(
'Content-Type': 'application/json',
},
}).then((res) => {
- if (res.status === 401) return { quantity: 0 };
+ if (res.status === 401) return { quantity: 0, avg_price: 0 };
return res.json();
});
}
diff --git a/FE/src/service/stocks.ts b/FE/src/service/stocks.ts
index 9f5d4bb4..fae307b9 100644
--- a/FE/src/service/stocks.ts
+++ b/FE/src/service/stocks.ts
@@ -22,3 +22,12 @@ export async function getStocksChartDataByCode(
}),
}).then((res) => res.json());
}
+
+export async function unsbscribe(code: string) {
+ return fetch(
+ `${import.meta.env.VITE_API_URL}/stocks/trade-history/${code}/unsubscribe`,
+ {
+ headers: { 'Content-Type': 'application/json' },
+ },
+ );
+}
diff --git a/FE/src/service/user.ts b/FE/src/service/user.ts
new file mode 100644
index 00000000..5aa9caaf
--- /dev/null
+++ b/FE/src/service/user.ts
@@ -0,0 +1,12 @@
+import { Profile } from 'types';
+
+export async function getMyProfile(): Promise
{
+ const url = import.meta.env.PROD
+ ? `${import.meta.env.VITE_API_URL}/user/profile`
+ : '/api/user/profile';
+
+ return fetch(url, {
+ credentials: 'include',
+ headers: { 'Content-Type': 'application/json' },
+ }).then((res) => res.json());
+}
diff --git a/FE/src/store/orderCancleAlertModalStore.ts b/FE/src/store/orderCancleAlertModalStore.ts
new file mode 100644
index 00000000..6f48162f
--- /dev/null
+++ b/FE/src/store/orderCancleAlertModalStore.ts
@@ -0,0 +1,21 @@
+import { Order } from 'types';
+import { create } from 'zustand';
+
+type ModalStore = {
+ isOpen: boolean;
+ order: Order | null;
+ onSuccess: () => void;
+ open: (order: Order, callback?: () => void) => void;
+ close: () => void;
+};
+
+const useOrderCancelAlertModalStore = create((set) => ({
+ isOpen: false,
+ order: null,
+ onSuccess: () => {},
+ open: (order, callback?) =>
+ set(() => ({ isOpen: true, order, onSuccess: callback })),
+ close: () => set(() => ({ isOpen: false })),
+}));
+
+export default useOrderCancelAlertModalStore;
diff --git a/FE/src/types.ts b/FE/src/types.ts
index 1c61149e..417126df 100644
--- a/FE/src/types.ts
+++ b/FE/src/types.ts
@@ -80,3 +80,8 @@ export type ChartSizeConfigType = {
yAxisWidth: number;
xAxisHeight: number;
};
+
+export type Profile = {
+ name: string;
+ email: string;
+};