Dashboard
--
From 44d4fa5a6043b8b8241027b041980396ae7fd783 Mon Sep 17 00:00:00 2001
From: Flatlogic Bot Brak zdarzeń.Kroki procesu
$node):
+ if (!isset($node['ui_hints']['title']) || $node['ui_hints']['title'] === '') continue;
$is_current = ($currentNodeId === $nodeId);
$is_completed = isset($visited_nodes[$nodeId]) && !$is_current;
@@ -122,7 +123,7 @@ $instance = $engine->getInstanceByDefId($person_id, $process_definition_id);
+
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'])) ?> diff --git a/_header.php b/_header.php index 5f72031..fc22757 100644 --- a/_header.php +++ b/_header.php @@ -1,7 +1,5 @@ getInstanceByDefId($personId, $processDefinitionId); + if ($instance) { + $engine->deleteInstance($instance['id']); + } +} + // The getOrCreateInstanceByDefId method is now responsible for all checks: // 1. Validating the process definition exists. // 2. Checking if the process is active. @@ -41,4 +49,4 @@ if ($instance) { } else { // This case should not be reached if the engine works as expected, as failures should throw exceptions. throw new Exception("Failed to initialize process for an unknown reason."); -} +} \ No newline at end of file diff --git a/_save_process_definition.php b/_save_process_definition.php index a956073..094a75c 100644 --- a/_save_process_definition.php +++ b/_save_process_definition.php @@ -16,7 +16,7 @@ function validate_definition_json($json) { throw new WorkflowRuleFailedException('Invalid JSON format in definition.'); } - $allowed_statuses = ['none', 'negative', 'in_progress', 'positive']; + $allowed_statuses = ['none', 'negative', 'in_progress', 'positive', 'active', 'processing', 'paused', 'completed', 'terminated']; if (isset($data['nodes'])) { foreach ($data['nodes'] as $node) { @@ -75,8 +75,8 @@ try { } else { // Update existing process $is_active = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 0; - $sql = 'UPDATE process_definitions SET name = ?, code = ?, definition_json = ?, is_active = ? WHERE id = ?'; - $params = [$name, $code, $definition_json, $is_active, $processId]; + $sql = 'UPDATE process_definitions SET name = ?, definition_json = ?, is_active = ? WHERE id = ?'; + $params = [$name, $definition_json, $is_active, $processId]; $message = 'Process updated successfully.'; } diff --git a/cookie.txt b/cookie.txt index 23d0714..abe37fb 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. -127.0.0.1 FALSE / FALSE 0 PHPSESSID abf9a8g0sidv8idojcrr65jetp +localhost FALSE / FALSE 0 PHPSESSID rfo6k0p8l4tpnmgek7dkpkopbl diff --git a/current_definition.json b/current_definition.json new file mode 100644 index 0000000..28672ef --- /dev/null +++ b/current_definition.json @@ -0,0 +1,130 @@ +{ + "start_node_id": "awaiting_call", + "eligibility_rules": [ + { + "type": "person_property_equals", + "params": { + "property": "role", + "value": "guest" + } + } + ], + "nodes": { + "awaiting_call": { + "ui_hints": { + "title": "Follow-up Call", + "status": "active", + "reason": "Awaiting follow-up call with the guest.", + "next_step": "Log the outcome of the call.", + "form_schema": [ + { "name": "call_date", "label": "Call Date", "type": "datetime-local", "default": "now", "required": true }, + { + "name": "outcome_status", + "label": "Call Outcome", + "type": "select", + "required": true, + "options": [ + { "value": "", "label": "-- Select Outcome --" }, + { "value": "no_answer", "label": "No Answer" }, + { "value": "wants_to_join", "label": "Wants to Join" }, + { "value": "declined", "label": "Declined" }, + { "value": "call_later", "label": "Call Later" } + ] + }, + { "name": "note", "label": "Notes", "type": "textarea" }, + { "name": "next_contact_date", "label": "Next Contact Date", "type": "datetime-local", "condition": { "field": "outcome_status", "value": "call_later" }, "required": true } + ] + } + }, + "outcome_router": { "ui_hints": { "status": "processing" } }, + "waiting_for_next_contact": { + "ui_hints": { + "title": "Waiting for Scheduled Call", + "status": "paused", + "reason": "Waiting until the scheduled date for the next call.", + "next_step": "Resume contact on or after the scheduled date." + } + }, + "decide_after_no_answer": { + "ui_hints": { + "title": "No Answer", + "status": "paused", + "reason": "The guest did not answer the call.", + "next_step": "Decide whether to try again or end the process." + } + }, + "end_positive": { "ui_hints": { "title": "Wants to Join", "status": "completed", "reason": "Guest wants to join. New member process started.", "next_step": "" } }, + "end_negative_declined": { "ui_hints": { "title": "Declined", "status": "terminated", "reason": "Guest declined to join.", "next_step": "" } }, + "end_negative_no_answer": { "ui_hints": { "title": "Process Ended", "status": "terminated", "reason": "Process ended after no answer.", "next_step": "" } }, + "end_negative_terminated": { "ui_hints": { "title": "Process Terminated", "status": "terminated", "reason": "Process manually terminated by user.", "next_step": "" } } + }, + "transitions": [ + { + "id": "submit_outcome", + "from": "awaiting_call", + "to": "outcome_router", + "name": "Submit Outcome", + "actions": [ + { "type": "set_data", "params": { "keys": ["call_date", "outcome_status", "note", "next_contact_date"] } } + ] + }, + { + "id": "route_wants_to_join", + "from": "outcome_router", + "to": "end_positive", + "name": "Route to Positive End", + "condition": { "field": "outcome_status", "value": "wants_to_join" }, + "actions": [ + { "type": "start_process", "process_code": "obsluga-przyjecia-nowego-czlonka" } + ] + }, + { + "id": "route_declined", + "from": "outcome_router", + "to": "end_negative_declined", + "name": "Route to Declined", + "condition": { "field": "outcome_status", "value": "declined" } + }, + { + "id": "route_no_answer", + "from": "outcome_router", + "to": "decide_after_no_answer", + "name": "Route to No Answer", + "condition": { "field": "outcome_status", "value": "no_answer" } + }, + { + "id": "route_call_later", + "from": "outcome_router", + "to": "waiting_for_next_contact", + "name": "Route to Call Later", + "condition": { "field": "outcome_status", "value": "call_later" } + }, + { "id": "continue_attempts", "from": "decide_after_no_answer", "to": "awaiting_call", "name": "Try Again" }, + { "id": "end_attempts", "from": "decide_after_no_answer", "to": "end_negative_no_answer", "name": "End Process" }, + { "id": "resume_contact", "from": "waiting_for_next_contact", "to": "awaiting_call", "name": "Resume / Attempt Call" }, + { + "id": "terminate_from_awaiting_call", + "from": "awaiting_call", + "to": "end_negative_terminated", + "name": "Zakończ proces", + "actions": [ { "type": "set_data", "params": { "keys": ["termination_note"] } } ], + "form_schema": [ { "name": "termination_note", "label": "Reason for Termination (optional)", "type": "textarea" } ] + }, + { + "id": "terminate_from_decide", + "from": "decide_after_no_answer", + "to": "end_negative_terminated", + "name": "Zakończ proces", + "actions": [ { "type": "set_data", "params": { "keys": ["termination_note"] } } ], + "form_schema": [ { "name": "termination_note", "label": "Reason for Termination (optional)", "type": "textarea" } ] + }, + { + "id": "terminate_from_waiting", + "from": "waiting_for_next_contact", + "to": "end_negative_terminated", + "name": "Zakończ proces", + "actions": [ { "type": "set_data", "params": { "keys": ["termination_note"] } } ], + "form_schema": [ { "name": "termination_note", "label": "Reason for Termination (optional)", "type": "textarea" } ] + } + ] +} \ No newline at end of file diff --git a/db/migrations/026_simplify_follow_up_ux.php b/db/migrations/026_simplify_follow_up_ux.php new file mode 100644 index 0000000..8b314ad --- /dev/null +++ b/db/migrations/026_simplify_follow_up_ux.php @@ -0,0 +1,11 @@ +prepare("UPDATE process_definitions SET definition_json = :json WHERE id = 4"); +$stmt->execute(['json' => $new_json_definition]); + +echo "Process definition for follow_up (ID: 4) updated successfully."; diff --git a/db/migrations/027_deactivate_sales_pipeline.php b/db/migrations/027_deactivate_sales_pipeline.php new file mode 100644 index 0000000..f6c7133 --- /dev/null +++ b/db/migrations/027_deactivate_sales_pipeline.php @@ -0,0 +1,19 @@ +exec($sql); + echo "Migration successful: Deactivated 'Sales Pipeline' process.\n"; + } catch (PDOException $e) { + die("Migration failed: " . $e->getMessage() . "\n"); + } +} + +if (basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) { + require_once __DIR__ . '/../../db/config.php'; + $pdo = db(); + migrate_027($pdo); +} + diff --git a/db/migrations/028_add_sort_order_to_process_definitions.php b/db/migrations/028_add_sort_order_to_process_definitions.php new file mode 100644 index 0000000..8ba2c38 --- /dev/null +++ b/db/migrations/028_add_sort_order_to_process_definitions.php @@ -0,0 +1,22 @@ +exec($sql); + echo "Migration successful: sort_order column added to process_definitions table.\n"; + } catch (PDOException $e) { + // Ignore if column already exists + if (strpos($e->getMessage(), 'Duplicate column name') === false) { + die("Migration failed: " . $e->getMessage() . "\n"); + } + } +} + +if (basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) { + require_once __DIR__ . '/../../db/config.php'; + $pdo = db(); + migrate_028($pdo); +} + diff --git a/db/migrations/029_set_initial_process_order.php b/db/migrations/029_set_initial_process_order.php new file mode 100644 index 0000000..2bc40c6 --- /dev/null +++ b/db/migrations/029_set_initial_process_order.php @@ -0,0 +1,32 @@ + 10, + 'Follow-up' => 20, + 'Obsługa przyjęcia nowego członka' => 30, + 'Wprowadzenie nowego członka' => 40, + 'Szkolenia dla młodego członka' => 50, + 'Mentoring' => 60, + ]; + + try { + $stmt = $pdo->prepare("UPDATE process_definitions SET sort_order = :sort_order WHERE name = :name"); + + foreach ($process_order as $name => $order) { + $stmt->execute(['sort_order' => $order, 'name' => $name]); + } + + echo "Migration successful: Initial process order set.\n"; + } catch (PDOException $e) { + die("Migration failed: " . $e->getMessage() . "\n"); + } +} + +if (basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) { + require_once __DIR__ . '/../../db/config.php'; + $pdo = db(); + migrate_029($pdo); +} + diff --git a/get_definition.php b/get_definition.php new file mode 100644 index 0000000..6ce665b --- /dev/null +++ b/get_definition.php @@ -0,0 +1,7 @@ +prepare("SELECT definition_json FROM process_definitions WHERE id = 4"); +$stmt->execute(); +$result = $stmt->fetch(PDO::FETCH_ASSOC); +echo $result['definition_json']; \ No newline at end of file diff --git a/get_process_definitions.php b/get_process_definitions.php new file mode 100644 index 0000000..1d9ba4f --- /dev/null +++ b/get_process_definitions.php @@ -0,0 +1,29 @@ +query("SELECT id, code, name, is_active FROM process_definitions ORDER BY id"); + $definitions = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Identify which processes appear on the dashboard + foreach ($definitions as &$def) { + if (!in_array($def['name'], ['Obsluga goscia', 'Przygotowanie spotkania grupy'])) { + $def['on_dashboard'] = 'Yes'; + } else { + $def['on_dashboard'] = 'No'; + } + } + + echo "--- Process Definitions --- +"; + echo str_pad("ID", 5) . str_pad("Code", 40) . str_pad("Name", 40) . str_pad("Active", 10) . str_pad("On Dashboard", 15) . "\n"; + echo str_repeat("-", 110) . "\n"; + foreach ($definitions as $def) { + echo str_pad($def['id'], 5) . str_pad($def['code'], 40) . str_pad($def['name'], 40) . str_pad($def['is_active'] ? 'Yes' : 'No', 10) . str_pad($def['on_dashboard'], 15) . "\n"; + } + +} catch (PDOException $e) { + die("DB ERROR: " . $e->getMessage()); +} + diff --git a/index.php b/index.php index 6ce4853..b147b62 100644 --- a/index.php +++ b/index.php @@ -1,41 +1,50 @@ getDashboardMatrix(); -$people = $matrix['people']; -$instances = $matrix['instances']; -$all_functions = $matrix['all_functions']; -$person_functions_map = $matrix['person_functions_map']; -$bni_groups = $matrix['bni_groups']; - -// Filter out specific process definitions -$processes = array_filter($matrix['definitions'], function($process) { - return !in_array($process['name'], ['Obsluga goscia', 'Przygotowanie spotkania grupy']); -}); +$searchTerm = $_GET['search'] ?? null; +$groupId = isset($_GET['group_id']) && $_GET['group_id'] !== '' ? (int)$_GET['group_id'] : null; +$activeProcessId = isset($_GET['active_process_id']) && $_GET['active_process_id'] !== '' ? (int)$_GET['active_process_id'] : null; +// Handle toggling of the active process filter +if (isset($_GET['active_process_id']) && $_GET['active_process_id'] == @$_SESSION['last_active_process_id']) { + unset($_GET['active_process_id']); + $activeProcessId = null; + $redirectUrl = "index.php"; + $queryParams = []; + if ($groupId) { + $queryParams['group_id'] = $groupId; + } + if ($searchTerm) { + $queryParams['search'] = $searchTerm; + } + if (!empty($queryParams)) { + $redirectUrl .= "?" . http_build_query($queryParams); + } + header("Location: " . $redirectUrl); + exit; +} +$_SESSION['last_active_process_id'] = $activeProcessId; +$matrixData = $workflowEngine->getDashboardMatrix($searchTerm, $groupId, $activeProcessId); +$people = $matrixData['people']; +$processes = $matrixData['definitions']; +$instances = $matrixData['instances']; +$spotkania_cols = $matrixData['spotkania_cols']; +$bni_groups = $matrixData['bni_groups']; +$all_functions = $matrixData['all_functions']; $status_colors = [ - 'completed' => '#28a745', - 'positive' => '#28a745', - 'in_progress' => '#fd7e14', - 'negative' => '#dc3545', - 'error' => '#dc3545', - 'none' => '#808080', - 'not_started' => '#808080', - 'inactive' => '#808080', + 'none' => 'secondary', + 'negative' => 'danger', + 'in_progress' => 'warning', + 'positive' => 'success', ]; - - ?> @@ -45,225 +54,160 @@ $status_colors = [