위젯 기능 추가
This commit is contained in:
@@ -12,7 +12,7 @@ export type SettingsCategoryId =
|
||||
| 'trading' | 'paper' | 'virtual' | 'live' | 'trend-search' | 'alert' | 'network' | 'admin';
|
||||
|
||||
export const TOP_MENU_IDS: TopMenuId[] = [
|
||||
'dashboard', 'chart', 'paper', 'virtual', 'trend-search', 'strategy-editor', 'strategy-evaluation', 'backtest', 'analysis-report', 'notifications', 'settings', 'verification-board',
|
||||
'dashboard', 'widget-dashboard', 'chart', 'paper', 'virtual', 'trend-search', 'strategy-editor', 'strategy-evaluation', 'backtest', 'analysis-report', 'notifications', 'settings', 'verification-board',
|
||||
];
|
||||
|
||||
export const SETTINGS_MENU_IDS: SettingsCategoryId[] = [
|
||||
@@ -21,7 +21,7 @@ export const SETTINGS_MENU_IDS: SettingsCategoryId[] = [
|
||||
];
|
||||
|
||||
export const ALL_MENU_IDS = [
|
||||
'dashboard', 'chart', 'paper', 'virtual', 'trend-search', 'verification-board', 'strategy', 'strategy-editor', 'strategy-evaluation', 'backtest', 'analysis-report', 'notifications', 'settings',
|
||||
'dashboard', 'widget-dashboard', 'chart', 'paper', 'virtual', 'trend-search', 'verification-board', 'strategy', 'strategy-editor', 'strategy-evaluation', 'backtest', 'analysis-report', 'notifications', 'settings',
|
||||
...SETTINGS_MENU_IDS.map(id => `settings_${id}` as const),
|
||||
] as const;
|
||||
|
||||
@@ -29,6 +29,7 @@ export type MenuPermissionId = typeof ALL_MENU_IDS[number];
|
||||
|
||||
export const MENU_LABELS: Record<string, string> = {
|
||||
dashboard: '대시보드',
|
||||
'widget-dashboard': '위젯',
|
||||
chart: '실시간차트',
|
||||
paper: '투자관리',
|
||||
virtual: '가상매매',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import {
|
||||
getAppSettingsCache,
|
||||
isAppSettingsCacheLoaded,
|
||||
patchAppSettingsCache,
|
||||
subscribeAppSettings,
|
||||
} from '../hooks/useAppSettings';
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
UI_PREFERENCES_VERSION,
|
||||
type UiPreferences,
|
||||
} from '../types/uiPreferences';
|
||||
import { normalizeWidgetLayout } from '../types/widgetDashboard';
|
||||
|
||||
const SAVE_DEBOUNCE_MS = 400;
|
||||
let saveTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
@@ -51,6 +53,9 @@ export function resolveUiPreferences(raw: unknown): UiPreferences {
|
||||
panels: { ...EMPTY_UI_PREFERENCES.panels, ...o.panels },
|
||||
virtual: { ...EMPTY_UI_PREFERENCES.virtual, ...o.virtual },
|
||||
tradeNotifications: { ...EMPTY_UI_PREFERENCES.tradeNotifications, ...o.tradeNotifications },
|
||||
widgetDashboard: o.widgetDashboard
|
||||
? normalizeWidgetLayout(o.widgetDashboard)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,6 +66,13 @@ export function getUiPreferences(): UiPreferences {
|
||||
|
||||
/** UI 설정 부분 갱신 — 낙관적 캐시 + 디바운스 DB 저장 */
|
||||
export function patchUiPreferences(patch: Partial<UiPreferences>, immediate = false): void {
|
||||
if (!isAppSettingsCacheLoaded()) {
|
||||
pendingPatch = pendingPatch
|
||||
? (deepMerge(pendingPatch as Record<string, unknown>, patch as Record<string, unknown>) as Partial<UiPreferences>)
|
||||
: patch;
|
||||
return;
|
||||
}
|
||||
|
||||
const current = getUiPreferences();
|
||||
const next = deepMerge(current as Record<string, unknown>, patch as Record<string, unknown>) as UiPreferences;
|
||||
next.v = UI_PREFERENCES_VERSION;
|
||||
@@ -73,6 +85,7 @@ export function patchUiPreferences(patch: Partial<UiPreferences>, immediate = fa
|
||||
|
||||
const flush = () => {
|
||||
saveTimer = null;
|
||||
if (!isAppSettingsCacheLoaded()) return;
|
||||
const toSave = pendingPatch
|
||||
? resolveUiPreferences(deepMerge(getUiPreferences() as Record<string, unknown>, pendingPatch as Record<string, unknown>))
|
||||
: next;
|
||||
@@ -95,10 +108,36 @@ export function patchUiPreferences(patch: Partial<UiPreferences>, immediate = fa
|
||||
saveTimer = setTimeout(flush, SAVE_DEBOUNCE_MS);
|
||||
}
|
||||
|
||||
/** 디바운스 대기 중인 UI 설정 즉시 DB 저장 (페이지 이탈 시) */
|
||||
export function flushPendingUiPreferences(): void {
|
||||
if (saveTimer != null) {
|
||||
clearTimeout(saveTimer);
|
||||
saveTimer = null;
|
||||
}
|
||||
if (!isAppSettingsCacheLoaded() || !pendingPatch) return;
|
||||
const toSave = resolveUiPreferences(
|
||||
deepMerge(getUiPreferences() as Record<string, unknown>, pendingPatch as Record<string, unknown>),
|
||||
);
|
||||
pendingPatch = null;
|
||||
void saveAppSettings({ uiPreferences: toSave }).catch(err => {
|
||||
console.warn('[uiPreferences] DB flush 저장 실패:', err);
|
||||
});
|
||||
}
|
||||
|
||||
export function subscribeUiPreferences(listener: () => void): () => void {
|
||||
return subscribeAppSettings(listener);
|
||||
}
|
||||
|
||||
/** 앱 설정 DB 로드 직후 — 로드 전에 큐잉된 patch 병합 */
|
||||
export function reconcilePendingUiPreferencesOnLoad(): void {
|
||||
if (!isAppSettingsCacheLoaded() || !pendingPatch) return;
|
||||
const merged = resolveUiPreferences(
|
||||
deepMerge(getUiPreferences() as Record<string, unknown>, pendingPatch as Record<string, unknown>),
|
||||
);
|
||||
patchAppSettingsCache({ uiPreferences: merged });
|
||||
pendingPatch = null;
|
||||
}
|
||||
|
||||
/** 중첩 경로 헬퍼 */
|
||||
export function getUiPref<K extends keyof UiPreferences>(key: K): UiPreferences[K] {
|
||||
return getUiPreferences()[key];
|
||||
|
||||
Reference in New Issue
Block a user