113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
|
import { create } from "zustand";
|
||
|
import { devtools } from "zustand/middleware";
|
||
|
import type {
|
||
|
AppState,
|
||
|
InvestProduct,
|
||
|
AIAnalysis,
|
||
|
NewsArticle,
|
||
|
SimulationResult,
|
||
|
} from "../types";
|
||
|
|
||
|
interface AppStore extends AppState {
|
||
|
// Actions
|
||
|
setProducts: (products: InvestProduct[]) => void;
|
||
|
setSelectedProduct: (product: InvestProduct | null) => void;
|
||
|
setAnalyses: (investCode: string, analyses: AIAnalysis[]) => void;
|
||
|
setSelectedAnalysis: (analysis: AIAnalysis | null) => void;
|
||
|
setNews: (news: NewsArticle[]) => void;
|
||
|
setCurrentView: (
|
||
|
view: "dashboard" | "detail" | "simulation" | "settings"
|
||
|
) => void;
|
||
|
setTheme: (theme: "dark" | "light") => void;
|
||
|
setLoading: (loading: boolean) => void;
|
||
|
setSimulationResult: (result: SimulationResult | null) => void;
|
||
|
setInvestmentAmount: (amount: number) => void;
|
||
|
|
||
|
// Computed values
|
||
|
getProductByCode: (code: string) => InvestProduct | undefined;
|
||
|
getAnalysesByProduct: (code: string) => AIAnalysis[];
|
||
|
getLatestAnalysis: (code: string) => AIAnalysis | undefined;
|
||
|
}
|
||
|
|
||
|
export const useAppStore = create<AppStore>()(
|
||
|
devtools(
|
||
|
(set, get) => ({
|
||
|
// Initial state
|
||
|
products: [],
|
||
|
selectedProduct: null,
|
||
|
analyses: {},
|
||
|
selectedAnalysis: null,
|
||
|
news: [],
|
||
|
isLoading: false,
|
||
|
currentView: "dashboard",
|
||
|
theme: "dark",
|
||
|
simulationResult: null,
|
||
|
investmentAmount: 10000000, // 기본값 1천만원
|
||
|
|
||
|
// Actions
|
||
|
setProducts: (products) => set({ products }),
|
||
|
|
||
|
setSelectedProduct: (product) => set({ selectedProduct: product }),
|
||
|
|
||
|
setAnalyses: (investCode, analyses) =>
|
||
|
set((state) => ({
|
||
|
analyses: {
|
||
|
...state.analyses,
|
||
|
[investCode]: analyses,
|
||
|
},
|
||
|
})),
|
||
|
|
||
|
setSelectedAnalysis: (analysis) => set({ selectedAnalysis: analysis }),
|
||
|
|
||
|
setNews: (news) => set({ news }),
|
||
|
|
||
|
setCurrentView: (view) => set({ currentView: view }),
|
||
|
|
||
|
setTheme: (theme) => {
|
||
|
// 로컬 스토리지에 테마 저장
|
||
|
localStorage.setItem("theme", theme);
|
||
|
set({ theme });
|
||
|
},
|
||
|
|
||
|
setLoading: (loading) => set({ isLoading: loading }),
|
||
|
|
||
|
setSimulationResult: (result) => set({ simulationResult: result }),
|
||
|
|
||
|
setInvestmentAmount: (amount) => set({ investmentAmount: amount }),
|
||
|
|
||
|
// Computed values
|
||
|
getProductByCode: (code) => {
|
||
|
const { products } = get();
|
||
|
return products.find((product) => product.code === code);
|
||
|
},
|
||
|
|
||
|
getAnalysesByProduct: (code) => {
|
||
|
const { analyses } = get();
|
||
|
return analyses[code] || [];
|
||
|
},
|
||
|
|
||
|
getLatestAnalysis: (code) => {
|
||
|
const { analyses } = get();
|
||
|
const productAnalyses = analyses[code] || [];
|
||
|
if (productAnalyses.length === 0) return undefined;
|
||
|
|
||
|
// 최신 분석 결과 반환 (분석 날짜 기준)
|
||
|
return productAnalyses.sort(
|
||
|
(a, b) =>
|
||
|
new Date(b.analysisDate).getTime() -
|
||
|
new Date(a.analysisDate).getTime()
|
||
|
)[0];
|
||
|
},
|
||
|
}),
|
||
|
{
|
||
|
name: "app-store", // devtools에서 표시될 이름
|
||
|
}
|
||
|
)
|
||
|
);
|
||
|
|
||
|
// 테마 초기화 (로컬 스토리지에서 불러오기)
|
||
|
const savedTheme = localStorage.getItem("theme") as "dark" | "light" | null;
|
||
|
if (savedTheme) {
|
||
|
useAppStore.getState().setTheme(savedTheme);
|
||
|
}
|