import { useState } from "react"; import { Loader2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useOrder } from "@/features/trade/hooks/useOrder"; import { useKisRuntimeStore } from "@/features/settings/store/use-kis-runtime-store"; import type { DashboardOrderSide, DashboardStockItem, } from "@/features/trade/types/trade.types"; interface OrderFormProps { stock?: DashboardStockItem; } /** * @description 대시보드 주문 패널에서 매수/매도 주문을 입력하고 전송합니다. * @see features/trade/hooks/useOrder.ts placeOrder - 주문 API 호출 * @see features/trade/components/TradeContainer.tsx OrderForm - 우측 주문 패널 렌더링 */ export function OrderForm({ stock }: OrderFormProps) { const verifiedCredentials = useKisRuntimeStore( (state) => state.verifiedCredentials, ); const { placeOrder, isLoading, error } = useOrder(); // ========== FORM STATE ========== const [price, setPrice] = useState(stock?.currentPrice.toString() || ""); const [quantity, setQuantity] = useState(""); const [activeTab, setActiveTab] = useState<"buy" | "sell">("buy"); // ========== ORDER HANDLER ========== const handleOrder = async (side: DashboardOrderSide) => { if (!stock || !verifiedCredentials) return; const priceNum = parseInt(price.replace(/,/g, ""), 10); const qtyNum = parseInt(quantity.replace(/,/g, ""), 10); if (Number.isNaN(priceNum) || priceNum <= 0) { alert("가격을 올바르게 입력해 주세요."); return; } if (Number.isNaN(qtyNum) || qtyNum <= 0) { alert("수량을 올바르게 입력해 주세요."); return; } if (!verifiedCredentials.accountNo) { alert("계좌번호가 없습니다. 설정에서 계좌번호를 입력해 주세요."); return; } const response = await placeOrder( { symbol: stock.symbol, side, orderType: "limit", price: priceNum, quantity: qtyNum, accountNo: verifiedCredentials.accountNo, accountProductCode: "01", }, verifiedCredentials, ); if (response?.orderNo) { alert(`주문 전송 완료: ${response.orderNo}`); setQuantity(""); } }; const totalPrice = parseInt(price.replace(/,/g, "") || "0", 10) * parseInt(quantity.replace(/,/g, "") || "0", 10); const setPercent = (pct: string) => { // TODO: 계좌 잔고 연동 시 퍼센트 자동 계산으로 교체 console.log("Percent clicked:", pct); }; const isMarketDataAvailable = Boolean(stock); return (
setActiveTab(value as "buy" | "sell")} className="flex h-full w-full flex-col" > {/* ========== ORDER SIDE TABS ========== */} 매수 매도 {/* ========== BUY TAB ========== */} {/* ========== SELL TAB ========== */}
); } /** * @description 주문 입력 영역(가격/수량/총액)을 렌더링합니다. * @see features/trade/components/order/OrderForm.tsx OrderForm - 매수/매도 탭에서 공용 호출 */ function OrderInputs({ type, price, setPrice, quantity, setQuantity, totalPrice, disabled, hasError, errorMessage, }: { type: "buy" | "sell"; price: string; setPrice: (v: string) => void; quantity: string; setQuantity: (v: string) => void; totalPrice: number; disabled: boolean; hasError: boolean; errorMessage: string | null; }) { return (
주문가능 - {type === "buy" ? "KRW" : "주"}
{hasError && (
{errorMessage}
)}
{type === "buy" ? "매수가격" : "매도가격"} setPrice(e.target.value)} disabled={disabled} />
주문수량 setQuantity(e.target.value)} disabled={disabled} />
주문총액
); } /** * @description 주문 비율(10/25/50/100%) 단축 버튼을 표시합니다. * @see features/trade/components/order/OrderForm.tsx setPercent - 버튼 클릭 이벤트 처리 */ function PercentButtons({ onSelect }: { onSelect: (pct: string) => void }) { return (
{["10%", "25%", "50%", "100%"].map((pct) => ( ))}
); }