Dashboard
--
diff --git a/WorkflowEngine.php b/WorkflowEngine.php index 7e5af76..db5ed98 100644 --- a/WorkflowEngine.php +++ b/WorkflowEngine.php @@ -11,19 +11,45 @@ class WorkflowEngine { $this->pdo = db(); } - public function getDashboardMatrix(): array { - // Get all people (potential assignees) - $stmt_people = $this->pdo->prepare(" - SELECT p.*, bg.name as bni_group_name - FROM people p - LEFT JOIN bni_groups bg ON p.bni_group_id = bg.id - ORDER BY p.last_name, p.first_name - "); - $stmt_people->execute(); + public function getDashboardMatrix(?string $searchTerm = null, ?int $groupId = null, ?int $activeProcessDefinitionId = null): array { + // 1. Base query for people + $sql_people = "SELECT p.*, bg.name as bni_group_name FROM people p LEFT JOIN bni_groups bg ON p.bni_group_id = bg.id"; + $params = []; + $where_clauses = []; + + // 2. Add filter conditions + if ($searchTerm) { + $where_clauses[] = "(p.first_name LIKE :search OR p.last_name LIKE :search OR p.company_name LIKE :search OR p.email LIKE :search)"; + $params[':search'] = '%' . $searchTerm . '%'; + } + + if ($groupId) { + $where_clauses[] = "p.bni_group_id = :group_id"; + $params[':group_id'] = $groupId; + } + + if ($activeProcessDefinitionId) { + $terminal_statuses = ['positive', 'negative', 'completed', 'error', 'inactive']; + $in_clause = implode(',', array_map([$this->pdo, 'quote'], $terminal_statuses)); + + $sql_people .= " INNER JOIN process_instances pi ON p.id = pi.person_id"; + $where_clauses[] = "pi.process_definition_id = :active_process_id AND (pi.current_status IS NOT NULL AND pi.current_status NOT IN ($in_clause))"; + $params[':active_process_id'] = $activeProcessDefinitionId; + } + + if (!empty($where_clauses)) { + $sql_people .= " WHERE " . implode(" AND ", $where_clauses); + } + + $sql_people .= " ORDER BY p.last_name, p.first_name"; + + // 3. Execute query to get filtered people + $stmt_people = $this->pdo->prepare($sql_people); + $stmt_people->execute($params); $people = $stmt_people->fetchAll(PDO::FETCH_ASSOC); - // Fetch all process definitions with their JSON - $stmt_defs = $this->pdo->prepare("SELECT id, name, definition_json FROM process_definitions ORDER BY name"); + // 4. Fetch all process definitions with their JSON + $stmt_defs = $this->pdo->prepare("SELECT id, name, definition_json, is_active FROM process_definitions WHERE is_active = 1 ORDER BY sort_order, name"); $stmt_defs->execute(); $process_definitions_raw = $stmt_defs->fetchAll(PDO::FETCH_ASSOC); @@ -38,71 +64,61 @@ class WorkflowEngine { $definition_map[$def['id']] = !empty($def['definition_json']) ? json_decode($def['definition_json'], true) : null; } - // Fetch instances - $stmt_instances = $this->pdo->prepare("SELECT * FROM process_instances"); - $stmt_instances->execute(); - $instances_data = $stmt_instances->fetchAll(PDO::FETCH_ASSOC); - + // 5. Fetch instances ONLY for the filtered people $instances = []; - foreach ($instances_data as $instance) { - $enriched_instance = $instance; - $def_id = $instance['process_definition_id']; - $node_id = $instance['current_node_id']; - - $definition = $definition_map[$def_id] ?? null; - - if ($definition && isset($definition['type']) && $definition['type'] === 'checklist') { - $tasks = $definition['tasks'] ?? []; - $instanceData = $instance['data_json'] ? json_decode($instance['data_json'], true) : []; - $totalTasks = count($tasks); - $completedTasks = 0; - if(is_array($instanceData)) { - foreach ($tasks as $task) { - if (!empty($instanceData[$task['code']])) { - $completedTasks++; + $person_ids = array_column($people, 'id'); + if (!empty($person_ids)) { + $placeholders = implode(',', array_fill(0, count($person_ids), '?')); + $stmt_instances = $this->pdo->prepare("SELECT * FROM process_instances WHERE person_id IN ($placeholders)"); + $stmt_instances->execute($person_ids); + $instances_data = $stmt_instances->fetchAll(PDO::FETCH_ASSOC); + + foreach ($instances_data as $instance) { + $enriched_instance = $instance; + $def_id = $instance['process_definition_id']; + $node_id = $instance['current_node_id']; + + $definition = $definition_map[$def_id] ?? null; + + if ($definition && isset($definition['type']) && $definition['type'] === 'checklist') { + $tasks = $definition['tasks'] ?? []; + $instanceData = $instance['data_json'] ? json_decode($instance['data_json'], true) : []; + $totalTasks = count($tasks); + $completedTasks = 0; + if(is_array($instanceData)) { + foreach ($tasks as $task) { + if (!empty($instanceData[$task['code']])) { + $completedTasks++; + } } } - } - - - if ($totalTasks > 0 && $completedTasks === $totalTasks) { - $status = 'completed'; - } elseif ($completedTasks > 0) { - $status = 'in_progress'; + + if ($totalTasks > 0 && $completedTasks === $totalTasks) { + $status = 'completed'; + } elseif ($completedTasks > 0) { + $status = 'in_progress'; + } else { + $status = 'inactive'; + } + $enriched_instance['computed_status'] = $status; + $enriched_instance['computed_reason'] = "$completedTasks/$totalTasks completed"; + $enriched_instance['computed_next_step'] = ''; + } else if ($definition && isset($definition['nodes'][$node_id])) { + $node_info = $definition['nodes'][$node_id]; + $enriched_instance['computed_status'] = $node_info['ui_hints']['status'] ?? $instance['current_status']; + $enriched_instance['computed_reason'] = $node_info['ui_hints']['reason'] ?? $instance['current_reason']; + $enriched_instance['computed_next_step'] = $node_info['ui_hints']['next_step'] ?? $instance['suggested_next_step']; } else { - $status = 'inactive'; - } - $enriched_instance['computed_status'] = $status; - $enriched_instance['computed_reason'] = "$completedTasks/$totalTasks completed"; - $enriched_instance['computed_next_step'] = ''; - } else if ($definition && isset($definition['nodes'][$node_id])) { - $node_info = $definition['nodes'][$node_id]; - $enriched_instance['computed_status'] = $node_info['ui_hints']['status'] ?? $instance['current_status']; - $enriched_instance['computed_reason'] = $node_info['ui_hints']['reason'] ?? $instance['current_reason']; - $enriched_instance['computed_next_step'] = $node_info['ui_hints']['next_step'] ?? $instance['suggested_next_step']; - } else { - $enriched_instance['computed_status'] = $instance['current_status']; - $enriched_instance['computed_reason'] = $instance['current_reason']; - $enriched_instance['computed_next_step'] = $instance['suggested_next_step']; - } - - $instances[$instance['person_id']][$def_id] = $enriched_instance; - } - - // Remove pre-emptive eligibility check. This is now handled on-demand by _get_instance_details.php - /* - foreach ($people as $person) { - foreach ($definitions as $def) { - if (!isset($instances[$person['id']][$def['id']])) { - $process_definition_raw = $process_definitions_raw[array_search($def['id'], array_column($process_definitions_raw, 'id'))]; - $eligibility = $this->checkEligibility($person['id'], $process_definition_raw); - $instances[$person['id']][$def['id']] = ['is_eligible' => $eligibility['is_eligible']]; + $enriched_instance['computed_status'] = $instance['current_status']; + $enriched_instance['computed_reason'] = $instance['current_reason']; + $enriched_instance['computed_next_step'] = $instance['suggested_next_step']; } + + $instances[$instance['person_id']][$def_id] = $enriched_instance; } } - */ - // Fetch ancillary data + // 6. Fetch ancillary data $stmt_functions = $this->pdo->query("SELECT * FROM functions ORDER BY display_order"); $all_functions = $stmt_functions->fetchAll(PDO::FETCH_ASSOC); @@ -115,6 +131,18 @@ class WorkflowEngine { $stmt_bni_groups = $this->pdo->query("SELECT * FROM bni_groups ORDER BY name"); $bni_groups = $stmt_bni_groups->fetchAll(PDO::FETCH_ASSOC); + // 7. Fetch Spotkania columns (upcoming meetings) + $today = date('Y-m-d H:i:s'); + $stmt_meetings = $this->pdo->prepare(" + SELECT bni_groups.id as group_id, bni_groups.name as group_name, MIN(calendar_events.start_datetime) as next_meeting_date + FROM bni_groups + LEFT JOIN calendar_event_groups ON bni_groups.id = calendar_event_groups.bni_group_id + LEFT JOIN calendar_events ON calendar_event_groups.calendar_event_id = calendar_events.id AND calendar_events.start_datetime >= :today + GROUP BY bni_groups.id + ORDER BY bni_groups.name + "); + $stmt_meetings->execute(['today' => $today]); + $spotkania_cols = $stmt_meetings->fetchAll(PDO::FETCH_ASSOC); return [ 'people' => $people, @@ -123,6 +151,7 @@ class WorkflowEngine { 'all_functions' => $all_functions, 'person_functions_map' => $person_functions_map, 'bni_groups' => $bni_groups, + 'spotkania_cols' => $spotkania_cols, // Add this to the return array ]; } @@ -698,4 +727,22 @@ class WorkflowEngine { // Also update the in-memory instance for the next step in the chain $instance['data_json'] = $newDataJson; } + + public function deleteInstance(int $instanceId): void { + $this->pdo->beginTransaction(); + try { + // Delete events + $stmt_events = $this->pdo->prepare("DELETE FROM process_events WHERE process_instance_id = ?"); + $stmt_events->execute([$instanceId]); + + // Delete instance + $stmt_instance = $this->pdo->prepare("DELETE FROM process_instances WHERE id = ?"); + $stmt_instance->execute([$instanceId]); + + $this->pdo->commit(); + } catch (Exception $e) { + $this->pdo->rollBack(); + throw $e; + } + } } \ No newline at end of file diff --git a/_get_instance_details.php b/_get_instance_details.php index 965bdd2..be19a97 100644 --- a/_get_instance_details.php +++ b/_get_instance_details.php @@ -102,6 +102,7 @@ $instance = $engine->getInstanceByDefId($person_id, $process_definition_id);
Brak zdarzeń.
+Outcome: = htmlspecialchars($outcomeLabel) ?>
+ +"= nl2br(htmlspecialchars($data['note'])) ?>"
+ + +Next follow-up: = date('d.m.Y, H:i', strtotime($data['next_contact_date'])) ?>
+ + + + = htmlspecialchars(ucfirst(str_replace('_', ' ', $event['event_type']))) ?> + ' . htmlspecialchars($message) . ''; + } + ?> + + By = htmlspecialchars($event['first_name'] . ' ' . $event['last_name']) ?> on = date('d.m.Y, H:i', strtotime($event['created_at'])) ?>