diff --git a/WorkflowEngine.php b/WorkflowEngine.php index f4071b8..98ed786 100644 --- a/WorkflowEngine.php +++ b/WorkflowEngine.php @@ -1,4 +1,5 @@ pdo->beginTransaction(); try { // 1. Find active process definition by code. $stmt_def = $this->pdo->prepare("SELECT * FROM process_definitions WHERE code = ? AND active = 1"); $stmt_def->execute([$processCode]); $definition = $stmt_def->fetch(PDO::FETCH_ASSOC); - error_log("startProcess: definition=" . print_r($definition, true)); if (!$definition) { - throw new Exception("Active process definition with code '$processCode' not found."); - } + // If no process definition is found, check if there is a definition for a checklist + $stmt_def = $this->pdo->prepare("SELECT * FROM process_definitions WHERE id = ?"); + $stmt_def->execute([$processCode]); + $definition = $stmt_def->fetch(PDO::FETCH_ASSOC); - $definition_json = json_decode($definition['definition_json'], true); - if (!$definition_json || !isset($definition_json['start_node_id'])) { - throw new Exception("Process definition is missing start_node_id."); + if (!$definition) { + throw new Exception("Process definition with code or id '$processCode' not found."); + } + + $definition_json = !empty($definition['definition_json']) ? json_decode($definition['definition_json'], true) : []; + if (empty($definition_json) || $definition_json['type'] !== 'checklist') { + throw new Exception("Process definition with code '$processCode' not found or not a checklist."); + } + + // For checklists, there's no start_node_id, so we can proceed with instance creation + $startNodeId = null; + + } else { + $definition_json = !empty($definition['definition_json']) ? json_decode($definition['definition_json'], true) : []; + if (empty($definition_json) || !isset($definition_json['start_node_id'])) { + throw new Exception("Process definition is missing start_node_id."); + } + $startNodeId = $definition_json['start_node_id']; } - $startNodeId = $definition_json['start_node_id']; // 2. Create a new process instance. $stmt_insert = $this->pdo->prepare( - "INSERT INTO process_instances (personId, processDefinitionId, current_node_id, current_status, lastActivityAt) VALUES (?, ?, ?, 'in_progress', NOW())" + "INSERT INTO process_instances (person_id, process_definition_id, current_node_id, current_status, lastActivityAt) VALUES (?, ?, ?, 'in_progress', NOW())" ); $stmt_insert->execute([$personId, $definition['id'], $startNodeId]); - error_log("startProcess: affected rows=" . $stmt_insert->rowCount()); $instanceId = $this->pdo->lastInsertId(); - error_log("startProcess: instanceId=$instanceId"); // 3. Create a system event for process start. $this->addEvent($instanceId, 'system', 'Process started.', $startNodeId, [], $userId); @@ -88,9 +101,9 @@ class WorkflowEngine { } $stmt_def = $this->pdo->prepare("SELECT definition_json FROM process_definitions WHERE id = ?"); - $stmt_def->execute([$instance['processDefinitionId']]); + $stmt_def->execute([$instance['process_definition_id']]); $definition_json = $stmt_def->fetchColumn(); - $definition = json_decode($definition_json, true); + $definition = !empty($definition_json) ? json_decode($definition_json, true) : []; $currentNodeId = $instance['current_node_id']; $nodeInfo = $definition['nodes'][$currentNodeId] ?? null; @@ -157,22 +170,45 @@ class WorkflowEngine { } } + public function addNote(int $instanceId, string $message, int $userId): bool { + $state = $this->getProcessState($instanceId); + if (!$state) { + // Even if the instance isn't found, we shouldn't crash. Log and return false. + error_log("addNote failed: Process instance $instanceId not found."); + return false; + } + $currentNodeId = $state['instance']['current_node_id']; + $payload = ['message' => $message]; + $this->addEvent($instanceId, 'note', $message, $currentNodeId, $payload, $userId); + return true; + } + private function addEvent(int $instanceId, string $eventType, string $message, ?string $nodeId, array $payload, int $userId): void { $stmt = $this->pdo->prepare( - "INSERT INTO process_events (processInstanceId, eventType, message, node_id, payload_json, createdById) VALUES (?, ?, ?, ?, ?, ?)" + "INSERT INTO process_events (processInstanceId, event_type, message, node_id, payload_json, createdBy, createdAt) VALUES (?, ?, ?, ?, ?, ?, NOW())" ); $stmt->execute([$instanceId, $eventType, $message, $nodeId, json_encode($payload), $userId]); } public function getOrCreateInstanceByDefId(int $personId, int $processDefinitionId, int $userId): ?array { - $stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE personId = ? AND processDefinitionId = ?"); + $stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE `person_id` = ? AND `process_definition_id` = ?"); $stmt->execute([$personId, $processDefinitionId]); $instance = $stmt->fetch(PDO::FETCH_ASSOC); if (!$instance) { - $stmt_def = $this->pdo->prepare("SELECT code FROM process_definitions WHERE id = ?"); + // For checklists, the process code is the process definition ID + $stmt_def = $this->pdo->prepare("SELECT definition_json FROM process_definitions WHERE id = ?"); $stmt_def->execute([$processDefinitionId]); - $processCode = $stmt_def->fetchColumn(); + $definition_json = $stmt_def->fetchColumn(); + $definition = !empty($definition_json) ? json_decode($definition_json, true) : []; + + if ($definition && isset($definition['type']) && $definition['type'] === 'checklist') { + $processCode = (string) $processDefinitionId; + } else { + $stmt_def = $this->pdo->prepare("SELECT code FROM process_definitions WHERE id = ?"); + $stmt_def->execute([$processDefinitionId]); + $processCode = $stmt_def->fetchColumn(); + } if($processCode) { $instanceId = $this->startProcess($processCode, $personId, $userId); @@ -187,7 +223,7 @@ class WorkflowEngine { } public function getEvents(int $instanceId): array { - $stmt_events = $this->pdo->prepare("SELECT pe.*, p.email as user_email, p.firstName, p.lastName FROM process_events pe JOIN people p ON pe.createdById = p.id WHERE pe.processInstanceId = ? ORDER BY pe.createdAt DESC"); + $stmt_events = $this->pdo->prepare("SELECT pe.*, p.email as user_email, p.firstName, p.lastName FROM process_events pe JOIN people p ON pe.createdBy = p.id WHERE pe.processInstanceId = ? ORDER BY pe.createdAt DESC"); $stmt_events->execute([$instanceId]); return $stmt_events->fetchAll(PDO::FETCH_ASSOC); } @@ -212,4 +248,17 @@ class WorkflowEngine { return $transitions; } -} + + public function getProcessDefinitionNodes(int $processDefinitionId): array { + $stmt = $this->pdo->prepare("SELECT definition_json FROM process_definitions WHERE id = ?"); + $stmt->execute([$processDefinitionId]); + $json = $stmt->fetchColumn(); + + if (!$json) { + return []; + } + + $definition = !empty($json) ? json_decode($json, true) : []; + return $definition['nodes'] ?? []; + } +} \ No newline at end of file diff --git a/_apply_transition.php b/_apply_transition.php index 34621ca..fced8b8 100644 --- a/_apply_transition.php +++ b/_apply_transition.php @@ -1,43 +1,67 @@ false, 'message' => 'Wystąpił nieoczekiwany błąd.']; if ($_SERVER['REQUEST_METHOD'] !== 'POST') { - header('Location: process_dashboard.php'); + http_response_code(405); + $response['message'] = 'Invalid request method.'; + echo json_encode($response); exit; } require_once 'WorkflowEngine.php'; if (!isset($_POST['instanceId']) || !isset($_POST['transitionId'])) { - $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Błąd: Brak wymaganych parametrów.']; - header('Location: process_dashboard.php'); + http_response_code(400); + $response['message'] = 'Błąd: Brak wymaganych parametrów.'; + echo json_encode($response); exit; } $instanceId = (int)$_POST['instanceId']; $transitionId = $_POST['transitionId']; $userId = $_SESSION['user_id'] ?? null; -$payload = $_POST['payload'] ?? null; +$payload = $_POST['payload'] ?? []; if (!$userId) { - $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Błąd: Sesja wygasła.']; - header('Location: login.php'); + http_response_code(401); + $response['message'] = 'Błąd: Sesja wygasła.'; + echo json_encode($response); exit; } try { $engine = new WorkflowEngine(); - $success = $engine->applyTransition($instanceId, $transitionId, $payload, $userId); + $success = false; + + if ($transitionId === 'note') { + // Special case: Just add a note, don't change state. + $message = $payload['message'] ?? ''; + if (!empty($message)) { + $success = $engine->addNote($instanceId, $message, $userId); + if ($success) { + $response['message'] = 'Notatka została dodana.'; + } + } + } else { + // Standard transition logic + $success = $engine->applyTransition($instanceId, $transitionId, $payload, $userId); + if ($success) { + $response['message'] = 'Akcja została wykonana pomyślnie.'; + } + } if ($success) { - $_SESSION['flash_message'] = ['type' => 'success', 'message' => 'Akcja została wykonana pomyślnie.']; - } else { - $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Błąd: Nie udało się wykonać akcji.']; + $response['success'] = true; } + } catch (Exception $e) { error_log("Error applying transition: " . $e->getMessage()); - $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Wystąpił krytyczny błąd: ' . $e->getMessage()]; + http_response_code(500); + $response['message'] = 'Wystąpił krytyczny błąd: ' . $e->getMessage(); } -header('Location: process_dashboard.php'); +echo json_encode($response); exit; \ No newline at end of file diff --git a/_get_instance_details.php b/_get_instance_details.php index b19d060..514a6e8 100644 --- a/_get_instance_details.php +++ b/_get_instance_details.php @@ -1,17 +1,15 @@ getOrCreateInstanceByDefId($personId, $processDefinitionId, $userId); - if (!$instance) { http_response_code(500); - die("Nie można pobrać lub utworzyć instancji procesu. personId: $personId, processDefinitionId: $processDefinitionId, userId: $userId"); + die("Nie można pobrać lub utworzyć instancji procesu."); } $instanceId = $instance['id']; -// 2. Pobierz powiązane dane przez silnik -$events = $engine->getEvents($instanceId); -$availableTransitions = $engine->getAvailableTransitions($instanceId); - -// 3. Pobierz nazwy do wyświetlenia -$pdo = db(); +// 2. Fetch all related data $stmt_person = $pdo->prepare("SELECT firstName, lastName FROM people WHERE id = ?"); $stmt_person->execute([$personId]); $person = $stmt_person->fetch(); -$stmt_process = $pdo->prepare("SELECT name FROM process_definitions WHERE id = ?"); +$stmt_process = $pdo->prepare("SELECT * FROM process_definitions WHERE id = ?"); $stmt_process->execute([$processDefinitionId]); $process = $stmt_process->fetch(); +$definition = $process && $process['definition_json'] ? json_decode($process['definition_json'], true) : null; +$isChecklist = ($definition && isset($definition['type']) && $definition['type'] === 'checklist'); + +$events = $engine->getEvents($instanceId); + ?> -

-

-

Status:

-
+ +
+ - +
-
Wykonaj akcję
-
- -
- - -
-
- - -
- -
- -
- -
Historia
- -

Brak zdarzeń.

- - - \ No newline at end of file +
+ > + +
+ + + + + + getProcessDefinitionNodes($processDefinitionId); + $availableTransitions = $engine->getAvailableTransitions($instanceId); + + $available_target_node_ids = array_map(function($t) { return $t['to']; }, $availableTransitions); + $available_transitions_map = []; + foreach ($availableTransitions as $t) { + $available_transitions_map[$t['to']] = $t; + } + + $visited_nodes = []; + foreach ($events as $event) { + if ($event['node_id']) { + $visited_nodes[$event['node_id']] = true; + } + } + ?> +
+
Kroki procesu
+ +
+ + +
+ +
+
Dodaj notatkę
+
+ +
+ +
+ +
+ +
+
Historia
+ +

Brak zdarzeń.

+ + + +
+ + + diff --git a/_get_process_bulk_details.php b/_get_process_bulk_details.php new file mode 100644 index 0000000..05ed8e5 --- /dev/null +++ b/_get_process_bulk_details.php @@ -0,0 +1,96 @@ + 'Process ID is required.']); + exit; +} + +$process_id = $_GET['process_id']; + +try { + $pdo = db(); + + // 1. Get process definition details + $stmt_def = $pdo->prepare("SELECT * FROM process_definitions WHERE id = ?"); + $stmt_def->execute([$process_id]); + $process_definition = $stmt_def->fetch(PDO::FETCH_ASSOC); + + if (!$process_definition) { + http_response_code(404); + echo json_encode(['error' => 'Process definition not found.']); + exit; + } + + if (!empty($process_definition['definition_json'])) { + $process_definition['definition_json'] = json_decode($process_definition['definition_json'], true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new Exception("Failed to decode process definition JSON. Error: " . json_last_error_msg()); + } + } else { + $process_definition['definition_json'] = []; + } + + // 2. Get all instances for this process + $stmt_instances = $pdo->prepare("SELECT * FROM process_instances WHERE processDefinitionId = ?"); + $stmt_instances->execute([$process_id]); + $instances = $stmt_instances->fetchAll(PDO::FETCH_ASSOC); + + $instance_ids = array_map(function($i) { return $i['id']; }, $instances); + + // 3. Get all events for these instances + $events = []; + if (!empty($instance_ids)) { + $placeholders = implode(',', array_fill(0, count($instance_ids), '?')); + $stmt_events = $pdo->prepare("SELECT * FROM process_events WHERE processInstanceId IN ($placeholders) ORDER BY createdAt, id"); + $stmt_events->execute($instance_ids); + $all_events = $stmt_events->fetchAll(PDO::FETCH_ASSOC); + // Group events by instance_id + foreach ($all_events as $event) { + $events[$event['processInstanceId']][] = $event; + } + } + + // 4. Get People details + $people_ids = array_unique(array_column($instances, 'personId')); + $people = []; + if (!empty($people_ids)) { + $valid_people_ids = array_filter($people_ids, 'is_numeric'); + + if (!empty($valid_people_ids)) { + $placeholders = implode(',', array_fill(0, count($valid_people_ids), '?')); + $stmt_people = $pdo->prepare("SELECT id, firstName, lastName FROM people WHERE id IN ($placeholders)"); + $stmt_people->execute(array_values($valid_people_ids)); + $people_results = $stmt_people->fetchAll(PDO::FETCH_ASSOC); + foreach ($people_results as $person) { + $people[$person['id']] = $person; + $people[$person['id']]['name'] = trim($person['firstName'] . ' ' . $person['lastName']); + } + } + } + + // Assemble the response + // Ensure steps are available, even if the JSON is empty or malformed. + $steps = !empty($process_definition['definition_json']['steps']) ? $process_definition['definition_json']['steps'] : []; + + $response = [ + 'process' => $process_definition, + 'steps' => $steps, + 'instances' => $instances, + 'events' => $events, + 'people' => $people + ]; + + echo json_encode($response); + +} catch (PDOException $e) { + http_response_code(500); + echo json_encode(['error' => 'A database error occurred.', 'details' => $e->getMessage()]); +} catch (Exception $e) { + http_response_code(500); + echo json_encode(['error' => 'A general error occurred.', 'details' => $e->getMessage()]); +} +?> \ No newline at end of file diff --git a/_init_single_instance.php b/_init_single_instance.php new file mode 100644 index 0000000..5e612c2 --- /dev/null +++ b/_init_single_instance.php @@ -0,0 +1,31 @@ +getOrCreateInstanceByDefId($personId, $processDefinitionId, $userId); + +if ($instance) { + $_SESSION['success_message'] = "Process initialized successfully."; +} else { + $_SESSION['error_message'] = "Failed to initialize process."; +} + +header('Location: index.php'); +exit(); diff --git a/_update_training_checklist_status.php b/_update_training_checklist_status.php new file mode 100644 index 0000000..e8c6852 --- /dev/null +++ b/_update_training_checklist_status.php @@ -0,0 +1,81 @@ + false, 'error' => 'Unauthorized']); + exit; +} + +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + http_response_code(405); + echo json_encode(['error' => 'Method Not Allowed']); + exit; +} + +$inputJSON = file_get_contents('php://input'); +$input = json_decode($inputJSON, true); + +if (json_last_error() !== JSON_ERROR_NONE) { + http_response_code(400); + echo json_encode(['error' => 'Invalid JSON']); + exit; +} + +$instanceId = $input['instance_id'] ?? null; +$taskCode = $input['task_code'] ?? null; +$isChecked = $input['is_checked'] ?? null; + +if (!$instanceId || !$taskCode || $isChecked === null) { + http_response_code(400); + echo json_encode(['error' => 'Missing required parameters: instance_id, task_code, is_checked']); + exit; +} + +try { + $pdo = db(); + + // Get current data_json + $stmt = $pdo->prepare("SELECT data_json FROM process_instances WHERE id = ?"); + $stmt->execute([$instanceId]); + $currentDataJson = $stmt->fetchColumn(); + + $data = $currentDataJson ? json_decode($currentDataJson, true) : []; + + // Update the specific task status + $data[$taskCode] = (bool)$isChecked; + $newDataJson = json_encode($data); + + // Save new data_json and update timestamp + $stmt = $pdo->prepare("UPDATE process_instances SET data_json = ?, lastActivityAt = CURRENT_TIMESTAMP WHERE id = ?"); + $success = $stmt->execute([$newDataJson, $instanceId]); + + if ($success) { + // Calculate progress + $stmt = $pdo->prepare("SELECT pd.definition_json FROM process_definitions pd JOIN process_instances pi ON pd.id = pi.process_definition_id WHERE pi.id = ?"); + $stmt->execute([$instanceId]); + $definitionJson = $stmt->fetchColumn(); + $definition = json_decode($definitionJson, true); + $totalTasks = count($definition['tasks'] ?? []); + $completedTasks = count(array_filter($data)); + + echo json_encode([ + 'success' => true, + 'message' => 'Status updated successfully.', + 'progress' => [ + 'completed' => $completedTasks, + 'total' => $totalTasks + ], + 'lastActivityAt' => date('d/m/y') + ]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Failed to update status.']); + } + +} catch (PDOException $e) { + http_response_code(500); + echo json_encode(['error' => 'Database error: ' . $e->getMessage()]); +} +?> \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js index 3a2a7cf..9011386 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -10,3 +10,151 @@ document.addEventListener('DOMContentLoaded', function () { }); } }); + +$(document).ready(function() { + // Handler for showing the edit person modal + $('#editPersonModal').on('show.bs.modal', function (event) { + var button = $(event.relatedTarget); // Button that triggered the modal + var personId = button.data('person-id'); // Extract info from data-* attributes + var modal = $(this); + + // Clear previous data + modal.find('form').trigger('reset'); + modal.find('#editPersonId').val(''); + modal.find('#editRoles').empty(); + // Clear file paths + modal.find('#editCompanyLogoPath, #editPersonPhotoPath, #editGainsSheetPath, #editTopWantedPath, #editTopOwnedPath').text(''); + + if (personId) { + // AJAX request to get person details + $.ajax({ + url: '_get_person_details.php', + type: 'GET', + data: { id: personId }, + dataType: 'json', + success: function(response) { + if (response.error) { + alert('Error fetching person details: ' + response.error); + return; + } + + var person = response.person; + var all_functions = response.all_functions; + var person_functions = response.person_functions; + + if (!person) { + alert('Could not find person data.'); + return; + } + + // Populate the form fields + modal.find('#editPersonId').val(person.id); + modal.find('#editFirstName').val(person.firstName); + modal.find('#editLastName').val(person.lastName); + modal.find('#editPhone').val(person.phone); + modal.find('#editEmail').val(person.email); + modal.find('#editRole').val(person.role); + modal.find('#editBniGroup').val(person.bni_group_id); + modal.find('#editCompanyName').val(person.companyName); + modal.find('#editNip').val(person.nip); + modal.find('#editIndustry').val(person.industry); + modal.find('#editCompanySize').val(person.company_size_revenue); + modal.find('#editBusinessDescription').val(person.business_description); + + // Populate file paths + if (person.company_logo_path) { + modal.find('#editCompanyLogoPath').text('Current file: ' + person.company_logo_path.split('/').pop()); + } + if (person.person_photo_path) { + modal.find('#editPersonPhotoPath').text('Current file: ' + person.person_photo_path.split('/').pop()); + } + if (person.gains_sheet_path) { + modal.find('#editGainsSheetPath').text('Current file: ' + person.gains_sheet_path.split('/').pop()); + } + if (person.top_wanted_contacts_path) { + modal.find('#editTopWantedPath').text('Current file: ' + person.top_wanted_contacts_path.split('/').pop()); + } + if (person.top_owned_contacts_path) { + modal.find('#editTopOwnedPath').text('Current file: ' + person.top_owned_contacts_path.split('/').pop()); + } + + // Populate functions/roles dropdown and select assigned ones + var rolesSelect = modal.find('#editRoles'); + rolesSelect.empty(); // Clear existing options + + if (all_functions && all_functions.length > 0) { + const groupedFunctions = all_functions.reduce((acc, func) => { + const groupName = func.group_name || 'General'; + if (!acc[groupName]) { + acc[groupName] = []; + } + acc[groupName].push(func); + return acc; + }, {}); + + for (const groupName in groupedFunctions) { + const optgroup = $('').attr('label', groupName); + groupedFunctions[groupName].forEach(function(func) { + var option = $('').val(func.id).text(func.name); + if (person_functions && person_functions.includes(String(func.id))) { + option.prop('selected', true); + } + optgroup.append(option); + }); + rolesSelect.append(optgroup); + } + } + + // Trigger change to show/hide conditional fields + modal.find('#editRole').trigger('change'); + + // Also set up the delete button + $('#deleteUserBtn').data('person-id', person.id); + $('#personNameToDelete').text(person.firstName + ' ' + person.lastName); + + }, + error: function(xhr, status, error) { + alert('An error occurred while fetching person data. Please try again.'); + console.error("AJAX Error:", status, error); + } + }); + } + }); + + // Show/hide group selection based on role for both Edit and Create modals + $(document).on('change', '#editRole, #createRole', function() { + const role = $(this).val(); + const isMember = role === 'member'; + + // Find the correct context (modal) for the elements + const modal = $(this).closest('.modal-content'); + + modal.find('.member-only-fields').toggle(isMember); + modal.find('#edit-group-selection-div, #create-group-selection-div').toggle(isMember); + }); + + // Handle Delete Person confirmation + $('#confirmDeleteBtn').on('click', function() { + var personId = $('#deleteUserBtn').data('person-id'); + if (personId) { + // Use a form submission to perform the delete + var form = $('
'); + form.attr("method", "post"); + form.attr("action", "_delete_person.php"); + + var field = $(''); + field.attr("type", "hidden"); + field.attr("name", "id"); + field.attr("value", personId); + form.append(field); + + $(document.body).append(form); + form.submit(); + } + }); + + // Set initial state for create form + $('#createPersonModal').on('show.bs.modal', function () { + $('#createRole').trigger('change'); + }); +}); \ No newline at end of file diff --git a/cookie.txt b/cookie.txt index 82d4cef..23d0714 100644 --- a/cookie.txt +++ b/cookie.txt @@ -2,4 +2,4 @@ # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -localhost FALSE / FALSE 0 PHPSESSID u19ekrhqoemk4c5avca3umanfb +127.0.0.1 FALSE / FALSE 0 PHPSESSID abf9a8g0sidv8idojcrr65jetp diff --git a/db_setup.php b/db_setup.php index ced3af6..2a4fe95 100644 --- a/db_setup.php +++ b/db_setup.php @@ -67,17 +67,17 @@ try { // 3. Process Instances table (updated FK) $pdo->exec("CREATE TABLE IF NOT EXISTS `process_instances` ( `id` INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY, - `personId` INT(11) UNSIGNED NOT NULL, - `processDefinitionId` INT(11) UNSIGNED NOT NULL, + `person_id` INT(11) UNSIGNED NOT NULL, + `process_definition_id` INT(11) UNSIGNED NOT NULL, `current_status` VARCHAR(255) NOT NULL DEFAULT 'none', `current_node_id` VARCHAR(255), `current_reason` TEXT, `suggested_next_step` TEXT, `data_json` TEXT, `lastActivityAt` TIMESTAMP NULL, - FOREIGN KEY (personId) REFERENCES people(id) ON DELETE CASCADE, - FOREIGN KEY (processDefinitionId) REFERENCES process_definitions(id) ON DELETE CASCADE, - UNIQUE KEY `person_process` (`personId`, `processDefinitionId`) + FOREIGN KEY (person_id) REFERENCES people(id) ON DELETE CASCADE, + FOREIGN KEY (process_definition_id) REFERENCES process_definitions(id) ON DELETE CASCADE, + UNIQUE KEY `person_process` (`person_id`, `process_definition_id`) )"); echo "Process instances table created or already exists.\n"; @@ -121,6 +121,7 @@ try { $pdo->exec("ALTER TABLE `process_instances` CHANGE `contactId` `personId` INT(11) UNSIGNED NOT NULL;"); echo "Migrated process_instances: contactId -> personId.\n"; } + // Drop old tables if they exist $pdo->exec("DROP TABLE IF EXISTS `users`, `contacts`;"); diff --git a/index.php b/index.php index e4456b8..5eb3fe5 100644 --- a/index.php +++ b/index.php @@ -1,4 +1,5 @@ query(" $people = $stmt->fetchAll(PDO::FETCH_ASSOC); // Fetch process definitions -$stmt = $pdo->prepare("SELECT * FROM process_definitions WHERE name NOT IN (?, ?) ORDER BY name"); +$stmt = $pdo->prepare("SELECT * FROM process_definitions WHERE name NOT IN (?, ?)" + . " ORDER BY name"); $stmt->execute(['Obsluga goscia', 'Przygotowanie spotkania grupy']); $processes = $stmt->fetchAll(PDO::FETCH_ASSOC); @@ -31,10 +33,14 @@ foreach ($all_instances as $instance) { $status_colors = [ - 'none' => 'secondary', - 'negative' => 'danger', - 'in_progress' => 'warning', - 'positive' => 'success', + 'completed' => '#28a745', + 'positive' => '#28a745', + 'in_progress' => '#fd7e14', + 'negative' => '#dc3545', + 'error' => '#dc3545', + 'none' => '#808080', + 'not_started' => '#808080', + 'inactive' => '#808080', ]; $pdo = db(); @@ -65,167 +71,216 @@ $bni_groups = $stmt_bni_groups->fetchAll(PDO::FETCH_ASSOC);
- - - - +
+ + + + - - - - + + + + -
-

Dashboard

-
- + -
- +
+ + 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); + ?> + +
+ + +
+ +
+ prepare("SELECT id FROM process_definitions WHERE name = ?"); + $stmt_meeting_process->execute(['Przygotowanie spotkania grupy']); + $meeting_process = $stmt_meeting_process->fetch(PDO::FETCH_ASSOC); + $meeting_process_id = $meeting_process ? $meeting_process['id'] : 'null'; + ?> + + + + + + + + + + + + + + $col): ?> + + + + + + + + + + + + + + + $col): ?> + + + + + 0 && $completedTasks === $totalTasks) { + $status = 'completed'; + } elseif ($completedTasks > 0) { + $status = 'in_progress'; + } else { + $status = 'inactive'; // Initialized but no progress + } + } + $color = $status_colors[$status] ?? $status_colors['inactive']; + ?> + + + + + + + +
PersonSpotkaniaInne procesy
+
+ + + + + + +
+
+
+
+ + + + + , Grupa: + +
+
+
+
+ + + + + +
+ +
+
+ "; + } else { + echo ''; // Empty cell if person is not in this group + } + ?> + + + + + + +
- - 'temp_szkolenia', 'name' => 'Szkolenia dla nowego członka']; - - // --- Spotkania Columns --- - // Fetch upcoming meetings for each group - $today = date('Y-m-d H:i:s'); - $stmt_meetings = $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); - ?> - -
- - -
- -
- - - - - - - - - - - - - - $col): ?> - - - - - - - - - - - - - - - $col): ?> - - - - - - - - - - -
PersonSpotkaniaInne procesy
-
- - - - - - -
-
-
-
- - - - - , Grupa: - -
-
-
-
- - - - - -
- -
-
- "; - } else { - echo ''; // Empty cell if person is not in this group - } - ?> - -   - -
+
@@ -523,40 +578,176 @@ $bni_groups = $stmt_bni_groups->fetchAll(PDO::FETCH_ASSOC); @@ -700,7 +954,7 @@ document.addEventListener('DOMContentLoaded', function () {