/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ load->model('desk_moloni/desk_moloni_config_model', 'config_model'); $this->load->model('desk_moloni/desk_moloni_sync_queue_model', 'queue_model'); $this->load->model('desk_moloni/desk_moloni_mapping_model', 'mapping_model'); $this->load->model('desk_moloni/desk_moloni_sync_log_model', 'sync_log_model'); $this->load->helper('desk_moloni'); $this->load->library('form_validation'); } /** * Dashboard main interface */ public function index() { if (!has_permission('desk_moloni', '', 'view')) { access_denied('desk_moloni'); } $data = [ 'title' => _l('desk_moloni_dashboard'), 'dashboard_stats' => $this->get_dashboard_stats(), 'recent_activities' => $this->sync_log_model->get_recent_activity(10), 'queue_summary' => $this->queue_model->get_queue_summary(), 'mapping_stats' => $this->mapping_model->get_mapping_statistics() ]; $data['title'] = 'Desk-Moloni Dashboard'; $this->load->view('admin/includes/header', $data); $this->load->view('admin/modules/desk_moloni/dashboard', $data); $this->load->view('admin/includes/footer'); } /** * Get dashboard statistics */ private function get_dashboard_stats() { try { return [ 'total_queued' => $this->queue_model->get_count(['status' => 'pending']), 'total_processing' => $this->queue_model->get_count(['status' => 'processing']), 'total_completed' => $this->queue_model->get_count(['status' => 'completed']), 'total_failed' => $this->queue_model->get_count(['status' => 'failed']), 'total_mappings' => $this->mapping_model->get_total_count(), 'oauth_status' => $this->config_model->isOAuthValid() ? 'connected' : 'disconnected' ]; } catch (Exception $e) { log_message('error', 'Dashboard stats error: ' . $e->getMessage()); return [ 'total_queued' => 0, 'total_processing' => 0, 'total_completed' => 0, 'total_failed' => 0, 'total_mappings' => 0, 'oauth_status' => 'unknown' ]; } } /** * Get dashboard analytics data */ public function get_analytics() { if (!has_permission('desk_moloni', '', 'view')) { $this->output ->set_status_header(403) ->set_content_type('application/json') ->set_output(json_encode(['success' => false, 'message' => _l('access_denied')])); return; } try { $days = (int) $this->input->get('days') ?: 7; $entity_type = $this->input->get('entity_type'); $analytics = [ 'summary' => $this->_get_summary_stats($days, $entity_type), 'charts' => $this->_get_chart_data($days, $entity_type), 'recent_activity' => $this->_get_recent_activity(20), 'error_analysis' => $this->_get_error_analysis($days), 'performance_metrics' => $this->_get_performance_metrics($days) ]; $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'data' => $analytics ])); } catch (Exception $e) { log_message('error', 'Desk-Moloni dashboard analytics error: ' . $e->getMessage()); $this->output ->set_status_header(500) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'message' => $e->getMessage() ])); } } /** * Get real-time sync status */ public function get_realtime_status() { if (!has_permission('desk_moloni', '', 'view')) { $this->output ->set_status_header(403) ->set_content_type('application/json') ->set_output(json_encode(['success' => false, 'message' => _l('access_denied')])); return; } try { $status = [ 'queue_status' => $this->_get_queue_realtime_status(), 'active_syncs' => $this->_get_active_syncs(), 'error_count_last_hour' => $this->_get_error_count_last_hour(), 'last_successful_sync' => $this->_get_last_successful_sync(), 'api_health' => $this->_check_api_health() ]; $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'data' => $status ])); } catch (Exception $e) { log_message('error', 'Desk-Moloni realtime status error: ' . $e->getMessage()); $this->output ->set_status_header(500) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'message' => $e->getMessage() ])); } } /** * Get sync rate trends */ public function get_sync_trends() { if (!has_permission('desk_moloni', '', 'view')) { $this->output ->set_status_header(403) ->set_content_type('application/json') ->set_output(json_encode(['success' => false, 'message' => _l('access_denied')])); return; } try { $period = $this->input->get('period') ?: 'daily'; $entity_type = $this->input->get('entity_type'); $days = (int) $this->input->get('days') ?: 30; $trends = $this->_get_sync_trends($period, $days, $entity_type); $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'data' => $trends ])); } catch (Exception $e) { log_message('error', 'Desk-Moloni sync trends error: ' . $e->getMessage()); $this->output ->set_status_header(500) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'message' => $e->getMessage() ])); } } /** * Export dashboard data */ public function export_data() { if (!has_permission('desk_moloni', '', 'view')) { access_denied('desk_moloni'); } try { $format = $this->input->get('format') ?: 'csv'; $type = $this->input->get('type') ?: 'sync_logs'; $days = (int) $this->input->get('days') ?: 30; switch ($type) { case 'sync_logs': $this->_export_sync_logs($format, $days); break; case 'error_report': $this->_export_error_report($format, $days); break; case 'performance_report': $this->_export_performance_report($format, $days); break; default: throw new Exception(_l('desk_moloni_invalid_export_type')); } } catch (Exception $e) { log_message('error', 'Desk-Moloni export error: ' . $e->getMessage()); set_alert('danger', $e->getMessage()); redirect(admin_url('modules/desk_moloni')); } } /** * Get summary statistics */ private function _get_summary_stats($days, $entity_type = null) { try { $date_from = date('Y-m-d H:i:s', strtotime("-{$days} days")); $filters = ['created_at >=' => $date_from]; if ($entity_type) { $filters['entity_type'] = $entity_type; } $total_synced = $this->sync_log_model->countLogs($filters); $successful_syncs = $this->sync_log_model->countLogs(array_merge($filters, ['status' => 'success'])); $failed_syncs = $this->sync_log_model->countLogs(array_merge($filters, ['status' => 'error'])); $stats = [ 'total_synced' => $total_synced, 'successful_syncs' => $successful_syncs, 'failed_syncs' => $failed_syncs, 'sync_success_rate' => $total_synced > 0 ? round(($successful_syncs / $total_synced) * 100, 2) : 0, 'avg_execution_time' => method_exists($this->sync_log_model, 'getAverageExecutionTime') ? $this->sync_log_model->getAverageExecutionTime($filters) : 0, 'queue_health_score' => $this->_calculate_queue_health_score() ]; return $stats; } catch (Exception $e) { log_message('error', 'Desk-Moloni summary stats error: ' . $e->getMessage()); return []; } } /** * Get chart data for dashboard visualizations */ private function _get_chart_data($days, $entity_type = null) { try { return [ 'sync_volume_chart' => $this->_get_sync_volume_chart($days, $entity_type), 'success_rate_chart' => $this->_get_success_rate_chart($days, $entity_type), 'entity_distribution' => $this->_get_entity_sync_distribution($days), 'error_category_chart' => $this->_get_error_category_distribution($days), 'performance_chart' => $this->_get_performance_trend_chart($days) ]; } catch (Exception $e) { log_message('error', 'Desk-Moloni chart data error: ' . $e->getMessage()); return []; } } /** * Get recent activity for dashboard feed */ private function _get_recent_activity($limit = 20) { try { return $this->sync_log_model->getRecentActivity($limit); } catch (Exception $e) { log_message('error', 'Desk-Moloni recent activity error: ' . $e->getMessage()); return []; } } /** * Get error analysis data */ private function _get_error_analysis($days) { try { $date_from = date('Y-m-d H:i:s', strtotime("-{$days} days")); return [ 'top_errors' => $this->sync_log_model->getTopErrors($days, 10), 'error_trends' => $this->sync_log_model->getErrorTrends($days), 'critical_errors' => $this->sync_log_model->getCriticalErrors($days), 'resolution_suggestions' => $this->_get_error_resolution_suggestions() ]; } catch (Exception $e) { log_message('error', 'Desk-Moloni error analysis error: ' . $e->getMessage()); return []; } } /** * Get performance metrics */ private function _get_performance_metrics($days) { try { $date_from = date('Y-m-d H:i:s', strtotime("-{$days} days")); return [ 'avg_response_time' => $this->sync_log_model->getAverageResponseTime($date_from), 'throughput' => $this->sync_log_model->getThroughputPerHour($date_from), 'resource_usage' => $this->_get_resource_usage($days), 'bottlenecks' => $this->_identify_performance_bottlenecks($days) ]; } catch (Exception $e) { log_message('error', 'Desk-Moloni performance metrics error: ' . $e->getMessage()); return []; } } /** * Check API health status */ private function _check_api_health() { try { $this->load->library('desk_moloni/moloni_api_client'); return $this->moloni_api_client->health_check(); } catch (Exception $e) { return [ 'status' => 'error', 'message' => $e->getMessage(), 'timestamp' => date('Y-m-d H:i:s') ]; } } /** * Calculate queue health score */ private function _calculate_queue_health_score() { try { $total_tasks = $this->queue_model->countTasks(); $failed_tasks = $this->queue_model->countTasks(['status' => 'failed']); $pending_tasks = $this->queue_model->countTasks(['status' => 'pending']); if ($total_tasks == 0) return 100; // Score based on failure rate and queue backlog $failure_rate = $failed_tasks / $total_tasks; $backlog_ratio = min($pending_tasks / max($total_tasks, 1), 1); $score = 100 - ($failure_rate * 50) - ($backlog_ratio * 30); return max(0, round($score, 1)); } catch (Exception $e) { log_message('error', 'Desk-Moloni queue health score error: ' . $e->getMessage()); return 0; } } /** * Get queue realtime status */ private function _get_queue_realtime_status() { try { return [ 'total_tasks' => $this->queue_model->countTasks(), 'pending_tasks' => $this->queue_model->countTasks(['status' => 'pending']), 'processing_tasks' => $this->queue_model->countTasks(['status' => 'processing']), 'failed_tasks' => $this->queue_model->countTasks(['status' => 'failed']), 'completed_today' => $this->queue_model->countTasks([ 'status' => 'completed', 'completed_at >=' => date('Y-m-d 00:00:00') ]) ]; } catch (Exception $e) { log_message('error', 'Desk-Moloni queue realtime status error: ' . $e->getMessage()); return []; } } /** * Get active syncs */ private function _get_active_syncs() { try { return $this->queue_model->getActiveTasks(); } catch (Exception $e) { log_message('error', 'Desk-Moloni active syncs error: ' . $e->getMessage()); return []; } } /** * Get error count from last hour */ private function _get_error_count_last_hour() { try { $one_hour_ago = date('Y-m-d H:i:s', strtotime('-1 hour')); return $this->sync_log_model->countLogs([ 'status' => 'error', 'created_at >=' => $one_hour_ago ]); } catch (Exception $e) { log_message('error', 'Desk-Moloni error count last hour error: ' . $e->getMessage()); return 0; } } /** * Get last successful sync */ private function _get_last_successful_sync() { try { return $this->sync_log_model->getLastSuccessfulSync(); } catch (Exception $e) { log_message('error', 'Desk-Moloni last successful sync error: ' . $e->getMessage()); return null; } } /** * Get sync trends */ private function _get_sync_trends($period, $days, $entity_type = null) { try { return $this->sync_log_model->getSyncTrends($period, $days, $entity_type); } catch (Exception $e) { log_message('error', 'Desk-Moloni sync trends error: ' . $e->getMessage()); return []; } } /** * Export sync logs */ private function _export_sync_logs($format, $days) { $date_from = date('Y-m-d H:i:s', strtotime("-{$days} days")); $logs = $this->sync_log_model->getLogsForExport(['created_at >=' => $date_from]); if ($format === 'csv') { $this->_export_as_csv($logs, 'sync_logs_' . date('Y-m-d')); } else { $this->_export_as_json($logs, 'sync_logs_' . date('Y-m-d')); } } /** * Export error report */ private function _export_error_report($format, $days) { $date_from = date('Y-m-d H:i:s', strtotime("-{$days} days")); $errors = $this->sync_log_model->getErrorReport(['created_at >=' => $date_from]); if ($format === 'csv') { $this->_export_as_csv($errors, 'error_report_' . date('Y-m-d')); } else { $this->_export_as_json($errors, 'error_report_' . date('Y-m-d')); } } /** * Export performance report */ private function _export_performance_report($format, $days) { $performance = $this->_get_performance_report($days); if ($format === 'csv') { $this->_export_as_csv($performance, 'performance_report_' . date('Y-m-d')); } else { $this->_export_as_json($performance, 'performance_report_' . date('Y-m-d')); } } /** * Export data as CSV */ private function _export_as_csv($data, $filename) { header('Content-Type: text/csv'); header('Content-Disposition: attachment; filename="' . $filename . '.csv"'); $output = fopen('php://output', 'w'); if (!empty($data)) { fputcsv($output, array_keys($data[0])); foreach ($data as $row) { fputcsv($output, $row); } } fclose($output); } /** * Export data as JSON */ private function _export_as_json($data, $filename) { header('Content-Type: application/json'); header('Content-Disposition: attachment; filename="' . $filename . '.json"'); echo json_encode($data, JSON_PRETTY_PRINT); } /** * Get performance report data */ private function _get_performance_report($days) { try { $date_from = date('Y-m-d H:i:s', strtotime("-{$days} days")); return [ 'period' => $days . ' days', 'from_date' => $date_from, 'to_date' => date('Y-m-d H:i:s'), 'metrics' => $this->_get_performance_metrics($days) ]; } catch (Exception $e) { log_message('error', 'Desk-Moloni performance report error: ' . $e->getMessage()); return []; } } /** * Placeholder methods for complex analytics (to be implemented) */ private function _get_sync_volume_chart($days, $entity_type = null) { return []; } private function _get_success_rate_chart($days, $entity_type = null) { return []; } private function _get_entity_sync_distribution($days) { return []; } private function _get_error_category_distribution($days) { return []; } private function _get_performance_trend_chart($days) { return []; } private function _get_error_resolution_suggestions() { return []; } private function _get_resource_usage($days) { return []; } private function _identify_performance_bottlenecks($days) { return []; } }