import { defineStore } from 'pinia' import { computed, ref, watch } from 'vue' import { normalizeError } from '@/services/error' import { menuApi, type MenuNode } from '@/services/modules/menu' export interface LayoutMenuItem { title: string path?: string icon?: string navigable?: boolean subItems?: LayoutMenuItem[] } export const useMenuStore = defineStore('menu', () => { const menu = ref([]) const favorite = ref([]) const isRail = ref(false) const error = ref(null) const loading = ref(false) const menuStorageKey = 'sk_playground_menu' const favoriteStorageKey = 'sk_playground_favorite' const isRailStorageKey = 'sk_playground_is_rail' const readNodes = (key: string): MenuNode[] => { if (typeof window === 'undefined') return [] try { const raw = window.localStorage.getItem(key) if (!raw) return [] const parsed = JSON.parse(raw) return Array.isArray(parsed) ? (parsed as MenuNode[]) : [] } catch { return [] } } const readBoolean = (key: string, defaultValue = false): boolean => { if (typeof window === 'undefined') return defaultValue try { const raw = window.localStorage.getItem(key) return raw === null ? defaultValue : raw === 'true' } catch { return defaultValue } } const writeValue = (key: string, value: any) => { if (typeof window === 'undefined') return try { window.localStorage.setItem(key, String(value)) } catch { return } } const writeNodes = (key: string, nodes: MenuNode[]) => { if (typeof window === 'undefined') return try { window.localStorage.setItem(key, JSON.stringify(nodes)) } catch { return } } const removeValue = (key: string) => { if (typeof window === 'undefined') return try { window.localStorage.removeItem(key) } catch { return } } const hydrate = () => { menu.value = readNodes(menuStorageKey) favorite.value = readNodes(favoriteStorageKey) isRail.value = readBoolean(isRailStorageKey) } hydrate() const toLayoutMenuItems = (nodes: MenuNode[]): LayoutMenuItem[] => { const getString = (node: MenuNode, key: string): string | undefined => { const v = node?.[key] return typeof v === 'string' ? v : undefined } const getChildren = (node: MenuNode): MenuNode[] => { return Array.isArray(node?.children) ? (node.children as MenuNode[]) : [] } return nodes .map((mdl) => { const mdlTitle = getString(mdl, 'mdl_name') ?? '' const untItems = getChildren(mdl) .map((unt) => { const untTitle = getString(unt, 'unt_name') ?? '' const fncItems = getChildren(unt) .map((fnc) => { const fncTitle = getString(fnc, 'fnc_name') ?? '' const fncId = getString(fnc, 'fnc_id') return { title: fncTitle, path: fncId ? `/${fncId}` : undefined, } satisfies LayoutMenuItem }) .filter((x) => x.title) return { title: untTitle, navigable: false, subItems: fncItems, } satisfies LayoutMenuItem }) .filter((x) => x.title) return { title: mdlTitle, navigable: false, subItems: untItems, } satisfies LayoutMenuItem }) .filter((x) => x.title) } const toFavoriteLayoutMenuItems = (nodes: MenuNode[]): LayoutMenuItem[] => { const getString = (node: MenuNode, key: string): string | undefined => { const v = node?.[key] return typeof v === 'string' ? v : undefined } const getChildren = (node: MenuNode): MenuNode[] => { return Array.isArray(node?.children) ? (node.children as MenuNode[]) : [] } return nodes .map((unt) => { const untTitle = getString(unt, 'unt_name') ?? '' const fncItems = getChildren(unt) .map((fnc) => { const fncTitle = getString(fnc, 'fnc_name') ?? '' const fncId = getString(fnc, 'fnc_id') return { title: fncTitle, path: fncId ? `/${fncId}` : undefined, } satisfies LayoutMenuItem }) .filter((x) => x.title) return { title: untTitle, navigable: false, subItems: fncItems, } satisfies LayoutMenuItem }) .filter((x) => x.title) } const menuItems = computed(() => toLayoutMenuItems(menu.value)) const favoriteItems = computed(() => toFavoriteLayoutMenuItems(favorite.value)) watch( menu, (val) => { writeNodes(menuStorageKey, val) }, { deep: true } ) watch( favorite, (val) => { writeNodes(favoriteStorageKey, val) }, { deep: true } ) watch(isRail, (val) => { writeValue(isRailStorageKey, val) }) const clear = () => { menu.value = [] favorite.value = [] isRail.value = false error.value = null removeValue(menuStorageKey) removeValue(favoriteStorageKey) removeValue(isRailStorageKey) } const getMenu = async (id: string) => { try { loading.value = true const res = await menuApi.getMenu({ userID: id }) menu.value = Array.isArray(res.data.data) ? (res.data.data as MenuNode[]) : [] } catch (error_) { const normalizedError = normalizeError(error_) if (normalizedError.name !== 'CanceledRequestError') { error.value = normalizedError.message } throw normalizedError } finally { loading.value = false } } const getFavorite = async (id: string) => { try { loading.value = true const res = await menuApi.getFavorite({ userID: id }) favorite.value = Array.isArray(res.data.data) ? (res.data.data as MenuNode[]) : [] } catch (error_) { const normalizedError = normalizeError(error_) if (normalizedError.name !== 'CanceledRequestError') { error.value = normalizedError.message } throw normalizedError } finally { loading.value = false } } return { menu, favorite, isRail, menuItems, favoriteItems, error, loading, hydrate, clear, getMenu, getFavorite, } })