Files
KisStock/pages/Trading.tsx
2026-01-31 22:34:57 +09:00

89 lines
3.9 KiB
TypeScript

import React, { useState, useMemo, useEffect } from 'react';
import { Search, ArrowRightLeft, ShieldCheck, Wallet, History, TrendingUp, Info } from 'lucide-react';
import { StockItem, OrderType, MarketType, TradeOrder } from '../types';
interface TradingProps {
marketMode: MarketType;
stocks: StockItem[];
onOrder: (order: Omit<TradeOrder, 'id' | 'timestamp' | 'status'>) => void;
}
const Trading: React.FC<TradingProps> = ({ marketMode, stocks, onOrder }) => {
const [search, setSearch] = useState('');
const [selectedStock, setSelectedStock] = useState<StockItem | null>(null);
const [orderAmount, setOrderAmount] = useState<number>(1);
const [orderType, setOrderType] = useState<OrderType>(OrderType.BUY);
// 모드 변경 시 수량 초기화
useEffect(() => {
setOrderAmount(1);
}, [orderType]);
// 시장 변경 시 선택 해제
useEffect(() => {
setSelectedStock(null);
}, [marketMode]);
const filteredStocks = useMemo(() => {
return stocks.filter(s =>
s.market === marketMode &&
!s.isHidden &&
(s.name.includes(search) || s.code.toLowerCase().includes(search.toLowerCase()))
);
}, [stocks, search, marketMode]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!selectedStock) return;
const confirmMsg = `${selectedStock.name} 종목을 ${orderAmount}${orderType === OrderType.BUY ? '매수' : '매도'}하시겠습니까?`;
if(window.confirm(confirmMsg)) {
onOrder({
stockCode: selectedStock.code,
stockName: selectedStock.name,
type: orderType,
price: selectedStock.price,
quantity: orderAmount
});
}
};
const isBuyMode = orderType === OrderType.BUY;
return (
<div className="grid grid-cols-1 lg:grid-cols-12 gap-10 animate-in fade-in duration-500 pb-20 max-w-[1600px] mx-auto">
{/* Left: Inventory List */}
<div className="lg:col-span-7 space-y-8">
<div className="bg-white p-10 rounded-[3.5rem] shadow-sm border border-slate-100 flex flex-col h-[750px]">
<div className="flex flex-col sm:flex-row gap-8 justify-between items-center mb-10">
<div>
<h3 className="text-2xl font-black text-slate-800 uppercase tracking-tight flex items-center gap-3">
<Wallet size={24} className="text-blue-600" /> {marketMode === MarketType.DOMESTIC ? '국내' : '해외'}
</h3>
</div>
<div className="relative w-full sm:w-[350px]">
<Search size={20} className="absolute left-6 top-1/2 -translate-y-1/2 text-slate-400" />
<input
type="text"
placeholder="종목명 또는 코드 검색..."
className="w-full pl-14 pr-6 py-4 bg-slate-50 border-2 border-transparent rounded-[1.8rem] focus:border-blue-500 focus:bg-white outline-none text-sm font-bold shadow-inner transition-all"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
</div>
<div className="flex-1 overflow-y-auto pr-2 scrollbar-hide space-y-4">
{filteredStocks.map(stock => (
<div
key={stock.code}
onClick={() => setSelectedStock(stock)}
className={`p-6 rounded-[2.5rem] border-2 transition-all cursor-pointer group flex items-center justify-between ${selectedStock?.code === stock.code ? 'border-blue-500 bg-blue-50/20 shadow-lg' : 'border-transparent bg-slate-50/60 hover:bg-white hover:border-slate-200'}`}
>
<div className="flex items-center gap-5">
<div className="w-12 h-12 rounded-2xl bg-slate-900 flex items-center justify-center font-black text-white text-[12px] uppercase">
{stock.name[0]}
</div>
<div>
<h4 className="font-black text-slate