Files
goldenChart/frontend/src/components/trendSearch/TrendSearchSettingsPanel.tsx
T
2026-05-27 01:36:06 +09:00

155 lines
5.4 KiB
TypeScript

import React, { useMemo } from 'react';
import type { TrendSearchAppSettings } from '../../utils/trendSearchAppSettings';
import {
TREND_SEARCH_REFRESH_OPTIONS,
formatTrendRefreshLabel,
} from '../../utils/trendSearchAppSettings';
import TrendSearchBullishWeightsSection from './TrendSearchBullishWeightsSection';
interface Props {
settings: TrendSearchAppSettings;
onChange: (next: TrendSearchAppSettings) => void;
}
const SettingRow: React.FC<{
label: string;
desc?: string;
children: React.ReactNode;
}> = ({ label, desc, children }) => (
<div className="stg-row">
<div className="stg-row-label">
<span className="stg-row-title">{label}</span>
{desc && <span className="stg-row-desc">{desc}</span>}
</div>
<div className="stg-row-ctrl">{children}</div>
</div>
);
const SettingSection: React.FC<{ title: string; children: React.ReactNode }> = ({
title, children,
}) => (
<div className="stg-section">
<div className="stg-section-title">{title}</div>
<div className="stg-section-body">{children}</div>
</div>
);
const TrendSearchSettingsPanel: React.FC<Props> = ({ settings, onChange }) => {
const patch = (p: Partial<TrendSearchAppSettings>) => onChange({ ...settings, ...p });
const weights = useMemo(() => ({
weightMaAlignment: settings.weightMaAlignment,
weightMaSlope: settings.weightMaSlope,
weightAdxTrend: settings.weightAdxTrend,
weightMacdMomentum: settings.weightMacdMomentum,
weightPricePosition: settings.weightPricePosition,
}), [settings]);
return (
<div className="tsd-settings-panel">
<SettingSection title="상승추세 검색그룹 배점">
<TrendSearchBullishWeightsSection
variant="settings"
weights={weights}
onChange={w => patch(w)}
/>
</SettingSection>
<SettingSection title="검색 조건">
<SettingRow label="최소 합격 점수" desc="이 점수 이상인 종목만 결과에 포함됩니다.">
<input
type="number"
className="stg-input stg-input--num"
min={0}
max={100}
step={5}
value={settings.minTrendScore}
onChange={e => patch({ minTrendScore: Number(e.target.value) })}
/>
</SettingRow>
<SettingRow label="결과 목록 최대 개수" desc="스캔 후 화면에 표시할 최대 종목 수입니다.">
<input
type="number"
className="stg-input stg-input--num"
min={5}
max={50}
value={settings.limit}
onChange={e => patch({ limit: Number(e.target.value) })}
/>
</SettingRow>
<SettingRow label="스캔 대상 종목 수" desc="거래대금 상위 N종목을 평가합니다.">
<input
type="number"
className="stg-input stg-input--num"
min={20}
max={120}
value={settings.scanLimit}
onChange={e => patch({ scanLimit: Number(e.target.value) })}
/>
</SettingRow>
</SettingSection>
<SettingSection title="자동 갱신">
<SettingRow label="기본 갱신 간격" desc="추세검색 화면 자동 갱신 드롭다운의 기본값입니다.">
<select
className="stg-select"
value={settings.autoRefreshSeconds}
onChange={e => patch({ autoRefreshSeconds: Number(e.target.value) })}
>
{TREND_SEARCH_REFRESH_OPTIONS.map(sec => (
<option key={sec} value={sec}>{formatTrendRefreshLabel(sec)}</option>
))}
</select>
</SettingRow>
</SettingSection>
<SettingSection title="투자대상 자동추가">
<SettingRow
label="자동추가 사용"
desc="추세검색 갱신 후 상위 순위 종목을 가상매매 투자대상에 자동 등록합니다."
>
<label className="stg-toggle">
<input
type="checkbox"
checked={settings.autoAddTargetsEnabled}
onChange={e => patch({ autoAddTargetsEnabled: e.target.checked })}
/>
<span className="stg-toggle-slider" />
</label>
</SettingRow>
<SettingRow
label="자동추가 순위 (상위 N위)"
desc="추세점수 순위 1위부터 N위까지 자동추가 후보입니다. 투자대상 최대 개수를 초과하면 추가하지 않습니다."
>
<input
type="number"
className="stg-input stg-input--num"
min={1}
max={50}
disabled={!settings.autoAddTargetsEnabled}
value={settings.autoAddTopRank}
onChange={e => patch({ autoAddTopRank: Number(e.target.value) })}
/>
</SettingRow>
<SettingRow
label="자동추가 최소 점수"
desc="상위 순위 안에서도 추세점수가 이 값 이상인 종목만 투자대상에 자동추가합니다."
>
<input
type="number"
className="stg-input stg-input--num"
min={0}
max={100}
step={5}
disabled={!settings.autoAddTargetsEnabled}
value={settings.autoAddMinScore}
onChange={e => patch({ autoAddMinScore: Number(e.target.value) })}
/>
</SettingRow>
</SettingSection>
</div>
);
};
export default TrendSearchSettingsPanel;