From 248835fa541ca45879c751a168fb3014c1368335 Mon Sep 17 00:00:00 2001 From: gahusb Date: Thu, 19 Mar 2026 23:36:33 +0900 Subject: [PATCH] =?UTF-8?q?stock=20=EC=8B=A4=ED=98=84=EC=86=90=EC=9D=B5=20?= =?UTF-8?q?=EB=B3=B4=EC=97=AC=EC=A4=84=20=EC=88=98=20=EC=9E=88=EA=B2=8C=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EA=B5=AC=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 25 ++ src/pages/stock/Stock.css | 547 ++++++++++++++++++++++++++++++++- src/pages/stock/StockTrade.jsx | 516 ++++++++++++++++++++++++++++++- 3 files changed, 1086 insertions(+), 2 deletions(-) diff --git a/src/api.js b/src/api.js index 80badc0..908fe50 100644 --- a/src/api.js +++ b/src/api.js @@ -221,6 +221,31 @@ export function clearTodos() { return apiDelete('/api/todos/done'); } +// ── 실현손익 내역 API ───────────────────────────────────────────────────────── +// GET /api/portfolio/sell-history?broker=X&days=N → { records: [...] } +// POST /api/portfolio/sell-history → 저장된 레코드 반환 +// DELETE /api/portfolio/sell-history/:id → { ok: true } + +export function getSellHistory({ broker, days } = {}) { + const qs = new URLSearchParams(); + if (broker && broker !== 'ALL') qs.set('broker', broker); + if (days) qs.set('days', String(days)); + const q = qs.toString(); + return apiGet(`/api/portfolio/sell-history${q ? '?' + q : ''}`); +} + +export function addSellHistory(record) { + return apiPost('/api/portfolio/sell-history', record); +} + +export function updateSellHistory(id, record) { + return apiPut(`/api/portfolio/sell-history/${id}`, record); +} + +export function deleteSellHistory(id) { + return apiDelete(`/api/portfolio/sell-history/${id}`); +} + // ── 블로그 API ──────────────────────────────────────────────────────────────── // GET /api/blog/posts → { posts: [{id, title, tags, body, date, excerpt}] } // POST /api/blog/posts → 새 글 생성 diff --git a/src/pages/stock/Stock.css b/src/pages/stock/Stock.css index 9bc1bde..8b0c062 100644 --- a/src/pages/stock/Stock.css +++ b/src/pages/stock/Stock.css @@ -2104,4 +2104,549 @@ .risk-grid { grid-template-columns: 1fr; } -} \ No newline at end of file +} +/* ══════════════════════════════════════════════════════════════════ + 실현손익 내역 + ══════════════════════════════════════════════════════════════════ */ + +.sell-history__filters { + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: 16px; +} + +.sell-history__filter-group { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} + +.sell-history__filter-label { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--muted); + min-width: 36px; +} + +.sell-history__filter-btn { + padding: 4px 12px; + border-radius: 999px; + border: 1px solid var(--line); + background: transparent; + color: var(--muted); + font-size: 12px; + cursor: pointer; + transition: all 0.15s; +} + +.sell-history__filter-btn:hover { + border-color: rgba(255, 255, 255, 0.3); + color: var(--text); +} + +.sell-history__filter-btn.is-active { + border-color: var(--accent-stock); + color: var(--accent-stock); + background: rgba(99, 179, 237, 0.08); +} + +.sell-history__summary { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + gap: 10px; + margin-bottom: 20px; +} + +.sell-history__summary-card { + border: 1px solid var(--line); + border-radius: 12px; + padding: 12px 16px; + background: rgba(0, 0, 0, 0.2); + display: flex; + flex-direction: column; + gap: 6px; + font-size: 12px; + color: var(--muted); +} + +.sell-history__summary-card strong { + font-size: 16px; + font-weight: 700; + color: var(--text); +} + +.sell-history__table-wrap { + overflow-x: auto; + border-radius: 12px; + border: 1px solid var(--line); +} + +.sell-history__table { + width: 100%; + border-collapse: collapse; + font-size: 13px; +} + +.sell-history__table thead th { + padding: 10px 12px; + text-align: left; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--muted); + border-bottom: 1px solid var(--line); + white-space: nowrap; +} + +.sell-history__table thead th.is-num { + text-align: right; +} + +.sell-history__table tbody td { + padding: 10px 12px; + border-bottom: 1px solid rgba(255, 255, 255, 0.04); + vertical-align: middle; + white-space: nowrap; + color: var(--text); +} + +.sell-history__table tbody tr:last-child td { + border-bottom: none; +} + +.sell-history__table tbody tr:hover td { + background: rgba(255, 255, 255, 0.03); +} + +.sell-history__table td.is-num { + text-align: right; + font-variant-numeric: tabular-nums; +} + +.sell-history__name { + font-weight: 500; + display: block; +} + +.sell-history__ticker { + font-size: 11px; + color: var(--muted); + font-family: monospace; +} + +.sell-history__broker { + font-size: 12px; + padding: 2px 8px; + border-radius: 999px; + border: 1px solid var(--line); + color: var(--muted); +} + +.sell-history__date { + font-size: 12px; + color: var(--muted); +} + +@media (max-width: 768px) { + .sell-history__summary { + grid-template-columns: repeat(2, 1fr); + } +} + +/* ══════════════════════════════════════════════════════════════════ + 매도 내역 수동 추가/수정 폼 + ══════════════════════════════════════════════════════════════════ */ + +.sh-form { + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 14px; + padding: 20px; + background: rgba(0, 0, 0, 0.25); + margin-bottom: 20px; + display: grid; + gap: 16px; +} + +.sh-form__title { + font-size: 13px; + font-weight: 600; + color: var(--text); +} + +.sh-form__grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 12px; +} + +.sh-form__grid label { + display: flex; + flex-direction: column; + gap: 6px; + font-size: 12px; + color: var(--muted); +} + +.sh-form__grid label input { + padding: 7px 10px; + border-radius: 8px; + border: 1px solid var(--line); + background: rgba(0, 0, 0, 0.3); + color: var(--text); + font-size: 13px; + width: 100%; + box-sizing: border-box; +} + +.sh-form__grid label input:focus { + outline: none; + border-color: var(--accent-stock); +} + +.sh-form__datetime { + grid-column: span 2; +} + +.sh-form__preview { + display: flex; + gap: 20px; + flex-wrap: wrap; + padding: 10px 14px; + border-radius: 10px; + background: rgba(255, 255, 255, 0.04); + border: 1px solid var(--line); + font-size: 13px; + color: var(--muted); +} + +.sh-form__preview strong { + margin-left: 6px; +} + +.sh-form__actions { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.sh-row-actions { + display: flex; + gap: 4px; + justify-content: flex-end; +} + +@media (max-width: 640px) { + .sh-form__grid { + grid-template-columns: 1fr 1fr; + } + + .sh-form__datetime { + grid-column: span 2; + } +} + +/* ══════════════════════════════════════════════════════════════════ + 실현손익 floating 토글 버튼 (우측 고정) + ══════════════════════════════════════════════════════════════════ */ + +.sh-floating-toggle { + position: fixed; + right: 0; + top: 50%; + transform: translateY(-50%); + z-index: 190; + + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 14px 10px; + border-radius: 12px 0 0 12px; + border: 1px solid rgba(251, 191, 36, 0.35); + border-right: none; + background: rgba(7, 11, 25, 0.92); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + color: #fbbf24; + cursor: pointer; + box-shadow: -4px 0 24px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(251, 191, 36, 0.08) inset; + transition: background 0.2s, box-shadow 0.2s, border-color 0.2s, padding 0.2s; + white-space: nowrap; +} + +.sh-floating-toggle:hover { + background: rgba(251, 191, 36, 0.1); + border-color: rgba(251, 191, 36, 0.65); + box-shadow: -4px 0 32px rgba(251, 191, 36, 0.15); + padding-right: 14px; +} + +.sh-floating-toggle__icon { + font-size: 20px; + line-height: 1; +} + +.sh-floating-toggle__label { + font-size: 10px; + font-weight: 600; + letter-spacing: 0.08em; + writing-mode: vertical-rl; + text-orientation: mixed; +} + +.sh-floating-toggle__badge { + background: #fbbf24; + color: #000; + border-radius: 999px; + font-size: 10px; + font-weight: 700; + padding: 2px 5px; + line-height: 1.3; + box-shadow: 0 2px 6px rgba(0,0,0,0.4); +} + +/* ══════════════════════════════════════════════════════════════════ + 실현손익 드로어 (slide-in from right) + ══════════════════════════════════════════════════════════════════ */ + +.sh-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.55); + z-index: 200; + animation: sh-backdrop-in 0.2s ease; +} + +@keyframes sh-backdrop-in { + from { opacity: 0; } + to { opacity: 1; } +} + +.sh-drawer { + position: fixed; + top: 0; + right: 0; + height: 100dvh; + width: min(520px, 100vw); + background: var(--bg, #070b19); + border-left: 1px solid var(--line); + z-index: 201; + display: flex; + flex-direction: column; + gap: 0; + overflow-y: auto; + overflow-x: hidden; + padding: 0; + transform: translateX(100%); + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); + /* 스크롤바 */ + scrollbar-width: thin; + scrollbar-color: var(--line) transparent; +} + +.sh-drawer.is-open { + transform: translateX(0); +} + +.sh-drawer__header { + position: sticky; + top: 0; + z-index: 10; + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + padding: 20px 20px 16px; + background: var(--bg, #070b19); + border-bottom: 1px solid var(--line); +} + +.sh-drawer__eyebrow { + margin: 0 0 4px; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.2em; + color: #fbbf24; +} + +.sh-drawer__title { + margin: 0; + font-size: 18px; + font-weight: 700; +} + +.sh-drawer__header-actions { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} + +.sh-drawer__close { + width: 32px; + height: 32px; + border-radius: 50%; + border: 1px solid var(--line); + background: transparent; + color: var(--muted); + cursor: pointer; + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.15s; + flex-shrink: 0; +} + +.sh-drawer__close:hover { + background: rgba(255, 255, 255, 0.08); + color: var(--text); +} + +/* 드로어 내 섹션들 패딩 */ +.sh-drawer .sell-history__filters, +.sh-drawer .sell-history__summary, +.sh-drawer .sh-drawer__list, +.sh-drawer .sh-form, +.sh-drawer .sh-drawer__empty { + margin: 0; + padding: 16px 20px; +} + +.sh-drawer .sell-history__filters { + border-bottom: 1px solid var(--line); + padding-bottom: 14px; +} + +.sh-drawer .sell-history__summary { + grid-template-columns: repeat(2, 1fr); + border-bottom: 1px solid var(--line); +} + +.sh-drawer .sh-form { + border-radius: 0; + border: none; + border-bottom: 1px solid var(--line); + background: rgba(0, 0, 0, 0.3); +} + +.sh-drawer .sh-form__grid { + grid-template-columns: 1fr 1fr; +} + +/* 드로어 카드형 목록 */ +.sh-drawer__list { + display: flex; + flex-direction: column; + gap: 12px; + padding-top: 16px; + padding-bottom: 24px; +} + +.sh-drawer__item { + border: 1px solid var(--line); + border-radius: 12px; + padding: 14px; + background: rgba(255, 255, 255, 0.02); + display: flex; + flex-direction: column; + gap: 10px; + transition: background 0.15s; +} + +.sh-drawer__item:hover { + background: rgba(255, 255, 255, 0.04); +} + +.sh-drawer__item-top { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.sh-drawer__item-name { + display: flex; + align-items: center; + gap: 8px; + font-weight: 600; + font-size: 14px; + color: var(--text); +} + +.sh-drawer__item-name code { + font-size: 11px; + color: var(--muted); + background: rgba(255, 255, 255, 0.06); + padding: 2px 6px; + border-radius: 4px; + font-family: monospace; +} + +.sh-drawer__item-actions { + display: flex; + gap: 4px; + flex-shrink: 0; +} + +.sh-drawer__item-meta { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.sh-drawer__item-metrics { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; +} + +.sh-drawer__item-metrics > div { + display: flex; + flex-direction: column; + gap: 3px; + font-size: 12px; +} + +.sh-drawer__item-metrics > div span { + color: var(--muted); + font-size: 11px; +} + +.sh-drawer__item-metrics > div strong { + font-size: 13px; + font-weight: 600; + color: var(--text); + font-variant-numeric: tabular-nums; +} + +.sh-drawer__empty { + color: var(--muted); + font-size: 13px; + text-align: center; + padding-top: 40px !important; +} + +@media (max-width: 480px) { + .sh-drawer { + width: 100vw; + } + + .sh-drawer__item-metrics { + grid-template-columns: repeat(2, 1fr); + } + + .sh-drawer .sh-form__grid { + grid-template-columns: 1fr; + } + + .sh-drawer .sh-form__datetime { + grid-column: span 1; + } +} diff --git a/src/pages/stock/StockTrade.jsx b/src/pages/stock/StockTrade.jsx index 03825b5..12228f2 100644 --- a/src/pages/stock/StockTrade.jsx +++ b/src/pages/stock/StockTrade.jsx @@ -15,6 +15,10 @@ import { getWTI, getAssetHistory, saveAssetSnapshot, + getSellHistory, + addSellHistory, + updateSellHistory, + deleteSellHistory, } from '../../api'; import Loading from '../../components/Loading'; import './Stock.css'; @@ -124,6 +128,25 @@ const emptyPortfolioForm = { avg_price: '', }; +/* ── empty sell-history form ─────────────────────────────────────── */ + +const toLocalDatetimeValue = (isoStr) => { + if (!isoStr) return ''; + const d = new Date(isoStr); + const pad = (n) => String(n).padStart(2, '0'); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; +}; + +const emptySellForm = () => ({ + broker: '', + ticker: '', + name: '', + quantity: '', + avg_price: '', + sell_price: '', + sold_at: toLocalDatetimeValue(new Date().toISOString()), +}); + /* ── TAB IDs ─────────────────────────────────────────────────────── */ const TAB_PORTFOLIO = 'portfolio'; @@ -163,6 +186,22 @@ const StockTrade = () => { const [sellConfirmId, setSellConfirmId] = useState(null); const [sellLoading, setSellLoading] = useState(false); + /* 실현손익 내역 */ + const [sellHistory, setSellHistory] = useState([]); + const [sellHistoryLoading, setSellHistoryLoading] = useState(false); + const [sellHistoryBroker, setSellHistoryBroker] = useState('ALL'); + const [sellHistoryPeriod, setSellHistoryPeriod] = useState('3M'); + + /* 실현손익 드로어 */ + const [sellDrawerOpen, setSellDrawerOpen] = useState(false); + + /* 실현손익 수동 추가/수정 폼 */ + const [sellFormOpen, setSellFormOpen] = useState(false); + const [sellEditId, setSellEditId] = useState(null); // null = 추가, number = 수정 중 id + const [sellForm, setSellForm] = useState(emptySellForm()); + const [sellFormSaving, setSellFormSaving] = useState(false); + const [sellFormError, setSellFormError] = useState(''); + /* Cash (예수금) form */ const [cashForm, setCashForm] = useState({ broker: '', cash: '' }); const [cashSaving, setCashSaving] = useState(false); @@ -231,6 +270,18 @@ const StockTrade = () => { } }, []); + const loadSellHistory = useCallback(async () => { + setSellHistoryLoading(true); + try { + const data = await getSellHistory(); + setSellHistory(data?.records ?? (Array.isArray(data) ? data : [])); + } catch { + /* 백엔드 미구현 시 빈 배열 유지 */ + } finally { + setSellHistoryLoading(false); + } + }, []); + const loadBalance = useCallback(async () => { setBalanceLoading(true); setBalanceError(''); @@ -302,12 +353,13 @@ const StockTrade = () => { useEffect(() => { if (activeTab === TAB_PORTFOLIO && !portfolioLoaded) { loadPortfolio(); + loadSellHistory(); } else if (activeTab === TAB_AI && !balanceLoaded) { loadBalance(); } else if (activeTab === TAB_REPORT && !portfolioLoaded) { loadPortfolio(); } - }, [activeTab, portfolioLoaded, balanceLoaded, loadPortfolio, loadBalance]); + }, [activeTab, portfolioLoaded, balanceLoaded, loadPortfolio, loadBalance, loadSellHistory]); /* 자산 추이: 포트폴리오 탭 첫 진입 또는 기간 변경 시 로드 */ useEffect(() => { @@ -495,8 +547,12 @@ const StockTrade = () => { const handleSell = async (item) => { const sellPrice = item.current_price ?? item.avg_price; + const avgPrice = item.avg_price ?? 0; const qty = item.quantity ?? 0; const saleAmount = sellPrice * qty; + const buyAmount = avgPrice * qty; + const realizedProfit = saleAmount - buyAmount; + const realizedRate = buyAmount > 0 ? (realizedProfit / buyAmount) * 100 : 0; const broker = item.broker ?? ''; setSellLoading(true); @@ -506,6 +562,29 @@ const StockTrade = () => { const newCash = (existing?.cash ?? 0) + saleAmount; await upsertCash(broker, newCash); await deletePortfolio(item.id); + + // 실현손익 기록 저장 (백엔드) + const record = { + broker, + ticker: item.ticker ?? '', + name: item.name ?? item.ticker ?? 'N/A', + quantity: qty, + avg_price: avgPrice, + sell_price: sellPrice, + buy_amount: buyAmount, + sell_amount: saleAmount, + realized_profit: realizedProfit, + realized_rate: realizedRate, + sold_at: new Date().toISOString(), + }; + try { + const saved = await addSellHistory(record); + setSellHistory((prev) => [saved ?? record, ...prev]); + } catch { + /* 백엔드 미구현 시 낙관적 UI 유지 */ + setSellHistory((prev) => [{ ...record, id: Date.now() }, ...prev]); + } + setSellConfirmId(null); await loadPortfolio(); } catch (err) { @@ -515,6 +594,93 @@ const StockTrade = () => { } }; + const handleDeleteSellRecord = async (id) => { + setSellHistory((prev) => prev.filter((r) => r.id !== id)); + try { + await deleteSellHistory(id); + } catch { + /* 실패 시 목록 재로드로 복구 */ + loadSellHistory(); + } + }; + + /* 수동 추가 폼 열기 */ + const handleSellFormOpen = () => { + setSellEditId(null); + setSellForm(emptySellForm()); + setSellFormError(''); + setSellFormOpen(true); + }; + + /* 수정 폼 열기 */ + const handleSellEditStart = (record) => { + setSellEditId(record.id); + setSellForm({ + broker: record.broker ?? '', + ticker: record.ticker ?? '', + name: record.name ?? '', + quantity: String(record.quantity ?? ''), + avg_price: String(record.avg_price ?? ''), + sell_price: String(record.sell_price ?? ''), + sold_at: toLocalDatetimeValue(record.sold_at), + }); + setSellFormError(''); + setSellFormOpen(true); + }; + + /* 폼 닫기 */ + const handleSellFormClose = () => { + setSellFormOpen(false); + setSellEditId(null); + setSellFormError(''); + }; + + /* 폼 제출 (추가 or 수정) */ + const handleSellFormSubmit = async (e) => { + e.preventDefault(); + setSellFormSaving(true); + setSellFormError(''); + + const qty = Number(sellForm.quantity); + const avgPrice = Number(sellForm.avg_price); + const sellPrice = Number(sellForm.sell_price); + const buyAmount = avgPrice * qty; + const sellAmount = sellPrice * qty; + const realizedProfit = sellAmount - buyAmount; + const realizedRate = buyAmount > 0 ? (realizedProfit / buyAmount) * 100 : 0; + + const payload = { + broker: sellForm.broker.trim(), + ticker: sellForm.ticker.trim(), + name: sellForm.name.trim(), + quantity: qty, + avg_price: avgPrice, + sell_price: sellPrice, + buy_amount: buyAmount, + sell_amount: sellAmount, + realized_profit: realizedProfit, + realized_rate: realizedRate, + sold_at: sellForm.sold_at ? new Date(sellForm.sold_at).toISOString() : new Date().toISOString(), + }; + + try { + if (sellEditId != null) { + const updated = await updateSellHistory(sellEditId, payload); + setSellHistory((prev) => + prev.map((r) => (r.id === sellEditId ? (updated ?? { ...payload, id: sellEditId }) : r)) + ); + } else { + const saved = await addSellHistory(payload); + setSellHistory((prev) => [saved ?? { ...payload, id: Date.now() }, ...prev]); + } + handleSellFormClose(); + } catch (err) { + setSellFormError(err?.message ?? String(err)); + } finally { + setSellFormSaving(false); + } + }; + /* ── report sort ─────────────────────────────────────────────── */ const handleReportSort = (field) => { @@ -801,6 +967,37 @@ ${holdingsText}${marketText} }); }, [portfolioHoldings, reportSortField, reportSortDir]); + /* ── derived: 실현손익 필터 ────────────────────────────────────── */ + + const sellHistoryBrokers = useMemo(() => { + const set = new Set(sellHistory.map((r) => r.broker).filter(Boolean)); + return ['ALL', ...Array.from(set).sort()]; + }, [sellHistory]); + + const filteredSellHistory = useMemo(() => { + const now = new Date(); + const periodMs = { + '1M': 30 * 86400000, + '3M': 90 * 86400000, + '6M': 180 * 86400000, + '1Y': 365 * 86400000, + 'ALL': Infinity, + }[sellHistoryPeriod] ?? Infinity; + return sellHistory.filter((r) => { + if (sellHistoryBroker !== 'ALL' && r.broker !== sellHistoryBroker) return false; + const diff = now - new Date(r.sold_at); + return diff <= periodMs; + }); + }, [sellHistory, sellHistoryBroker, sellHistoryPeriod]); + + const sellHistorySummary = useMemo(() => { + const totalProfit = filteredSellHistory.reduce((s, r) => s + (r.realized_profit ?? 0), 0); + const totalSell = filteredSellHistory.reduce((s, r) => s + (r.sell_amount ?? 0), 0); + const totalBuy = filteredSellHistory.reduce((s, r) => s + (r.buy_amount ?? 0), 0); + const rate = totalBuy > 0 ? (totalProfit / totalBuy) * 100 : 0; + return { totalProfit, totalSell, totalBuy, rate, count: filteredSellHistory.length }; + }, [filteredSellHistory]); + /* ── render ───────────────────────────────────────────────────── */ return ( @@ -1555,6 +1752,8 @@ ${holdingsText}${marketText}

)} + + {/* sell history → 드로어로 이동됨 */} )} @@ -2195,6 +2394,321 @@ ${holdingsText}${marketText} ) : null} + + {/* ── 실현손익 floating 토글 버튼 (마우스 추적) ────────── */} + {!sellDrawerOpen && ( + + )} + + {/* ════════════════════════════════════════════════════════ + 실현손익 드로어 + ════════════════════════════════════════════════════════ */} + {sellDrawerOpen && ( +
{ setSellDrawerOpen(false); handleSellFormClose(); }} + /> + )} +
); };