보조지표 기준값 변경시 전략편집기 연동
This commit is contained in:
@@ -5,7 +5,7 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import type { IndicatorConfig } from '../types';
|
||||
import type { PlotDef, HLineDef } from '../utils/indicatorRegistry';
|
||||
import { buildMainIndicatorConfig } from '../utils/indicatorMainConfig';
|
||||
import { indicatorTypesOnChart } from '../utils/indicatorMainConfig';
|
||||
import { indicatorDefaultsFingerprint } from '../utils/indicatorDefaultsFingerprint';
|
||||
import IndicatorSettingsForm from './IndicatorSettingsForm';
|
||||
import { resetConfigToDefaults } from '../utils/indicatorSettingsEditor';
|
||||
import type { IchimokuCloudColors } from '../utils/ichimokuConfig';
|
||||
@@ -18,6 +18,13 @@ import IndicatorSettingsListSearch from './IndicatorSettingsListSearch';
|
||||
import IndicatorSettingsListRow from './IndicatorSettingsListRow';
|
||||
import { MAIN_INDICATOR_TYPES } from '../utils/indicatorMainTab';
|
||||
|
||||
export interface IndicatorSaveUiState {
|
||||
save: () => Promise<void>;
|
||||
dirty: boolean;
|
||||
saving: boolean;
|
||||
saveMessage: string | null;
|
||||
}
|
||||
|
||||
export interface IndicatorMainDefaultsPanelProps {
|
||||
chartIndicators: IndicatorConfig[];
|
||||
onAddToChart: (type: string, config?: IndicatorConfig) => void;
|
||||
@@ -35,32 +42,65 @@ export interface IndicatorMainDefaultsPanelProps {
|
||||
hlines?: HLineDef[],
|
||||
cloudColors?: IchimokuCloudColors,
|
||||
) => void;
|
||||
}
|
||||
|
||||
function persistDefaults(
|
||||
updated: IndicatorConfig,
|
||||
saveParams: IndicatorMainDefaultsPanelProps['saveParams'],
|
||||
saveVisual: IndicatorMainDefaultsPanelProps['saveVisual'],
|
||||
) {
|
||||
saveParams(updated.type, updated.params);
|
||||
saveVisual(updated.type, updated.plots, updated.hlines, updated.cloudColors);
|
||||
/** DB에 보조지표 기본값 일괄 저장 */
|
||||
saveAllDefaults?: (configs: Record<string, IndicatorConfig>) => Promise<void>;
|
||||
/** 상단 저장 버튼 상태 (SettingsPage 헤더 연동) */
|
||||
onSaveUiState?: (state: IndicatorSaveUiState | null) => void;
|
||||
/** DB 로드 완료·저장 후 재로드 트리거 */
|
||||
settingsRevision?: number;
|
||||
}
|
||||
|
||||
const ALL_TYPES = getSettingsIndicatorTypes();
|
||||
|
||||
function buildDefaultsMap(
|
||||
getParams: IndicatorMainDefaultsPanelProps['getParams'],
|
||||
getVisualConfig: IndicatorMainDefaultsPanelProps['getVisualConfig'],
|
||||
): Record<string, IndicatorConfig> {
|
||||
const next: Record<string, IndicatorConfig> = {};
|
||||
for (const type of ALL_TYPES) {
|
||||
const cfg = buildMainIndicatorConfig(type, [], getParams, getVisualConfig);
|
||||
if (cfg) next[type] = cfg;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
function fingerprintMap(map: Record<string, IndicatorConfig>): string {
|
||||
return ALL_TYPES.map(t => map[t] ? indicatorDefaultsFingerprint(map[t]) : '').join('|');
|
||||
}
|
||||
|
||||
const IndicatorMainDefaultsPanel: React.FC<IndicatorMainDefaultsPanelProps> = ({
|
||||
chartIndicators,
|
||||
onAddToChart,
|
||||
onRemoveFromChart,
|
||||
getParams,
|
||||
saveParams,
|
||||
getVisualConfig,
|
||||
saveVisual,
|
||||
saveAllDefaults,
|
||||
onSaveUiState,
|
||||
settingsRevision = 0,
|
||||
}) => {
|
||||
const onChartTypes = useMemo(() => indicatorTypesOnChart(chartIndicators), [chartIndicators]);
|
||||
const onChartTypes = useMemo(() => new Set(chartIndicators.map(i => i.type)), [chartIndicators]);
|
||||
const [expandedTypes, setExpandedTypes] = useState<Set<string>>(() => new Set());
|
||||
const [configs, setConfigs] = useState<Record<string, IndicatorConfig>>({});
|
||||
const [baselineFp, setBaselineFp] = useState('');
|
||||
const [search, setSearch] = useState('');
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saveMessage, setSaveMessage] = useState<string | null>(null);
|
||||
|
||||
const reloadFromDb = useCallback(() => {
|
||||
const next = buildDefaultsMap(getParams, getVisualConfig);
|
||||
setConfigs(next);
|
||||
setBaselineFp(fingerprintMap(next));
|
||||
setSaveMessage(null);
|
||||
}, [getParams, getVisualConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
reloadFromDb();
|
||||
}, [reloadFromDb, settingsRevision]);
|
||||
|
||||
const dirty = useMemo(
|
||||
() => baselineFp !== '' && fingerprintMap(configs) !== baselineFp,
|
||||
[configs, baselineFp],
|
||||
);
|
||||
|
||||
const filtered = useMemo(
|
||||
() => filterSettingsIndicatorTypes(ALL_TYPES, search),
|
||||
@@ -71,15 +111,6 @@ const IndicatorMainDefaultsPanel: React.FC<IndicatorMainDefaultsPanelProps> = ({
|
||||
[filtered],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const next: Record<string, IndicatorConfig> = {};
|
||||
for (const type of ALL_TYPES) {
|
||||
const cfg = buildMainIndicatorConfig(type, chartIndicators, getParams, getVisualConfig);
|
||||
if (cfg) next[type] = cfg;
|
||||
}
|
||||
setConfigs(next);
|
||||
}, [chartIndicators, getParams, getVisualConfig]);
|
||||
|
||||
const toggleExpand = useCallback((type: string) => {
|
||||
setExpandedTypes(prev => {
|
||||
const next = new Set(prev);
|
||||
@@ -91,6 +122,7 @@ const IndicatorMainDefaultsPanel: React.FC<IndicatorMainDefaultsPanelProps> = ({
|
||||
|
||||
const handleConfigChange = useCallback((type: string, updated: IndicatorConfig) => {
|
||||
setConfigs(prev => ({ ...prev, [type]: updated }));
|
||||
setSaveMessage(null);
|
||||
}, []);
|
||||
|
||||
const handleToggleChart = useCallback((type: string, enabled: boolean) => {
|
||||
@@ -105,22 +137,52 @@ const IndicatorMainDefaultsPanel: React.FC<IndicatorMainDefaultsPanelProps> = ({
|
||||
const base = configs[type];
|
||||
if (!base) return;
|
||||
const reset = resetConfigToDefaults(base);
|
||||
persistDefaults(reset, saveParams, saveVisual);
|
||||
handleConfigChange(type, reset);
|
||||
}, [configs, saveParams, saveVisual, handleConfigChange]);
|
||||
}, [configs, handleConfigChange]);
|
||||
|
||||
const handleResetAll = useCallback(() => {
|
||||
if (!window.confirm('목록에 있는 모든 보조지표 기본값을 초기화할까요?')) return;
|
||||
if (!window.confirm('목록에 있는 모든 보조지표 기본값을 초기화할까요? (저장 버튼을 눌러야 DB에 반영됩니다)')) return;
|
||||
const next: Record<string, IndicatorConfig> = {};
|
||||
for (const type of ALL_TYPES) {
|
||||
const base = configs[type] ?? buildMainIndicatorConfig(type, [], getParams, getVisualConfig);
|
||||
if (!base) continue;
|
||||
const reset = resetConfigToDefaults(base);
|
||||
next[type] = reset;
|
||||
persistDefaults(reset, saveParams, saveVisual);
|
||||
next[type] = resetConfigToDefaults(base);
|
||||
}
|
||||
setConfigs(next);
|
||||
}, [configs, getParams, getVisualConfig, saveParams, saveVisual]);
|
||||
setSaveMessage(null);
|
||||
}, [configs, getParams, getVisualConfig]);
|
||||
|
||||
const handleSaveAll = useCallback(async () => {
|
||||
if (!saveAllDefaults) {
|
||||
setSaveMessage('저장 API를 사용할 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
if (!dirty) return;
|
||||
setSaving(true);
|
||||
setSaveMessage(null);
|
||||
try {
|
||||
await saveAllDefaults(configs);
|
||||
const fp = fingerprintMap(configs);
|
||||
setBaselineFp(fp);
|
||||
setSaveMessage('저장되었습니다.');
|
||||
} catch (err) {
|
||||
console.error('[IndicatorMainDefaultsPanel] save failed', err);
|
||||
setSaveMessage('저장에 실패했습니다. 다시 시도해 주세요.');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}, [saveAllDefaults, configs, dirty]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onSaveUiState) return;
|
||||
onSaveUiState({
|
||||
save: handleSaveAll,
|
||||
dirty,
|
||||
saving,
|
||||
saveMessage,
|
||||
});
|
||||
return () => onSaveUiState(null);
|
||||
}, [onSaveUiState, handleSaveAll, dirty, saving, saveMessage]);
|
||||
|
||||
const renderRows = (types: string[]) => types.map(type => {
|
||||
const cfg = configs[type];
|
||||
@@ -135,10 +197,7 @@ const IndicatorMainDefaultsPanel: React.FC<IndicatorMainDefaultsPanelProps> = ({
|
||||
variant="settings"
|
||||
onToggleExpand={() => toggleExpand(type)}
|
||||
onToggleEnabled={on => handleToggleChart(type, on)}
|
||||
onChange={updated => {
|
||||
persistDefaults(updated, saveParams, saveVisual);
|
||||
handleConfigChange(type, updated);
|
||||
}}
|
||||
onChange={updated => handleConfigChange(type, updated)}
|
||||
onRowDefaults={() => handleRowDefaults(type)}
|
||||
/>
|
||||
);
|
||||
@@ -149,7 +208,7 @@ const IndicatorMainDefaultsPanel: React.FC<IndicatorMainDefaultsPanelProps> = ({
|
||||
<div className="stg-ind-intro">
|
||||
<p>
|
||||
지표 추가 팝업 <strong>Main</strong> 탭 {MAIN_INDICATOR_TYPES.length}종을 상단에 두었고, 그 아래에 등록된 모든 보조지표가 있습니다.
|
||||
우측 스위치로 차트에 바로 추가·제거할 수 있으며, 파라미터·색상 변경은 DB 기본값에 자동 저장됩니다.
|
||||
우측 스위치로 차트에 바로 추가·제거할 수 있습니다. 파라미터·색상을 변경한 뒤 <strong>상단 저장</strong> 버튼을 눌러 DB에 반영하세요.
|
||||
</p>
|
||||
<button type="button" className="stg-ind-reset-all" onClick={handleResetAll}>
|
||||
전체 기본값 복원
|
||||
|
||||
Reference in New Issue
Block a user