/** * Document Store Module * * Zustand store for managing document state and operations. * Handles: * - Current document state (currentDocument) * - Document list with pagination * - Save status tracking * - CRUD operations for documents * * @module stores/documentStore */ import { create } from 'zustand'; import type { DocumentStoreState } from '../types/store'; import type { Document, DocumentListFilters, CreateDocumentRequest } from '../types/document'; import type { ListFilters } from '../types/api'; import * as documentService from '../services/documentService'; /** * Initial pagination state */ const initialPagination = { page: 1, pageSize: 20, total: 0, totalPages: 0, }; /** * Document store * * Manages document state and provides actions for document operations. * All async actions automatically handle errors and update the store state. * * @example * ```typescript * // Create a new document * await useDocumentStore.getState().createDocument({ * title: 'My Document', * content: '# Hello World', * format: 'markdown', * source: 'chat', * }); * * // Update current document content (local only) * useDocumentStore.getState().updateContent('# Updated content'); * * // Fetch document list * await useDocumentStore.getState().fetchDocumentList({ * source: 'chat', * sortBy: 'createdAt', * sortOrder: 'desc', * }); * ``` */ export const useDocumentStore = create((set, get) => ({ // ============ State ============ currentDocument: null, documents: [], pagination: initialPagination, saveStatus: 'saved', // ============ Actions ============ /** * Create a new document * * Creates a document via the API and sets it as the current document. * Save status is set to 'saved' after successful creation. * * @param req - Document creation request (userId, fileUrl, sessionId) * @throws {Error} When document creation fails */ createDocument: async (req: CreateDocumentRequest) => { try { set({ saveStatus: 'saving' }); // Call API to create document const response = await documentService.createDocument(req); // Fetch the full document to set as current const fullDocument = await documentService.getDocument(response.documentId); // Update state with new document set({ currentDocument: fullDocument, saveStatus: 'saved', }); // Refresh document list to include the new document await get().fetchDocumentList(); } catch (error) { set({ saveStatus: 'error' }); throw error; } }, /** * Fetch a document by ID * * Retrieves a document from the API and sets it as the current document. * Optionally includes blocks array. * * @param id - Document ID to fetch * @param includeBlocks - Whether to include blocks in response * @throws {Error} When document fetch fails */ fetchDocument: async (id: string, includeBlocks: boolean = false) => { try { const document = await documentService.getDocument(id, { includeBlocks }); set({ currentDocument: document, saveStatus: 'saved', }); } catch (error) { set({ currentDocument: null }); throw error; } }, /** * Fetch document list with optional filters * * Retrieves a paginated list of documents from the API. * Updates the documents array and pagination info. * * @param filters - Optional filters for listing (source, sortBy, sortOrder, page, pageSize) * @throws {Error} When document list fetch fails */ fetchDocumentList: async (filters?: ListFilters) => { try { // 转换前端的camelCase字段名为后端的snake_case const apiFilters: DocumentListFilters = { userId: 'default-user', // TODO: 从认证上下文获取 page: filters?.page, pageSize: filters?.pageSize, sortBy: filters?.sortBy === 'createdAt' ? 'created_at' : filters?.sortBy === 'updatedAt' ? 'updated_at' : undefined, sortOrder: filters?.sortOrder, }; const response = await documentService.listDocuments(apiFilters); set({ documents: response.documents, pagination: response.pagination, }); } catch (error) { set({ documents: [], pagination: initialPagination }); throw error; } }, /** * Delete documents by session ID * * Deletes all documents for a session via the API. * If any deleted document is the current document, clears the current document state. * * @param sessionId - Session ID whose documents to delete * @throws {Error} When document deletion fails */ deleteDocumentsBySession: async (sessionId: string) => { await documentService.deleteDocuments(sessionId); // Clear current document if it belonged to this session const currentDoc = get().currentDocument; if (currentDoc && currentDoc.sessionId === sessionId) { set({ currentDocument: null, saveStatus: 'saved' }); } // Refresh document list to reflect deletion await get().fetchDocumentList(); }, /** * Set current document * * Directly sets the current document in state (local only). * Used when document is already loaded. * * @param document - Document to set as current */ setCurrentDocument: (document: Document | null) => { set({ currentDocument: document, saveStatus: 'saved', }); }, /** * Set save status * * Manually updates the save status indicator. * Used by external components like auto-save hooks. * * @param status - New save status */ setSaveStatus: (status) => { set({ saveStatus: status }); }, })); export default useDocumentStore;