feat(stock): /stock/screener 페이지 골격 + hooks 4개 + 컴포넌트 stub 6개
This commit is contained in:
3
src/pages/stock/screener/components/GatePanel.jsx
Normal file
3
src/pages/stock/screener/components/GatePanel.jsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function GatePanel({ meta, value, onChange }) {
|
||||
return <section className="screener-card"><h3>{meta?.label ?? '게이트'}</h3><p style={{fontSize: 12, color:'#9ca3af'}}>TODO: 게이트 파라미터 폼 (Task 4.5)</p></section>;
|
||||
}
|
||||
10
src/pages/stock/screener/components/GlobalControls.jsx
Normal file
10
src/pages/stock/screener/components/GlobalControls.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function GlobalControls({ settings, setSettings, onRun, onSave, onPersist, dirty, running }) {
|
||||
return (
|
||||
<section className="screener-card">
|
||||
<h3>실행 옵션</h3>
|
||||
<button onClick={onRun} disabled={running}>{running ? '실행 중…' : '지금 실행 (미리보기)'}</button>
|
||||
<button onClick={onSave} disabled={running} style={{ marginTop: 8 }}>스냅샷 저장</button>
|
||||
<button onClick={onPersist} disabled={!dirty} style={{ marginTop: 8 }}>설정 저장</button>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
3
src/pages/stock/screener/components/NodePanel.jsx
Normal file
3
src/pages/stock/screener/components/NodePanel.jsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function NodePanel({ meta, weights, params, onWeights, onParams }) {
|
||||
return <section className="screener-card"><h3>점수 노드 ({meta.length})</h3><p style={{fontSize: 12, color:'#9ca3af'}}>TODO: 노드별 카드 (Task 4.4)</p></section>;
|
||||
}
|
||||
25
src/pages/stock/screener/components/ResultTable.jsx
Normal file
25
src/pages/stock/screener/components/ResultTable.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
export default function ResultTable({ result }) {
|
||||
if (!result) return <section className="screener-card"><p style={{color:'#9ca3af'}}>아직 결과 없음. "지금 실행"을 눌러보세요.</p></section>;
|
||||
return (
|
||||
<section className="screener-card">
|
||||
<h3>Top {result.top_n} · 통과 {result.survivors_count}</h3>
|
||||
<table style={{ width: '100%', fontSize: 13 }}>
|
||||
<thead>
|
||||
<tr><th>#</th><th>종목</th><th>총점</th><th>진입</th><th>손절</th><th>익절</th><th>R%</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(result.results || []).map((r) => (
|
||||
<tr key={r.ticker}>
|
||||
<td>{r.rank}</td><td>{r.name} ({r.ticker})</td>
|
||||
<td>{r.total_score?.toFixed?.(1)}</td>
|
||||
<td>{r.entry_price?.toLocaleString?.()}</td>
|
||||
<td>{r.stop_price?.toLocaleString?.()}</td>
|
||||
<td>{r.target_price?.toLocaleString?.()}</td>
|
||||
<td>{r.r_pct?.toFixed?.(1)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
17
src/pages/stock/screener/components/RunHistoryList.jsx
Normal file
17
src/pages/stock/screener/components/RunHistoryList.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
export default function RunHistoryList({ runs, loading, onSelect, selectedId }) {
|
||||
if (loading) return <section className="screener-card"><p>로딩…</p></section>;
|
||||
return (
|
||||
<section className="screener-card">
|
||||
<h3>최근 실행</h3>
|
||||
<ul style={{listStyle:'none', padding:0, margin:0, fontSize:13}}>
|
||||
{(runs || []).map((r) => (
|
||||
<li key={r.id} style={{padding:'6px 0', borderBottom:'1px solid #1f2937', cursor:'pointer',
|
||||
color: selectedId === r.id ? '#fbbf24' : '#e5e7eb'}}
|
||||
onClick={() => onSelect(r.id)}>
|
||||
{r.asof} · {r.mode}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
9
src/pages/stock/screener/components/TelegramPreview.jsx
Normal file
9
src/pages/stock/screener/components/TelegramPreview.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
export default function TelegramPreview({ payload }) {
|
||||
if (!payload) return null;
|
||||
return (
|
||||
<section className="screener-card">
|
||||
<h3>텔레그램 미리보기</h3>
|
||||
<pre style={{whiteSpace:'pre-wrap', fontFamily:'monospace', fontSize:12}}>{payload.text}</pre>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user