import { useState, useEffect, useCallback } from 'react'; import axios from 'axios'; // Define Document interface export interface Document { _id: string; title: string; description: string; filePath: string; originalFilename: string; fileType: string; fileSize: number; documentType: 'meeting_minutes' | 'board_meeting_minutes' | 'committee_meeting_minutes' | 'annual_meeting_minutes' | 'bylaws' | 'financial_report' | 'annual_report' | 'board_report' | 'committee_report' | 'meeting_agenda' | 'board_meeting_agenda' | 'committee_meeting_agenda' | 'program_document'; isPublic: boolean; meetingDate?: Date; uploadDate: Date; lastModified: Date; accessibilityChecked: boolean; hasTextVersion: boolean; textVersionPath?: string; createdAt: string; updatedAt: string; } // Define filter interface export interface DocumentFilter { documentType?: string; search?: string; isPublic?: boolean; page?: number; limit?: number; } // Define pagination interface export interface Pagination { total: number; page: number; limit: number; pages: number; } // Define hook return interface export interface UseDocumentsReturn { documents: Document[]; loading: boolean; error: Error | null; pagination: Pagination | null; fetchDocuments: (filters?: DocumentFilter) => Promise; getDocumentById: (id: string) => Promise; createDocument: (formData: FormData) => Promise; updateDocument: (id: string, documentData: Partial) => Promise; deleteDocument: (id: string) => Promise; uploadTextVersion: (id: string, formData: FormData) => Promise; updatePublicStatus: (id: string, isPublic: boolean) => Promise; markAccessibilityChecked: (id: string, checked: boolean) => Promise; } // API base URL const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000/api'; /** * Hook for managing documents data */ export function useDocuments(): UseDocumentsReturn { const [documents, setDocuments] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [pagination, setPagination] = useState(null); /** * Fetch documents with optional filtering */ const fetchDocuments = useCallback(async (filters?: DocumentFilter) => { setLoading(true); setError(null); try { // Build query string from filters const queryParams = new URLSearchParams(); if (filters) { Object.entries(filters).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== '') { queryParams.append(key, String(value)); } }); } const response = await axios.get(`${API_URL}/documents?${queryParams.toString()}`); setDocuments(response.data.documents); setPagination(response.data.pagination); } catch (err) { console.error('Error fetching documents:', err); setError(err instanceof Error ? err : new Error('Failed to fetch documents')); } finally { setLoading(false); } }, []); /** * Get a single document by ID */ const getDocumentById = useCallback(async (id: string): Promise => { try { const response = await axios.get(`${API_URL}/documents/${id}`); return response.data.document; } catch (err) { console.error(`Error fetching document with ID ${id}:`, err); return null; } }, []); /** * Create a new document */ const createDocument = useCallback(async (formData: FormData): Promise => { try { const response = await axios.post(`${API_URL}/documents`, formData, { headers: { 'Content-Type': 'multipart/form-data', // Add authorization header if needed // 'Authorization': `Bearer ${token}` } }); // Refresh documents list after creating fetchDocuments(); return response.data.document; } catch (err) { console.error('Error creating document:', err); // Rethrow the error so it can be caught and handled by the component throw err; } }, [fetchDocuments]); /** * Update an existing document */ const updateDocument = useCallback(async (id: string, documentData: Partial): Promise => { try { const response = await axios.put(`${API_URL}/documents/${id}`, documentData, { headers: { 'Content-Type': 'application/json', // Add authorization header if needed // 'Authorization': `Bearer ${token}` } }); // Update local state setDocuments(prevDocuments => prevDocuments.map(document => document._id === id ? { ...document, ...response.data.document } : document ) ); return response.data.document; } catch (err) { console.error(`Error updating document with ID ${id}:`, err); // Rethrow the error so it can be caught and handled by the component throw err; } }, []); /** * Delete a document */ const deleteDocument = useCallback(async (id: string): Promise => { try { await axios.delete(`${API_URL}/documents/${id}`, { headers: { // Add authorization header if needed // 'Authorization': `Bearer ${token}` } }); // Update local state setDocuments(prevDocuments => prevDocuments.filter(document => document._id !== id)); return true; } catch (err) { console.error(`Error deleting document with ID ${id}:`, err); return false; } }, []); /** * Upload text version for a document */ const uploadTextVersion = useCallback(async (id: string, formData: FormData): Promise => { try { const response = await axios.post( `${API_URL}/documents/${id}/textversion`, formData, { headers: { 'Content-Type': 'multipart/form-data', // Add authorization header if needed // 'Authorization': `Bearer ${token}` } } ); // Update local state setDocuments(prevDocuments => prevDocuments.map(document => document._id === id ? { ...document, ...response.data.document } : document ) ); return response.data.document; } catch (err) { console.error(`Error uploading text version for document with ID ${id}:`, err); return null; } }, []); /** * Update document public status */ const updatePublicStatus = useCallback(async (id: string, isPublic: boolean): Promise => { try { const response = await axios.put( `${API_URL}/documents/${id}/public`, { isPublic }, { headers: { 'Content-Type': 'application/json', // Add authorization header if needed // 'Authorization': `Bearer ${token}` } } ); // Update local state setDocuments(prevDocuments => prevDocuments.map(document => document._id === id ? { ...document, ...response.data.document } : document ) ); return response.data.document; } catch (err) { console.error(`Error updating public status for document with ID ${id}:`, err); return null; } }, []); /** * Mark document as accessibility checked */ const markAccessibilityChecked = useCallback(async (id: string, accessibilityChecked: boolean): Promise => { try { const response = await axios.put( `${API_URL}/documents/${id}/accessibility`, { accessibilityChecked }, { headers: { 'Content-Type': 'application/json', // Add authorization header if needed // 'Authorization': `Bearer ${token}` } } ); // Update local state setDocuments(prevDocuments => prevDocuments.map(document => document._id === id ? { ...document, ...response.data.document } : document ) ); return response.data.document; } catch (err) { console.error(`Error marking document with ID ${id} as accessibility checked:`, err); return null; } }, []); // Load documents on initial render useEffect(() => { fetchDocuments(); }, [fetchDocuments]); return { documents, loading, error, pagination, fetchDocuments, getDocumentById, createDocument, updateDocument, deleteDocument, uploadTextVersion, updatePublicStatus, markAccessibilityChecked }; }