"use client"; import { create } from "zustand"; import type { Workspace } from "@/shared/types"; import { toast } from "sonner"; import { api } from "@/shared/api"; import { createLogger } from "@/shared/logger"; const logger = createLogger("workspace-store"); interface WorkspaceState { workspace: Workspace | null; workspaces: Workspace[]; } interface WorkspaceActions { hydrateWorkspace: ( wsList: Workspace[], preferredWorkspaceId?: string | null, ) => Workspace | null; switchWorkspace: (workspaceId: string) => void; refreshWorkspaces: () => Promise; updateWorkspace: (ws: Workspace) => void; createWorkspace: (data: { name: string; slug: string; description?: string; }) => Promise; leaveWorkspace: (workspaceId: string) => Promise; deleteWorkspace: (workspaceId: string) => Promise; clearWorkspace: () => void; } type WorkspaceStore = WorkspaceState & WorkspaceActions; export const useWorkspaceStore = create((set, get) => ({ // State workspace: null, workspaces: [], // Actions hydrateWorkspace: (wsList, preferredWorkspaceId) => { set({ workspaces: wsList }); const nextWorkspace = (preferredWorkspaceId ? wsList.find((item) => item.id === preferredWorkspaceId) : null) ?? wsList[0] ?? null; if (!nextWorkspace) { api.setWorkspaceId(null); localStorage.removeItem("multica_workspace_id"); set({ workspace: null }); return null; } api.setWorkspaceId(nextWorkspace.id); localStorage.setItem("multica_workspace_id", nextWorkspace.id); set({ workspace: nextWorkspace }); logger.debug("hydrate workspace", nextWorkspace.name, nextWorkspace.id); // Members, agents, skills, issues, inbox are all managed by TanStack Query. // They auto-fetch when components mount with the workspace ID in their query key. return nextWorkspace; }, switchWorkspace: (workspaceId) => { logger.info("switching to", workspaceId); const { workspaces, hydrateWorkspace } = get(); const ws = workspaces.find((item) => item.id === workspaceId); if (!ws) return; api.setWorkspaceId(ws.id); localStorage.setItem("multica_workspace_id", ws.id); // All data caches (issues, inbox, members, agents, skills, runtimes) // are managed by TanStack Query, keyed by wsId — auto-refetch on switch. set({ workspace: ws }); hydrateWorkspace(workspaces, ws.id); }, refreshWorkspaces: async () => { const { workspace, hydrateWorkspace } = get(); const storedWorkspaceId = localStorage.getItem("multica_workspace_id"); try { const wsList = await api.listWorkspaces(); hydrateWorkspace(wsList, workspace?.id ?? storedWorkspaceId); return wsList; } catch (e) { logger.error("failed to refresh workspaces", e); toast.error("Failed to refresh workspaces"); return get().workspaces; } }, updateWorkspace: (ws) => { set((state) => ({ workspace: state.workspace?.id === ws.id ? ws : state.workspace, workspaces: state.workspaces.map((item) => item.id === ws.id ? ws : item, ), })); }, createWorkspace: async (data) => { const ws = await api.createWorkspace(data); set((state) => ({ workspaces: [...state.workspaces, ws] })); return ws; }, leaveWorkspace: async (workspaceId) => { await api.leaveWorkspace(workspaceId); const { workspace, hydrateWorkspace } = get(); const wsList = await api.listWorkspaces(); const preferredWorkspaceId = workspace?.id === workspaceId ? null : (workspace?.id ?? null); hydrateWorkspace(wsList, preferredWorkspaceId); }, deleteWorkspace: async (workspaceId) => { await api.deleteWorkspace(workspaceId); const { workspace, hydrateWorkspace } = get(); const wsList = await api.listWorkspaces(); const preferredWorkspaceId = workspace?.id === workspaceId ? null : (workspace?.id ?? null); hydrateWorkspace(wsList, preferredWorkspaceId); }, clearWorkspace: () => { api.setWorkspaceId(null); set({ workspace: null, workspaces: [] }); }, }));