/** * Activity Store — visionA Cloud(Dashboard 時間軸用) * * 對齊: * - `.autoflow/03-design/components.md` §4 Dashboard `ActivityTimeline` * - `.autoflow/03-design/flows/flow-offline-handling.md` §9 Activity Timeline 擴充 * * 職責: * - 保存最近的活動事件(配對、裝置上下線、燒錄、模型上傳等) * - 容量上限 100 筆(超過從頭 drop) * * F6 範圍(雛形): * - 事件來源尚未接(F7/F8 會從 WS `/ws/devices/events` 推入 + login / upload 事件 seed) * - 這個 store 先建立,讓 Dashboard 能 render empty state;真實事件由 F7+ 補 */ "use client"; import { create } from "zustand"; /** 活動類型(對齊 flow-offline-handling.md §9 雲端版 + local-tool 既有類型) */ export type ActivityType = | "device_paired" | "device_unpaired" | "device_online" | "device_offline" | "tunnel_reconnected" | "flash_start" | "flash_complete" | "flash_error" | "model_upload" | "model_delete" | "cluster_degraded"; export interface ActivityEntry { id: string; type: ActivityType; message: string; /** Unix ms(用 `Date.now()`;方便前端相對時間格式化) */ timestamp: number; /** 可選關聯 */ deviceId?: string; modelId?: string; } const ACTIVITY_LIMIT = 100; interface ActivityState { activities: ActivityEntry[]; addActivity: (entry: Omit & { id?: string; timestamp?: number; }) => void; clear: () => void; } export const useActivityStore = create()((set) => ({ activities: [], addActivity: (entry) => set((state) => { const id = entry.id ?? `act_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; const timestamp = entry.timestamp ?? Date.now(); const next: ActivityEntry = { id, type: entry.type, message: entry.message, timestamp, deviceId: entry.deviceId, modelId: entry.modelId, }; // 新事件放最前面,超過上限從尾部 drop const combined = [next, ...state.activities].slice(0, ACTIVITY_LIMIT); return { activities: combined }; }), clear: () => set({ activities: [] }), }));