Files
goldenChart/frontend/src/utils/strategyTemplateStorage.ts
T

98 lines
3.3 KiB
TypeScript

/** 전략편집기 — 사용자 저장 템플릿 (DB ui_preferences) */
import type { LogicNode } from './strategyTypes';
import type { EditorConditionState } from './strategyConditionSerde';
import { getUiPreferences, patchUiPreferences } from './uiPreferencesDb';
export type UserStrategyTemplate = {
id: string;
label: string;
description?: string;
signal: 'buy' | 'sell';
editorState: EditorConditionState;
orphans: LogicNode[];
createdAt: number;
};
function cloneJson<T>(value: T): T {
return JSON.parse(JSON.stringify(value)) as T;
}
export function createUserTemplateId(): string {
return `tmpl-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
}
function parseStored(raw: unknown): UserStrategyTemplate[] {
if (!Array.isArray(raw)) return [];
return raw.filter(
(t): t is UserStrategyTemplate =>
t != null
&& typeof t === 'object'
&& typeof (t as UserStrategyTemplate).id === 'string'
&& typeof (t as UserStrategyTemplate).label === 'string'
&& ((t as UserStrategyTemplate).signal === 'buy' || (t as UserStrategyTemplate).signal === 'sell')
&& typeof (t as UserStrategyTemplate).editorState === 'object'
&& Array.isArray((t as UserStrategyTemplate).orphans),
);
}
export function loadUserStrategyTemplates(): UserStrategyTemplate[] {
const raw = getUiPreferences().strategyEditor?.userTemplates;
return parseStored(raw);
}
export function saveUserStrategyTemplates(templates: UserStrategyTemplate[]): void {
patchUiPreferences({ strategyEditor: { userTemplates: templates } });
}
export function addUserStrategyTemplate(
input: Omit<UserStrategyTemplate, 'id' | 'createdAt'>,
): UserStrategyTemplate {
const next: UserStrategyTemplate = {
...input,
id: createUserTemplateId(),
editorState: cloneJson(input.editorState),
orphans: cloneJson(input.orphans),
createdAt: Date.now(),
};
const templates = [...loadUserStrategyTemplates(), next];
saveUserStrategyTemplates(templates);
return next;
}
export function deleteUserStrategyTemplate(id: string): UserStrategyTemplate[] {
const templates = loadUserStrategyTemplates().filter(t => t.id !== id);
saveUserStrategyTemplates(templates);
return templates;
}
export function updateUserStrategyTemplate(
id: string,
patch: Partial<Pick<UserStrategyTemplate, 'label' | 'description' | 'signal' | 'editorState' | 'orphans'>>,
): UserStrategyTemplate[] {
const templates = loadUserStrategyTemplates().map(t => {
if (t.id !== id) return t;
return {
...t,
...patch,
...(patch.editorState != null ? { editorState: cloneJson(patch.editorState) } : null),
...(patch.orphans != null ? { orphans: cloneJson(patch.orphans) } : null),
};
});
saveUserStrategyTemplates(templates);
return templates;
}
export function loadHiddenBuiltinTemplateIds(): string[] {
const raw = getUiPreferences().strategyEditor?.hiddenBuiltinTemplateIds;
if (!Array.isArray(raw)) return [];
return raw.filter((id): id is string => typeof id === 'string' && id.length > 0);
}
export function hideBuiltinTemplate(id: string): string[] {
const hidden = loadHiddenBuiltinTemplateIds();
if (hidden.includes(id)) return hidden;
const next = [...hidden, id];
patchUiPreferences({ strategyEditor: { hiddenBuiltinTemplateIds: next } });
return next;
}