feat(stock): /stock/screener 페이지 골격 + hooks 4개 + 컴포넌트 stub 6개
This commit is contained in:
@@ -1,10 +1,71 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import './Screener.css';
|
||||
|
||||
import { useScreenerMeta } from './hooks/useScreenerMeta';
|
||||
import { useScreenerSettings } from './hooks/useScreenerSettings';
|
||||
import { useScreenerRun } from './hooks/useScreenerRun';
|
||||
import { useScreenerHistory } from './hooks/useScreenerHistory';
|
||||
|
||||
import GatePanel from './components/GatePanel';
|
||||
import NodePanel from './components/NodePanel';
|
||||
import GlobalControls from './components/GlobalControls';
|
||||
import ResultTable from './components/ResultTable';
|
||||
import TelegramPreview from './components/TelegramPreview';
|
||||
import RunHistoryList from './components/RunHistoryList';
|
||||
|
||||
export default function Screener() {
|
||||
return (
|
||||
<div style={{ padding: 24, color: '#e5e7eb' }}>
|
||||
<h1>스크리너</h1>
|
||||
<p>구현 중 — Task 4.3에서 본 페이지 골격 + hooks + 컴포넌트가 추가됩니다.</p>
|
||||
const { meta, loading: metaLoading } = useScreenerMeta();
|
||||
const { settings, dirty, setLocal, save } = useScreenerSettings();
|
||||
const { result, running, runPreview, runSave } = useScreenerRun();
|
||||
const { runs, runs_loading, selectRun, selectedRun } = useScreenerHistory();
|
||||
|
||||
const activeResult = selectedRun || result;
|
||||
|
||||
if (metaLoading || !meta || !settings) {
|
||||
return <div className="screener-loading">로딩 중…</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="screener-page">
|
||||
<header className="screener-header">
|
||||
<div>
|
||||
<h1>스크리너</h1>
|
||||
<p className="meta">
|
||||
최근 자동 잡: {runs?.find(r => r.mode === 'auto')?.asof ?? '-'}
|
||||
· 분석 기준일: {activeResult?.asof ?? settings.asof ?? '-'}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
<nav>
|
||||
<Link to="/stock">시장</Link>
|
||||
<Link to="/stock/trade">트레이드</Link>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div className="screener-grid">
|
||||
<aside className="screener-left">
|
||||
<GatePanel meta={meta.gate_nodes[0]} value={settings.gate_params} onChange={(p) => setLocal({...settings, gate_params: p})} />
|
||||
<NodePanel meta={meta.score_nodes} weights={settings.weights} params={settings.node_params}
|
||||
onWeights={(w) => setLocal({...settings, weights: w})}
|
||||
onParams={(p) => setLocal({...settings, node_params: p})} />
|
||||
<GlobalControls settings={settings} setSettings={setLocal}
|
||||
onRun={() => runPreview(settings)}
|
||||
onSave={() => runSave(settings)}
|
||||
onPersist={save}
|
||||
dirty={dirty}
|
||||
running={running} />
|
||||
</aside>
|
||||
|
||||
<main className="screener-center">
|
||||
<ResultTable result={activeResult} />
|
||||
<TelegramPreview payload={activeResult?.telegram_payload} />
|
||||
</main>
|
||||
|
||||
<aside className="screener-right">
|
||||
<RunHistoryList runs={runs} loading={runs_loading} onSelect={selectRun}
|
||||
selectedId={selectedRun?.meta?.id} />
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user