/** * Legal Dashboard API Client * ========================= * * JavaScript client for communicating with the Legal Dashboard backend API. * Handles all HTTP requests and data transformation between frontend and backend. */ class LegalDashboardAPI { constructor(baseUrl = '') { this.baseUrl = baseUrl || window.location.origin; this.apiBase = `${this.baseUrl}/api`; // Request interceptor for common headers this.defaultHeaders = { 'Content-Type': 'application/json', }; } /** * Generic HTTP request handler with error handling */ async request(endpoint, options = {}) { const url = `${this.apiBase}${endpoint}`; const config = { ...options, headers: { ...this.defaultHeaders, ...options.headers } }; try { const response = await fetch(url, config); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.detail || `HTTP ${response.status}: ${response.statusText}`); } const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } return await response.text(); } catch (error) { console.error(`API Request failed: ${endpoint}`, error); throw error; } } /** * Health check - verify backend is running */ async healthCheck() { return this.request('/health'); } // ==================== DASHBOARD API ==================== /** * Get dashboard summary statistics */ async getDashboardSummary() { const response = await this.request('/dashboard/summary'); return response.data; } /** * Get processing trends for charts */ async getProcessingTrends(period = 'weekly') { const response = await this.request(`/dashboard/charts/processing-trends?period=${period}`); return response.data; } /** * Get status distribution for pie chart */ async getStatusDistribution() { const response = await this.request('/dashboard/charts/status-distribution'); return response.data; } /** * Get category distribution for pie chart */ async getCategoryDistribution() { const response = await this.request('/dashboard/charts/category-distribution'); return response.data; } // ==================== DOCUMENTS API ==================== /** * Get paginated list of documents with filtering */ async getDocuments(params = {}) { const searchParams = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { if (value !== null && value !== undefined && value !== '') { searchParams.append(key, value); } }); const endpoint = `/documents/?${searchParams.toString()}`; return this.request(endpoint); } /** * Get single document by ID */ async getDocument(documentId) { return this.request(`/documents/${documentId}`); } /** * Delete document by ID */ async deleteDocument(documentId) { return this.request(`/documents/${documentId}`, { method: 'DELETE' }); } // ==================== OCR API ==================== /** * Upload files for OCR processing */ async uploadFiles(files) { const formData = new FormData(); // Add files to form data files.forEach(file => { formData.append('files', file); }); return this.request('/ocr/upload', { method: 'POST', headers: {}, // Remove Content-Type to let browser set it for FormData body: formData }); } /** * Extract text from PDF immediately (for testing) */ async extractTextImmediate(file) { const formData = new FormData(); formData.append('file', file); return this.request('/ocr/extract', { method: 'POST', headers: {}, body: formData }); } // ==================== SCRAPING API ==================== /** * Scrape content from website */ async scrapeWebsite(scrapingRequest) { return this.request('/scraping/scrape', { method: 'POST', body: JSON.stringify(scrapingRequest) }); } /** * Get scraping history */ async getScrapingHistory(skip = 0, limit = 50) { const response = await this.request(`/scraping/scrape/history?skip=${skip}&limit=${limit}`); return response.data; } // ==================== ANALYTICS API ==================== /** * Calculate similarity between two documents */ async calculateSimilarity(doc1Id, doc2Id) { const response = await this.request(`/analytics/similarity?doc1_id=${doc1Id}&doc2_id=${doc2Id}`); return response.data; } /** * Get quality analysis */ async getQualityAnalysis() { const response = await this.request('/analytics/quality-analysis'); return response.data; } /** * Get performance metrics */ async getPerformanceMetrics() { const response = await this.request('/analytics/performance-metrics'); return response.data; } } // ==================== DATA MODELS ==================== /** * Document model to match backend structure */ class DocumentModel { constructor(backendData) { this.id = backendData.id; this.filename = backendData.filename; this.original_filename = backendData.original_filename; this.file_size = backendData.file_size; this.file_path = backendData.file_path; this.category = backendData.category; this.quality_score = backendData.quality_score || 0; this.confidence_score = backendData.confidence_score || 0; this.status = backendData.status; this.created_at = backendData.created_at; this.processed_at = backendData.processed_at; this.ocr_text = backendData.ocr_text; this.summary = backendData.summary; this.keywords = backendData.keywords || []; this.extracted_entities = backendData.extracted_entities || []; this.processing_time = backendData.processing_time || 0; this.importance_score = backendData.importance_score || 0; this.similarity_scores = backendData.similarity_scores || {}; this.legal_references = backendData.legal_references || []; } /** * Format file size for display */ getFormattedFileSize() { return formatFileSize(this.file_size); } /** * Get creation date formatted for Persian locale */ getFormattedDate() { return formatDate(this.created_at); } /** * Get quality class for styling */ getQualityClass() { if (this.quality_score >= 8.5) return 'quality-excellent'; if (this.quality_score >= 6.5) return 'quality-good'; if (this.quality_score >= 4.5) return 'quality-average'; return 'quality-poor'; } /** * Get status text in Persian */ getStatusText() { const statusMap = { 'processed': 'پردازش شده', 'processing': 'در حال پردازش', 'uploaded': 'آپلود شده', 'pending': 'در انتظار', 'error': 'خطا' }; return statusMap[this.status] || this.status; } /** * Get status icon */ getStatusIcon() { const iconMap = { 'processed': 'check-circle', 'processing': 'spinner fa-spin', 'uploaded': 'cloud-upload-alt', 'pending': 'clock', 'error': 'exclamation-triangle' }; return iconMap[this.status] || 'question-circle'; } } /** * Scraping result model */ class ScrapingResultModel { constructor(backendData) { this.success = backendData.success; this.url = backendData.url; this.title = backendData.title; this.content_length = backendData.content_length; this.processing_time = backendData.processing_time; this.data = backendData.data; this.error = backendData.error; } isSuccessful() { return this.success && !this.error; } getFormattedProcessingTime() { return `${this.processing_time.toFixed(2)} ثانیه`; } } // ==================== UTILITY FUNCTIONS ==================== /** * Format file size in bytes to human readable format */ function formatFileSize(bytes) { if (bytes === 0) return '0 بایت'; const k = 1024; const sizes = ['بایت', 'کیلوبایت', 'مگابایت', 'گیگابایت']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } /** * Format date for Persian locale */ function formatDate(dateString) { if (!dateString) return 'نامشخص'; const date = new Date(dateString); return date.toLocaleDateString('fa-IR', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } /** * Show toast notification */ function showToast(message, type = 'info', title = 'اعلان') { const toastContainer = document.getElementById('toastContainer'); if (!toastContainer) return; const toast = document.createElement('div'); toast.className = `toast ${type}`; const icons = { success: 'check-circle', error: 'exclamation-triangle', warning: 'exclamation-circle', info: 'info-circle' }; toast.innerHTML = `