feat(screener): integrate mode toggle (form|canvas) with lazy canvas
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, lazy, Suspense } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import './Screener.css';
|
import './Screener.css';
|
||||||
|
|
||||||
@@ -6,6 +6,8 @@ import { useScreenerMeta } from './hooks/useScreenerMeta';
|
|||||||
import { useScreenerSettings } from './hooks/useScreenerSettings';
|
import { useScreenerSettings } from './hooks/useScreenerSettings';
|
||||||
import { useScreenerRun } from './hooks/useScreenerRun';
|
import { useScreenerRun } from './hooks/useScreenerRun';
|
||||||
import { useScreenerHistory } from './hooks/useScreenerHistory';
|
import { useScreenerHistory } from './hooks/useScreenerHistory';
|
||||||
|
import { useScreenerMode } from './hooks/useScreenerMode';
|
||||||
|
import { useIsMobile } from '../../../hooks/useIsMobile';
|
||||||
|
|
||||||
import GatePanel from './components/GatePanel';
|
import GatePanel from './components/GatePanel';
|
||||||
import NodePanel from './components/NodePanel';
|
import NodePanel from './components/NodePanel';
|
||||||
@@ -13,18 +15,22 @@ import GlobalControls from './components/GlobalControls';
|
|||||||
import ResultTable from './components/ResultTable';
|
import ResultTable from './components/ResultTable';
|
||||||
import TelegramPreview from './components/TelegramPreview';
|
import TelegramPreview from './components/TelegramPreview';
|
||||||
import RunHistoryList from './components/RunHistoryList';
|
import RunHistoryList from './components/RunHistoryList';
|
||||||
|
import ModeToggle from './components/ModeToggle';
|
||||||
|
|
||||||
|
const CanvasLayout = lazy(() => import('./components/canvas/CanvasLayout'));
|
||||||
|
|
||||||
export default function Screener() {
|
export default function Screener() {
|
||||||
const { meta, loading: metaLoading } = useScreenerMeta();
|
const { meta, loading: metaLoading } = useScreenerMeta();
|
||||||
const { settings, dirty, setLocal, save } = useScreenerSettings();
|
const { settings, dirty, setLocal, save } = useScreenerSettings();
|
||||||
const { result, running, previewHistory, runPreview, runSave, selectPreview } = useScreenerRun();
|
const { result, running, previewHistory, runPreview, runSave, selectPreview } = useScreenerRun();
|
||||||
const { runs, runs_loading, selectRun, selectedRun } = useScreenerHistory();
|
const { runs, runs_loading, selectRun, selectedRun } = useScreenerHistory();
|
||||||
|
const { mode, setMode } = useScreenerMode();
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
const effectiveMode = isMobile ? 'form' : mode;
|
||||||
|
|
||||||
// 비교 모드 — 미리보기 히스토리에서 선택된 항목 ID
|
|
||||||
const [compareId, setCompareId] = useState(null);
|
const [compareId, setCompareId] = useState(null);
|
||||||
const compareItem = previewHistory.find((p) => p.id === compareId);
|
const compareItem = previewHistory.find((p) => p.id === compareId);
|
||||||
const compareResult = compareItem?.result ?? null;
|
const compareResult = compareItem?.result ?? null;
|
||||||
|
|
||||||
const activeResult = selectedRun || result;
|
const activeResult = selectedRun || result;
|
||||||
|
|
||||||
if (metaLoading || !meta || !settings) {
|
if (metaLoading || !meta || !settings) {
|
||||||
@@ -41,31 +47,47 @@ export default function Screener() {
|
|||||||
· 분석 기준일: {activeResult?.asof ?? settings.asof ?? '-'}
|
· 분석 기준일: {activeResult?.asof ?? settings.asof ?? '-'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="screener-header-right">
|
||||||
|
{!isMobile && <ModeToggle value={mode} onChange={setMode} />}
|
||||||
<nav>
|
<nav>
|
||||||
<Link to="/stock">시장</Link>
|
<Link to="/stock">시장</Link>
|
||||||
<Link to="/stock/trade">트레이드</Link>
|
<Link to="/stock/trade">트레이드</Link>
|
||||||
</nav>
|
</nav>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
{effectiveMode === 'form' ? (
|
||||||
<div className="screener-grid">
|
<div className="screener-grid">
|
||||||
<aside className="screener-left">
|
<aside className="screener-left">
|
||||||
<GatePanel meta={meta.gate_nodes[0]} value={settings.gate_params} onChange={(p) => setLocal({...settings, gate_params: p})} />
|
<GatePanel
|
||||||
<NodePanel meta={meta.score_nodes} weights={settings.weights} params={settings.node_params}
|
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 })}
|
onWeights={(w) => setLocal({ ...settings, weights: w })}
|
||||||
onParams={(p) => setLocal({...settings, node_params: p})} />
|
onParams={(p) => setLocal({ ...settings, node_params: p })}
|
||||||
<GlobalControls settings={settings} setSettings={setLocal}
|
/>
|
||||||
|
<GlobalControls
|
||||||
|
settings={settings} setSettings={setLocal}
|
||||||
onRun={() => runPreview(settings)}
|
onRun={() => runPreview(settings)}
|
||||||
onSave={() => runSave(settings)}
|
onSave={() => runSave(settings)}
|
||||||
onPersist={save}
|
onPersist={save}
|
||||||
dirty={dirty}
|
dirty={dirty}
|
||||||
running={running} />
|
running={running}
|
||||||
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main className="screener-center">
|
<main className="screener-center">
|
||||||
<ResultTable result={activeResult} compareWith={compareResult} compareLabel={compareItem ? new Date(compareItem.timestamp).toLocaleTimeString() : null} />
|
<ResultTable
|
||||||
|
result={activeResult}
|
||||||
|
compareWith={compareResult}
|
||||||
|
compareLabel={compareItem ? new Date(compareItem.timestamp).toLocaleTimeString() : null}
|
||||||
|
/>
|
||||||
<TelegramPreview payload={activeResult?.telegram_payload} />
|
<TelegramPreview payload={activeResult?.telegram_payload} />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<aside className="screener-right">
|
<aside className="screener-right">
|
||||||
<RunHistoryList
|
<RunHistoryList
|
||||||
runs={runs}
|
runs={runs}
|
||||||
@@ -79,6 +101,29 @@ export default function Screener() {
|
|||||||
/>
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<Suspense fallback={<div className="screener-loading">캔버스 로딩 중…</div>}>
|
||||||
|
<CanvasLayout
|
||||||
|
meta={meta}
|
||||||
|
settings={settings}
|
||||||
|
setLocal={setLocal}
|
||||||
|
save={save}
|
||||||
|
dirty={dirty}
|
||||||
|
result={result}
|
||||||
|
running={running}
|
||||||
|
previewHistory={previewHistory}
|
||||||
|
runPreview={runPreview}
|
||||||
|
runSave={runSave}
|
||||||
|
selectPreview={selectPreview}
|
||||||
|
runs={runs}
|
||||||
|
runs_loading={runs_loading}
|
||||||
|
selectRun={selectRun}
|
||||||
|
selectedRun={selectedRun}
|
||||||
|
compareId={compareId}
|
||||||
|
setCompareId={setCompareId}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user