Wersja bazowa
This commit is contained in:
parent
110c203f6a
commit
9234472371
263
WorkflowEngine.php
Normal file
263
WorkflowEngine.php
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class WorkflowEngine
|
||||||
|
*
|
||||||
|
* Centralny serwis do zarządzania logiką procesów.
|
||||||
|
* Interfejs użytkownika nie powinien bezpośrednio zmieniać statusów ani instancji; wszystkie operacje muszą przechodzić przez ten silnik.
|
||||||
|
*/
|
||||||
|
class WorkflowEngine {
|
||||||
|
|
||||||
|
private $pdo;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->pdo = db();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pobiera wszystkie dane niezbędne dla głównej macierzy pulpitu procesów.
|
||||||
|
*
|
||||||
|
* @return array Tablica zawierająca 'people', 'definitions' i zmapowaną tablicę 'instances'.
|
||||||
|
*/
|
||||||
|
public function getDashboardMatrix(): array {
|
||||||
|
// Get all people (potential assignees)
|
||||||
|
$stmt_people = $this->pdo->prepare("SELECT id, firstName, lastName, companyName, role, email, phone FROM people ORDER BY lastName, firstName");
|
||||||
|
$stmt_people->execute();
|
||||||
|
$people = $stmt_people->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Fetch all process definitions
|
||||||
|
$stmt_defs = $this->pdo->prepare("SELECT id, name FROM process_definitions ORDER BY name");
|
||||||
|
$stmt_defs->execute();
|
||||||
|
$process_definitions = $stmt_defs->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Fetch instances
|
||||||
|
$stmt_instances = $this->pdo->prepare("SELECT * FROM process_instances");
|
||||||
|
$stmt_instances->execute();
|
||||||
|
$instances_data = $stmt_instances->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$instances = [];
|
||||||
|
foreach ($instances_data as $instance) {
|
||||||
|
$instances[$instance['personId']][$instance['processDefinitionId']] = $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'people' => $people,
|
||||||
|
'definitions' => $process_definitions,
|
||||||
|
'instances' => $instances,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rozpoczyna nową instancję procesu dla danej osoby.
|
||||||
|
*
|
||||||
|
* @param string $processCode Unikalny kod definicji procesu.
|
||||||
|
* @param int $personId ID osoby.
|
||||||
|
* @param int $userId ID użytkownika inicjującego akcję.
|
||||||
|
* @return int|null ID nowo utworzonej instancji lub null w przypadku niepowodzenia.
|
||||||
|
*/
|
||||||
|
public function startProcess(string $processCode, int $personId, int $userId): ?int {
|
||||||
|
// 1. Znajdź aktywną definicję procesu po kodzie.
|
||||||
|
// 2. Pobierz start_node_id z definicji.
|
||||||
|
// 3. Utwórz nową instancję procesu ze statusem 'in_progress' i current_node_id.
|
||||||
|
// 4. Utwórz zdarzenie systemowe dla rozpoczęcia procesu.
|
||||||
|
// TODO: Implementacja logiki.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pobiera pojedynczą instancję procesu, tworząc ją, jeśli nie istnieje.
|
||||||
|
*
|
||||||
|
* @param int $personId
|
||||||
|
* @param int $processDefinitionId
|
||||||
|
* @param int $userId Użytkownik inicjujący utworzenie, jeśli to nastąpi.
|
||||||
|
* @return array|null Tablica asocjacyjna z danymi instancji.
|
||||||
|
*/
|
||||||
|
public function getOrCreateInstanceByDefId(int $personId, int $processDefinitionId, int $userId): ?array {
|
||||||
|
$stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE personId = ? AND processDefinitionId = ?");
|
||||||
|
$stmt->execute([$personId, $processDefinitionId]);
|
||||||
|
$instance = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$instance) {
|
||||||
|
// Fetch the process definition to get the initial status
|
||||||
|
$stmt_def = $this->pdo->prepare("SELECT definition_json FROM process_definitions WHERE id = ?");
|
||||||
|
$stmt_def->execute([$processDefinitionId]);
|
||||||
|
$definition_raw = $stmt_def->fetchColumn();
|
||||||
|
$definition = $definition_raw ? json_decode($definition_raw, true) : null;
|
||||||
|
$initial_status = $definition['initial_status'] ?? 'none';
|
||||||
|
|
||||||
|
$stmt_insert = $this->pdo->prepare("INSERT INTO process_instances (personId, processDefinitionId, current_status) VALUES (?, ?, ?)");
|
||||||
|
$stmt_insert->execute([$personId, $processDefinitionId, $initial_status]);
|
||||||
|
$instanceId = $this->pdo->lastInsertId();
|
||||||
|
|
||||||
|
// Utwórz zdarzenie systemowe dla utworzenia instancji
|
||||||
|
$this->addEvent($instanceId, 'system', 'Instancja utworzona.', null, [], $userId);
|
||||||
|
|
||||||
|
// Pobierz ponownie nowo utworzoną instancję
|
||||||
|
$stmt->execute([$personId, $processDefinitionId]);
|
||||||
|
$instance = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dodaje nowe zdarzenie do historii instancji.
|
||||||
|
* To jest wewnętrzna metoda pomocnicza.
|
||||||
|
*/
|
||||||
|
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, event_type, message, node_id, payload_json, createdById) VALUES (?, ?, ?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
$stmt->execute([$instanceId, $eventType, $message, $nodeId, json_encode($payload), $userId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pobiera historię zdarzeń dla danej instancji.
|
||||||
|
*
|
||||||
|
* @param int $instanceId
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
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->execute([$instanceId]);
|
||||||
|
return $stmt_events->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pobiera listę dostępnych przejść z bieżącego węzła instancji.
|
||||||
|
*
|
||||||
|
* @param int $instanceId
|
||||||
|
* @return array Lista dostępnych przejść.
|
||||||
|
*/
|
||||||
|
public function getAvailableTransitions(int $instanceId): array {
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
'SELECT pi.current_status, pd.definition_json '
|
||||||
|
. 'FROM process_instances pi '
|
||||||
|
. 'JOIN process_definitions pd ON pi.processDefinitionId = pd.id '
|
||||||
|
. 'WHERE pi.id = ?'
|
||||||
|
);
|
||||||
|
$stmt->execute([$instanceId]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$result || empty($result['definition_json'])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$definition = json_decode($result['definition_json'], true);
|
||||||
|
if (!$definition || !isset($definition['transitions'])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_status = $result['current_status'];
|
||||||
|
$allowed_transitions = $definition['transitions'][$current_status] ?? [];
|
||||||
|
|
||||||
|
$transitions = [];
|
||||||
|
foreach ($allowed_transitions as $target_status) {
|
||||||
|
$transitions[] = [
|
||||||
|
'id' => 'transition_' . str_replace(' ', '_', strtolower($target_status)), // e.g., transition_in_progress
|
||||||
|
'name' => 'Oznacz jako ' . $target_status, // e.g., Oznacz jako In Progress
|
||||||
|
'target_status' => $target_status
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stosuje przejście do instancji procesu. To jest główna metoda do postępu w przepływie pracy.
|
||||||
|
*
|
||||||
|
* @param int $instanceId ID instancji procesu.
|
||||||
|
* @param string $transitionId ID przejścia do zastosowania (z definition_json).
|
||||||
|
* @param array $inputPayload Dane zebrane od użytkownika dla tego kroku.
|
||||||
|
* @param int $userId ID użytkownika wykonującego akcję.
|
||||||
|
* @return bool True w przypadku sukcesu, false w przypadku porażki.
|
||||||
|
*/
|
||||||
|
public function applyTransition(int $instanceId, string $transitionId, array $inputPayload, int $userId): bool {
|
||||||
|
$this->pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Pobierz instancję i dostępne przejścia
|
||||||
|
$stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE id = ?");
|
||||||
|
$stmt->execute([$instanceId]);
|
||||||
|
$instance = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$instance) {
|
||||||
|
// Instancja nie znaleziona
|
||||||
|
$this->pdo->rollBack();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$availableTransitions = $this->getAvailableTransitions($instanceId);
|
||||||
|
|
||||||
|
// 2. Sprawdź, czy przejście jest dozwolone
|
||||||
|
$selectedTransition = null;
|
||||||
|
if ($transitionId === 'note') { // Specjalny przypadek dodawania notatki
|
||||||
|
$selectedTransition = ['id' => 'note', 'name' => 'Dodano notatkę', 'target_status' => $instance['current_status']];
|
||||||
|
} else {
|
||||||
|
foreach ($availableTransitions as $trans) {
|
||||||
|
if ($trans['id'] === $transitionId) {
|
||||||
|
$selectedTransition = $trans;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$selectedTransition) {
|
||||||
|
// Nieprawidłowe lub niedozwolone przejście
|
||||||
|
$this->pdo->rollBack();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Utwórz zdarzenie
|
||||||
|
$eventType = ($transitionId === 'note') ? 'note' : 'transition_applied';
|
||||||
|
$message = $inputPayload['message'] ?? $selectedTransition['name'];
|
||||||
|
$this->addEvent($instanceId, $eventType, $message, null, $inputPayload, $userId);
|
||||||
|
|
||||||
|
// 4. Zaktualizuj instancję
|
||||||
|
$stmt_update = $this->pdo->prepare(
|
||||||
|
"UPDATE process_instances SET current_status = ?, lastActivityAt = CURRENT_TIMESTAMP WHERE id = ?"
|
||||||
|
);
|
||||||
|
$stmt_update->execute([$selectedTransition['target_status'], $instanceId]);
|
||||||
|
|
||||||
|
$this->pdo->commit();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->pdo->rollBack();
|
||||||
|
error_log("Błąd w applyTransition: " . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Masowa operacja stosowania tego samego przejścia do wielu osób dla danego procesu.
|
||||||
|
*
|
||||||
|
* @param string $processCode
|
||||||
|
* @param array $personIds
|
||||||
|
* @param string $transitionId
|
||||||
|
* @param array $inputPayload
|
||||||
|
* @param int $userId
|
||||||
|
* @return array Podsumowanie wyników (np. ['success' => count, 'failed' => count]).
|
||||||
|
*/
|
||||||
|
public function bulkApplyTransition(string $processCode, array $personIds, string $transitionId, array $inputPayload, int $userId): array {
|
||||||
|
// 1. Upewnij się, że instancje istnieją dla wszystkich osób (użyj ensureInstances).
|
||||||
|
// 2. Przejdź przez pętlę personIds i wywołaj applyTransition dla każdej z nich.
|
||||||
|
// TODO: Implementacja logiki.
|
||||||
|
return ['success' => 0, 'failed' => 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zapewnia, że instancje procesów istnieją dla danego zestawu osób i kodów procesów.
|
||||||
|
* Jeśli instancja brakuje, zostanie utworzona.
|
||||||
|
*
|
||||||
|
* @param array $personIds
|
||||||
|
* @param array $processCodes
|
||||||
|
* @param int $userId
|
||||||
|
* @return array Podsumowanie utworzonych instancji.
|
||||||
|
*/
|
||||||
|
public function ensureInstances(array $personIds, array $processCodes, int $userId): array {
|
||||||
|
// TODO: Implementacja logiki do tworzenia brakujących instancji.
|
||||||
|
return ['created' => 0];
|
||||||
|
}
|
||||||
|
}
|
||||||
32
_add_bni_group.php
Normal file
32
_add_bni_group.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if (isset($_POST['add'])) {
|
||||||
|
$name = $_POST['name'];
|
||||||
|
$city = $_POST['city'] ?? null;
|
||||||
|
$active = isset($_POST['active']) ? 1 : 0;
|
||||||
|
$display_order = $_POST['display_order'] ?? 0;
|
||||||
|
|
||||||
|
if (empty($name)) {
|
||||||
|
$_SESSION['error_message'] = 'Name is required.';
|
||||||
|
header('Location: bni_groups.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO bni_groups (name, city, active, display_order) VALUES (:name, :city, :active, :display_order)");
|
||||||
|
$stmt->bindParam(':name', $name);
|
||||||
|
$stmt->bindParam(':city', $city);
|
||||||
|
$stmt->bindParam(':active', $active, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':display_order', $display_order, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = 'BNI Group added successfully!';
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['error_message'] = 'Error adding BNI group: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: bni_groups.php');
|
||||||
|
exit;
|
||||||
88
_add_calendar_event.php
Normal file
88
_add_calendar_event.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: login.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$title = $_POST['title'] ?? '';
|
||||||
|
$description = $_POST['description'] ?? '';
|
||||||
|
$start_datetime = $_POST['start_datetime'] ?? '';
|
||||||
|
$end_datetime = $_POST['end_datetime'] ?? '';
|
||||||
|
$event_type_id = $_POST['event_type_id'] ?? null;
|
||||||
|
$recurrence = $_POST['recurrence'] ?? '';
|
||||||
|
$recurrence_end_date = $_POST['recurrence_end_date'] ?? '';
|
||||||
|
|
||||||
|
if (empty($recurrence)) {
|
||||||
|
$recurrence = null;
|
||||||
|
}
|
||||||
|
if (empty($recurrence_end_date)) {
|
||||||
|
$recurrence_end_date = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($title) || empty($start_datetime) || empty($end_datetime) || empty($event_type_id)) {
|
||||||
|
header("Location: calendar.php?error=empty_fields");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
// Insert the main event
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO calendar_events (title, description, start_datetime, end_datetime, event_type_id, recurrence, recurrence_end_date) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$title, $description, $start_datetime, $end_datetime, $event_type_id, $recurrence, $recurrence_end_date]);
|
||||||
|
$parent_event_id = $pdo->lastInsertId();
|
||||||
|
|
||||||
|
if ($recurrence && !empty($recurrence_end_date)) {
|
||||||
|
$start_date = new DateTime($start_datetime);
|
||||||
|
$end_date = new DateTime($end_datetime);
|
||||||
|
$recurrence_end = new DateTime($recurrence_end_date);
|
||||||
|
$interval_spec = '';
|
||||||
|
|
||||||
|
switch ($recurrence) {
|
||||||
|
case 'daily':
|
||||||
|
$interval_spec = 'P1D';
|
||||||
|
break;
|
||||||
|
case 'weekly':
|
||||||
|
$interval_spec = 'P1W';
|
||||||
|
break;
|
||||||
|
case 'monthly':
|
||||||
|
$interval_spec = 'P1M';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($interval_spec) {
|
||||||
|
$interval = new DateInterval($interval_spec);
|
||||||
|
$period_start = clone $start_date;
|
||||||
|
$period_start->add($interval);
|
||||||
|
|
||||||
|
$period = new DatePeriod($period_start, $interval, $recurrence_end);
|
||||||
|
|
||||||
|
$stmt_recur = $pdo->prepare("INSERT INTO calendar_events (title, description, start_datetime, end_datetime, event_type_id, parent_event_id) VALUES (?, ?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
|
foreach ($period as $date) {
|
||||||
|
$new_start_datetime = $date->format('Y-m-d H:i:s');
|
||||||
|
$end_date_clone = clone $date;
|
||||||
|
$new_end_datetime = $end_date_clone->add($start_date->diff($end_date))->format('Y-m-d H:i:s');
|
||||||
|
$stmt_recur->execute([$title, $description, $new_start_datetime, $new_end_datetime, $event_type_id, $parent_event_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
header("Location: calendar.php");
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
error_log($e->getMessage());
|
||||||
|
header("Location: calendar.php?error=db_error");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
_add_event_type.php
Normal file
18
_add_event_type.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (isset($_POST['add'])) {
|
||||||
|
$name = $_POST['name'];
|
||||||
|
$color = $_POST['color'];
|
||||||
|
$display_order = $_POST['display_order'];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO event_types (name, color, display_order) VALUES (?, ?, ?)");
|
||||||
|
$stmt->execute([$name, $color, $display_order]);
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
$_SESSION['success_message'] = 'Event type added successfully.';
|
||||||
|
header('Location: event_types.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
21
_add_function.php
Normal file
21
_add_function.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
include 'db/config.php';
|
||||||
|
|
||||||
|
if (isset($_POST['name'])) {
|
||||||
|
$name = trim($_POST['name']);
|
||||||
|
$display_order = trim($_POST['display_order']);
|
||||||
|
if (!empty($name)) {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("functions (name, display_order) VALUES (:name, :display_order)");
|
||||||
|
$stmt->execute(['name' => $name, 'display_order' => $display_order]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: roles.php');
|
||||||
|
exit();
|
||||||
31
_add_process_event.php
Normal file
31
_add_process_event.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(401);
|
||||||
|
die('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$instanceId = $_POST['instanceId'] ?? null;
|
||||||
|
$eventType = $_POST['eventType'] ?? null;
|
||||||
|
$description = $_POST['description'] ?? null;
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
if (!$instanceId || !$eventType) {
|
||||||
|
http_response_code(400);
|
||||||
|
die('Missing parameters');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the event
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO process_events (processInstanceId, eventType, description, createdBy) VALUES (?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$instanceId, $eventType, $description, $userId]);
|
||||||
|
|
||||||
|
// Update the last activity time for the instance
|
||||||
|
$stmt_update = $pdo->prepare("UPDATE process_instances SET lastActivityAt = NOW() WHERE id = ?");
|
||||||
|
$stmt_update->execute([$instanceId]);
|
||||||
|
|
||||||
|
// Redirect back to the dashboard
|
||||||
|
header("Location: process_dashboard.php");
|
||||||
|
exit;
|
||||||
43
_apply_transition.php
Normal file
43
_apply_transition.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: process_dashboard.php');
|
||||||
|
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');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$instanceId = (int)$_POST['instanceId'];
|
||||||
|
$transitionId = $_POST['transitionId'];
|
||||||
|
$userId = $_SESSION['user_id'] ?? null;
|
||||||
|
$payload = $_POST['payload'] ?? null;
|
||||||
|
|
||||||
|
if (!$userId) {
|
||||||
|
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Błąd: Sesja wygasła.'];
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$engine = new WorkflowEngine();
|
||||||
|
$success = $engine->applyTransition($instanceId, $transitionId, $payload, $userId);
|
||||||
|
|
||||||
|
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.'];
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("Error applying transition: " . $e->getMessage());
|
||||||
|
$_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Wystąpił krytyczny błąd: ' . $e->getMessage()];
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: process_dashboard.php');
|
||||||
|
exit;
|
||||||
53
_bulk_add_event.php
Normal file
53
_bulk_add_event.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(401);
|
||||||
|
die('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$person_ids = json_decode($_POST['person_ids'] ?? '[]');
|
||||||
|
$process_id = $_POST['process_id'] ?? null;
|
||||||
|
$message = $_POST['description'] ?? null; // The form sends 'description'
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
if (empty($person_ids) || !$process_id || !$message) {
|
||||||
|
http_response_code(400);
|
||||||
|
die('Missing parameters');
|
||||||
|
}
|
||||||
|
|
||||||
|
$placeholders = implode(',', array_fill(0, count($person_ids), '?'));
|
||||||
|
|
||||||
|
// Get all relevant instance IDs
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM process_instances WHERE processDefinitionId = ? AND personId IN ($placeholders)");
|
||||||
|
$params = array_merge([$process_id], $person_ids);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$instance_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
if (!empty($instance_ids)) {
|
||||||
|
$instance_placeholders = implode(',', array_fill(0, count($instance_ids), '?'));
|
||||||
|
|
||||||
|
// Update last activity
|
||||||
|
$stmt_update = $pdo->prepare("UPDATE process_instances SET lastActivityAt = NOW() WHERE id IN ($instance_placeholders)");
|
||||||
|
$stmt_update->execute($instance_ids);
|
||||||
|
|
||||||
|
// Bulk insert events
|
||||||
|
$event_sql = "INSERT INTO process_events (processInstanceId, event_type, message, createdById) VALUES ";
|
||||||
|
$event_rows = [];
|
||||||
|
$event_params = [];
|
||||||
|
foreach($instance_ids as $instance_id) {
|
||||||
|
$event_rows[] = "(?, 'note', ?, ?)";
|
||||||
|
$event_params[] = $instance_id;
|
||||||
|
$event_params[] = $message;
|
||||||
|
$event_params[] = $userId;
|
||||||
|
}
|
||||||
|
$event_sql .= implode(', ', $event_rows);
|
||||||
|
$stmt_event = $pdo->prepare($event_sql);
|
||||||
|
$stmt_event->execute($event_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['flash_message'] = "Bulk event addition completed.";
|
||||||
|
header('Location: process_dashboard.php');
|
||||||
|
exit;
|
||||||
33
_bulk_init_instances.php
Normal file
33
_bulk_init_instances.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(401);
|
||||||
|
die('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$person_ids = json_decode($_POST['person_ids'] ?? '[]');
|
||||||
|
$process_id = $_POST['process_id'] ?? null;
|
||||||
|
|
||||||
|
if (empty($person_ids) || !$process_id) {
|
||||||
|
http_response_code(400);
|
||||||
|
die('Missing parameters');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = "INSERT IGNORE INTO process_instances (personId, processDefinitionId, current_status) VALUES ";
|
||||||
|
$rows = [];
|
||||||
|
$params = [];
|
||||||
|
foreach($person_ids as $person_id) {
|
||||||
|
$rows[] = "(?, ?, 'none')";
|
||||||
|
$params[] = $person_id;
|
||||||
|
$params[] = $process_id;
|
||||||
|
}
|
||||||
|
$sql .= implode(', ', $rows);
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
|
||||||
|
$_SESSION['flash_message'] = "Bulk initialization completed.";
|
||||||
|
header('Location: process_dashboard.php');
|
||||||
|
exit;
|
||||||
54
_bulk_update_status.php
Normal file
54
_bulk_update_status.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(401);
|
||||||
|
die('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$person_ids = json_decode($_POST['person_ids'] ?? '[]');
|
||||||
|
$process_id = $_POST['process_id'] ?? null;
|
||||||
|
$status = $_POST['status'] ?? null;
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
if (empty($person_ids) || !$process_id || !$status) {
|
||||||
|
http_response_code(400);
|
||||||
|
die('Missing parameters');
|
||||||
|
}
|
||||||
|
|
||||||
|
$placeholders = implode(',', array_fill(0, count($person_ids), '?'));
|
||||||
|
|
||||||
|
// Get all relevant instance IDs
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM process_instances WHERE processDefinitionId = ? AND personId IN ($placeholders)");
|
||||||
|
$params = array_merge([$process_id], $person_ids);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$instance_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
if (!empty($instance_ids)) {
|
||||||
|
$instance_placeholders = implode(',', array_fill(0, count($instance_ids), '?'));
|
||||||
|
|
||||||
|
// Update statuses
|
||||||
|
$stmt_update = $pdo->prepare("UPDATE process_instances SET current_status = ?, lastActivityAt = NOW() WHERE id IN ($instance_placeholders)");
|
||||||
|
$stmt_update->execute(array_merge([$status], $instance_ids));
|
||||||
|
|
||||||
|
// Bulk insert events
|
||||||
|
$event_sql = "INSERT INTO process_events (processInstanceId, event_type, message, createdById) VALUES ";
|
||||||
|
$event_rows = [];
|
||||||
|
$event_params = [];
|
||||||
|
$message = "Status changed to $status";
|
||||||
|
foreach($instance_ids as $instance_id) {
|
||||||
|
$event_rows[] = "(?, 'status_change', ?, ?)";
|
||||||
|
$event_params[] = $instance_id;
|
||||||
|
$event_params[] = $message;
|
||||||
|
$event_params[] = $userId;
|
||||||
|
}
|
||||||
|
$event_sql .= implode(', ', $event_rows);
|
||||||
|
$stmt_event = $pdo->prepare($event_sql);
|
||||||
|
$stmt_event->execute($event_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['flash_message'] = "Bulk status update completed.";
|
||||||
|
header('Location: process_dashboard.php');
|
||||||
|
exit;
|
||||||
53
_create_person.php
Normal file
53
_create_person.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$firstName = $_POST['firstName'];
|
||||||
|
$lastName = $_POST['lastName'];
|
||||||
|
$email = $_POST['email'];
|
||||||
|
$password = $_POST['password'];
|
||||||
|
$companyName = $_POST['companyName'] ?? null;
|
||||||
|
$phone = $_POST['phone'] ?? null;
|
||||||
|
$role = $_POST['role'] ?? 'członek'; // Default to 'członek'
|
||||||
|
$functions = isset($_POST['functions']) ? $_POST['functions'] : [];
|
||||||
|
|
||||||
|
if (empty($firstName) || empty($lastName) || empty($email) || empty($password)) {
|
||||||
|
$_SESSION['error_message'] = 'Imię, nazwisko, email i hasło są wymagane.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Insert person details
|
||||||
|
$sql = 'INSERT INTO people (firstName, lastName, email, password, companyName, phone, role) VALUES (?, ?, ?, ?, ?, ?, ?)';
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$firstName, $lastName, $email, password_hash($password, PASSWORD_DEFAULT), $companyName, $phone, $role]);
|
||||||
|
$personId = $pdo->lastInsertId();
|
||||||
|
|
||||||
|
// Assign functions
|
||||||
|
if (!empty($functions)) {
|
||||||
|
$sql = "INSERT INTO user_functions (user_id, function_id) VALUES (?, ?)";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
foreach ($functions as $functionId) {
|
||||||
|
$stmt->execute([$personId, $functionId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = 'Osoba dodana pomyślnie.';
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log('Create failed: ' . $e->getMessage());
|
||||||
|
if ($e->errorInfo[1] == 1062) {
|
||||||
|
$_SESSION['error_message'] = 'Błąd: Konto z tym adresem email już istnieje.';
|
||||||
|
} else {
|
||||||
|
$_SESSION['error_message'] = 'Błąd podczas dodawania osoby.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
||||||
20
_delete_bni_group.php
Normal file
20
_delete_bni_group.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$id = $_GET['id'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM bni_groups WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = 'BNI Group deleted successfully!';
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['error_message'] = 'Error deleting BNI group: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: bni_groups.php');
|
||||||
|
exit;
|
||||||
48
_delete_calendar_event.php
Normal file
48
_delete_calendar_event.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: login.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$event_id = $_GET['id'];
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
// Check if the event is a parent event
|
||||||
|
$stmt = $pdo->prepare("SELECT parent_event_id FROM calendar_events WHERE id = ?");
|
||||||
|
$stmt->execute([$event_id]);
|
||||||
|
$event = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($event) {
|
||||||
|
if ($event['parent_event_id'] === null) {
|
||||||
|
// It's a parent event, delete it and all its children
|
||||||
|
$stmt_delete_children = $pdo->prepare("DELETE FROM calendar_events WHERE parent_event_id = ?");
|
||||||
|
$stmt_delete_children->execute([$event_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the event itself
|
||||||
|
$stmt_delete = $pdo->prepare("DELETE FROM calendar_events WHERE id = ?");
|
||||||
|
$stmt_delete->execute([$event_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
header("Location: calendar.php");
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
error_log($e->getMessage());
|
||||||
|
header("Location: calendar.php?error=db_error");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
header("Location: calendar.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
16
_delete_event_type.php
Normal file
16
_delete_event_type.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$id = $_GET['id'];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM event_types WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
$_SESSION['success_message'] = 'Event type deleted successfully.';
|
||||||
|
header('Location: event_types.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
21
_delete_function.php
Normal file
21
_delete_function.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['user_id']) || !isset($_GET['id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
include 'db/config.php';
|
||||||
|
|
||||||
|
$id = $_GET['id'];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM functions WHERE id = :id");
|
||||||
|
$stmt->execute(['id' => $id]);
|
||||||
|
|
||||||
|
// Optional: Also delete user_functions associated with this function
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM user_functions WHERE function_id = :function_id");
|
||||||
|
$stmt->execute(['function_id' => $id]);
|
||||||
|
|
||||||
|
header('Location: functions.php');
|
||||||
|
exit();
|
||||||
16
_delete_person.php
Normal file
16
_delete_person.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$id = $_GET['id'];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM people WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = 'Osoba usunięta pomyślnie.';
|
||||||
|
header('Location: persons.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
28
_footer.php
Normal file
28
_footer.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!-- Bootstrap Bundle with Popper -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<!-- jQuery -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||||
|
<!-- jQuery UI -->
|
||||||
|
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
|
||||||
|
|
||||||
|
<!-- Custom JS -->
|
||||||
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const sidebarLinks = document.querySelectorAll('#sidebar .nav-link');
|
||||||
|
const currentPath = window.location.pathname.split('/').pop();
|
||||||
|
|
||||||
|
sidebarLinks.forEach(link => {
|
||||||
|
const linkPath = link.getAttribute('href').split('/').pop();
|
||||||
|
if (linkPath === currentPath) {
|
||||||
|
link.classList.add('active');
|
||||||
|
} else {
|
||||||
|
link.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
26
_get_event_details.php
Normal file
26
_get_event_details.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header("HTTP/1.1 401 Unauthorized");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$event_id = $_GET['id'];
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT c.*, t.name as type_name FROM calendar_events c LEFT JOIN event_types t ON c.event_type_id = t.id WHERE c.id = ?");
|
||||||
|
$stmt->execute([$event_id]);
|
||||||
|
$event = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($event) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($event);
|
||||||
|
} else {
|
||||||
|
header("HTTP/1.1 404 Not Found");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
header("HTTP/1.1 400 Bad Request");
|
||||||
|
}
|
||||||
88
_get_instance_details.php
Normal file
88
_get_instance_details.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'WorkflowEngine.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(401);
|
||||||
|
die('Brak autoryzacji');
|
||||||
|
}
|
||||||
|
|
||||||
|
$personId = $_GET['personId'] ?? null;
|
||||||
|
$processDefinitionId = $_GET['processId'] ?? null; // Pulpit wysyła processId, który jest ID definicji
|
||||||
|
|
||||||
|
if (!$personId || !$processDefinitionId) {
|
||||||
|
http_response_code(400);
|
||||||
|
die('Brakujące parametry');
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
$engine = new WorkflowEngine();
|
||||||
|
|
||||||
|
// 1. Pobierz lub utwórz instancję
|
||||||
|
$instance = $engine->getOrCreateInstanceByDefId($personId, $processDefinitionId, $userId);
|
||||||
|
if (!$instance) {
|
||||||
|
http_response_code(500);
|
||||||
|
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();
|
||||||
|
$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->execute([$processDefinitionId]);
|
||||||
|
$process = $stmt_process->fetch();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h4><?= htmlspecialchars($person['firstName'] . ' ' . $person['lastName']) ?> - <?= htmlspecialchars($process['name']) ?></h4>
|
||||||
|
<p>Status: <span class="badge bg-secondary"><?= htmlspecialchars($instance['current_status']) ?></span></p>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h5>Wykonaj akcję</h5>
|
||||||
|
<form action="_apply_transition.php" method="post">
|
||||||
|
<input type="hidden" name="instanceId" value="<?= $instanceId ?>">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="transitionSelect" class="form-label">Akcja</label>
|
||||||
|
<select name="transitionId" id="transitionSelect" class="form-select" required>
|
||||||
|
<option value="" disabled selected>-- Wybierz akcję --</option>
|
||||||
|
<?php foreach ($availableTransitions as $transition): ?>
|
||||||
|
<option value="<?= $transition['id'] ?>"><?= htmlspecialchars($transition['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<option value="note">Dodaj notatkę</option> <!-- Specjalny przypadek -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="payloadMessage" class="form-label">Notatka / Wiadomość (opcjonalnie)</label>
|
||||||
|
<textarea name="payload[message]" id="payloadMessage" class="form-control" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Zatwierdź</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h5>Historia</h5>
|
||||||
|
<?php if (empty($events)): ?>
|
||||||
|
<p>Brak zdarzeń.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<ul class="list-group">
|
||||||
|
<?php foreach ($events as $event): ?>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<strong><?= htmlspecialchars(ucfirst(str_replace('_', ' ', $event['event_type']))) ?></strong>
|
||||||
|
<?php if (!empty($event['message'])):
|
||||||
|
$payload = json_decode($event['payload_json'], true);
|
||||||
|
$message = $payload['message'] ?? $event['message'];
|
||||||
|
?>
|
||||||
|
<p class="mb-1"><?= htmlspecialchars($message) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<small class="text-muted">Przez <?= htmlspecialchars($event['firstName'] . ' ' . $event['lastName']) ?> dnia <?= date('d.m.Y, H:i', strtotime($event['createdAt'])) ?></small>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
<?php endif; ?>
|
||||||
32
_get_person_details.php
Normal file
32
_get_person_details.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (isset($_GET['id'])) {
|
||||||
|
$person_id = $_GET['id'];
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Fetch person details
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM people WHERE id = ?");
|
||||||
|
$stmt->execute([$person_id]);
|
||||||
|
$person = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Fetch all functions
|
||||||
|
$stmt = $pdo->query("SELECT id, name FROM functions ORDER BY display_order");
|
||||||
|
$all_functions = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Fetch person's functions
|
||||||
|
$stmt = $pdo->prepare("SELECT function_id FROM user_functions WHERE user_id = ?");
|
||||||
|
$stmt->execute([$person_id]);
|
||||||
|
$person_functions = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
'person' => $person,
|
||||||
|
'all_functions' => $all_functions,
|
||||||
|
'person_functions' => $person_functions
|
||||||
|
];
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($response);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
44
_header.php
Normal file
44
_header.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title><?php echo getenv('PROJECT_NAME') ?: 'My App'; ?> - Dashboard</title>
|
||||||
|
<meta name="description" content="<?php echo getenv('PROJECT_DESCRIPTION') ?: 'A modern web application.'; ?>">
|
||||||
|
|
||||||
|
<!-- Bootstrap CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- Bootstrap Icons -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
|
|
||||||
|
<!-- Google Fonts -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Custom CSS -->
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
|
||||||
|
<!-- OG Meta Tags -->
|
||||||
|
<meta property="og:title" content="<?php echo getenv('PROJECT_NAME') ?: 'My App'; ?>">
|
||||||
|
<meta property="og:description" content="<?php echo getenv('PROJECT_DESCRIPTION') ?: 'A modern web application.'; ?>">
|
||||||
|
<meta property="og:image" content="<?php echo getenv('PROJECT_IMAGE_URL') ?: 'https://via.placeholder.com/1200x630.png?text=Visit+My+App'; ?>">
|
||||||
|
<meta property="og:url" content="<?php echo (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]"; ?>">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
|
||||||
|
<!-- Twitter Card -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="<?php echo getenv('PROJECT_NAME') ?: 'My App'; ?>">
|
||||||
|
<meta name="twitter:description" content="<?php echo getenv('PROJECT_DESCRIPTION') ?: 'A modern web application.'; ?>">
|
||||||
|
<meta name="twitter:image" content="<?php echo getenv('PROJECT_IMAGE_URL') ?: 'https://via.placeholder.com/1200x630.png?text=Visit+My+App'; ?>">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
37
_init_instances.php
Normal file
37
_init_instances.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Get all active people
|
||||||
|
$stmt_people = $pdo->prepare("SELECT id FROM people WHERE active = 1");
|
||||||
|
$stmt_people->execute();
|
||||||
|
$people = $stmt_people->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
// Get all active process definitions
|
||||||
|
$stmt_processes = $pdo->prepare("SELECT id FROM process_definitions WHERE is_active = 1");
|
||||||
|
$stmt_processes->execute();
|
||||||
|
$processes = $stmt_processes->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
$insert_stmt = $pdo->prepare("INSERT IGNORE INTO process_instances (personId, processDefinitionId, current_status) VALUES (?, ?, 'none')");
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
foreach ($people as $personId) {
|
||||||
|
foreach ($processes as $processId) {
|
||||||
|
$insert_stmt->execute([$personId, $processId]);
|
||||||
|
if ($insert_stmt->rowCount() > 0) {
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['flash_message'] = "Initialized $count new process instances.";
|
||||||
|
|
||||||
|
header("Location: process_dashboard.php"); // Redirect to the main dashboard
|
||||||
|
exit;
|
||||||
16
_navbar.php
Normal file
16
_navbar.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
|
||||||
|
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#"><?php echo getenv('PROJECT_NAME') ?: 'My App'; ?></a>
|
||||||
|
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="navbar-nav flex-row px-3">
|
||||||
|
<?php if (isset($_SESSION['user_name'])): ?>
|
||||||
|
<li class="nav-item text-nowrap">
|
||||||
|
<span class="nav-link">Witaj, <?= htmlspecialchars($_SESSION['user_name']) ?></span>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
<li class="nav-item text-nowrap ms-2">
|
||||||
|
<a class="nav-link" href="logout.php">Wyloguj</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
49
_save_process_definition.php
Normal file
49
_save_process_definition.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$processId = $_POST['process_id'] ?? null;
|
||||||
|
$name = $_POST['name'];
|
||||||
|
$definition_json = $_POST['definition_json'];
|
||||||
|
|
||||||
|
if (empty($name)) {
|
||||||
|
die('Process name is required.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate JSON
|
||||||
|
if (!empty($definition_json)) {
|
||||||
|
json_decode($definition_json);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
die('Invalid JSON format in definition.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
if (empty($processId)) {
|
||||||
|
// Create new process
|
||||||
|
$sql = 'INSERT INTO process_definitions (name, definition_json) VALUES (?, ?)';
|
||||||
|
$params = [$name, $definition_json];
|
||||||
|
$message = 'Process created successfully.';
|
||||||
|
} else {
|
||||||
|
// Update existing process
|
||||||
|
$sql = 'UPDATE process_definitions SET name = ?, definition_json = ? WHERE id = ?';
|
||||||
|
$params = [$name, $definition_json, $processId];
|
||||||
|
$message = 'Process updated successfully.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
$_SESSION['success_message'] = $message;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log('Save process definition failed: ' . $e->getMessage());
|
||||||
|
die('Save process definition failed. Please check the logs.');
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: process_definitions.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
56
_sidebar.php
Normal file
56
_sidebar.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php $current_page = basename($_SERVER['PHP_SELF']); ?>
|
||||||
|
<nav id="sidebar" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
|
||||||
|
<div class="position-sticky pt-3">
|
||||||
|
<ul class="nav flex-column">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= ($current_page == 'index.php' || $current_page == '') ? 'active' : '' ?>" aria-current="page" href="/">
|
||||||
|
<i class="bi bi-kanban"></i>
|
||||||
|
<span class="nav-link-text">Pulpit procesów</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= ($current_page == 'calendar.php') ? 'active' : '' ?>" href="calendar.php">
|
||||||
|
<i class="bi bi-calendar-event"></i>
|
||||||
|
<span class="nav-link-text">Kalendarz</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link collapsed" href="#settings-submenu" data-bs-toggle="collapse" aria-expanded="false">
|
||||||
|
<i class="bi bi-gear"></i>
|
||||||
|
<span class="nav-link-text">Ustawienia</span>
|
||||||
|
</a>
|
||||||
|
<ul class="nav flex-column collapse" id="settings-submenu" data-bs-parent="#sidebar">
|
||||||
|
<li class="nav-item submenu-item">
|
||||||
|
<a class="nav-link <?= ($current_page == 'event_types.php') ? 'active' : '' ?>" href="event_types.php">
|
||||||
|
<i class="bi bi-tags"></i>
|
||||||
|
<span class="nav-link-text">Typy zdarzeń</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item submenu-item">
|
||||||
|
<a class="nav-link <?= ($current_page == 'bni_groups.php') ? 'active' : '' ?>" href="bni_groups.php">
|
||||||
|
<i class="bi bi-people"></i>
|
||||||
|
<span class="nav-link-text">Grupy BNI</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item submenu-item">
|
||||||
|
<a class="nav-link <?= ($current_page == 'functions.php') ? 'active' : '' ?>" href="functions.php">
|
||||||
|
<i class="bi bi-person-rolodex"></i>
|
||||||
|
<span class="nav-link-text">Funkcje</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin'): ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= ($current_page == 'process_definitions.php') ? 'active' : '' ?>" href="process_definitions.php">
|
||||||
|
<i class="bi bi-diagram-3"></i>
|
||||||
|
<span class="nav-link-text">Definicje procesów</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
34
_update_bni_group.php
Normal file
34
_update_bni_group.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if (isset($_POST['edit'])) {
|
||||||
|
$id = $_POST['id'];
|
||||||
|
$name = $_POST['name'];
|
||||||
|
$city = $_POST['city'] ?? null;
|
||||||
|
$active = isset($_POST['active']) ? 1 : 0;
|
||||||
|
$display_order = $_POST['display_order'] ?? 0;
|
||||||
|
|
||||||
|
if (empty($name) || empty($id)) {
|
||||||
|
$_SESSION['error_message'] = 'Name and ID are required.';
|
||||||
|
header('Location: bni_groups.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("UPDATE bni_groups SET name = :name, city = :city, active = :active, display_order = :display_order WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':name', $name);
|
||||||
|
$stmt->bindParam(':city', $city);
|
||||||
|
$stmt->bindParam(':active', $active, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':display_order', $display_order, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = 'BNI Group updated successfully!';
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['error_message'] = 'Error updating BNI group: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: bni_groups.php');
|
||||||
|
exit;
|
||||||
67
_update_bni_group_order.php
Normal file
67
_update_bni_group_order.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (isset($_POST['order']) && is_array($_POST['order'])) {
|
||||||
|
$ordered_ids = $_POST['order'];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($ordered_ids as $index => $id) {
|
||||||
|
$sql = "UPDATE bni_groups SET display_order = ? WHERE id = ?";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$index + 1, $id]);
|
||||||
|
}
|
||||||
|
$pdo->commit();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Order updated successfully.']);
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Error updating display order: ' . $e->getMessage()]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for old form submission, though it's being deprecated
|
||||||
|
if (isset($_POST['ids']) && isset($_POST['display_order'])) {
|
||||||
|
$ids = $_POST['ids'];
|
||||||
|
$display_orders = $_POST['display_order'];
|
||||||
|
|
||||||
|
if (count($ids) !== count($display_orders)) {
|
||||||
|
$_SESSION['error_message'] = "Something went wrong. Please try again.";
|
||||||
|
header("Location: bni_groups.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
for ($i = 0; $i < count($ids); $i++) {
|
||||||
|
$sql = "UPDATE bni_groups SET display_order = ? WHERE id = ?";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$display_orders[$i], $ids[$i]]);
|
||||||
|
}
|
||||||
|
$pdo->commit();
|
||||||
|
$_SESSION['success_message'] = "Display order updated successfully.";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
$_SESSION['error_message'] = "Error updating display order: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: bni_groups.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(400);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid request.']);
|
||||||
40
_update_calendar_event.php
Normal file
40
_update_calendar_event.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: login.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$event_id = $_POST['event_id'] ?? null;
|
||||||
|
$title = $_POST['title'] ?? '';
|
||||||
|
$description = $_POST['description'] ?? '';
|
||||||
|
$start_datetime = $_POST['start_datetime'] ?? '';
|
||||||
|
$end_datetime = $_POST['end_datetime'] ?? '';
|
||||||
|
$event_type_id = $_POST['event_type_id'] ?? null;
|
||||||
|
|
||||||
|
// For now, we don't support updating recurring events.
|
||||||
|
// This will be implemented in the future.
|
||||||
|
|
||||||
|
if (empty($event_id) || empty($title) || empty($start_datetime) || empty($end_datetime) || empty($event_type_id)) {
|
||||||
|
header("Location: calendar.php?error=empty_fields");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("UPDATE calendar_events SET title = ?, description = ?, start_datetime = ?, end_datetime = ?, event_type_id = ? WHERE id = ?");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt->execute([$title, $description, $start_datetime, $end_datetime, $event_type_id, $event_id]);
|
||||||
|
header("Location: calendar.php");
|
||||||
|
exit();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Handle database error
|
||||||
|
error_log($e->getMessage());
|
||||||
|
header("Location: calendar.php?error=db_error");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
19
_update_event_type.php
Normal file
19
_update_event_type.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (isset($_POST['edit'])) {
|
||||||
|
$id = $_POST['id'];
|
||||||
|
$name = $_POST['name'];
|
||||||
|
$color = $_POST['color'];
|
||||||
|
$display_order = $_POST['display_order'];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("UPDATE event_types SET name = ?, color = ?, display_order = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$name, $color, $display_order, $id]);
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
$_SESSION['success_message'] = 'Event type updated successfully.';
|
||||||
|
header('Location: event_types.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
67
_update_event_type_order.php
Normal file
67
_update_event_type_order.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (isset($_POST['order']) && is_array($_POST['order'])) {
|
||||||
|
$ordered_ids = $_POST['order'];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($ordered_ids as $index => $id) {
|
||||||
|
$sql = "UPDATE event_types SET display_order = ? WHERE id = ?";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$index + 1, $id]);
|
||||||
|
}
|
||||||
|
$pdo->commit();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Order updated successfully.']);
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Error updating display order: ' . $e->getMessage()]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for old form submission, though it's being deprecated
|
||||||
|
if (isset($_POST['ids']) && isset($_POST['display_order'])) {
|
||||||
|
$ids = $_POST['ids'];
|
||||||
|
$display_orders = $_POST['display_order'];
|
||||||
|
|
||||||
|
if (count($ids) !== count($display_orders)) {
|
||||||
|
$_SESSION['error_message'] = "Something went wrong. Please try again.";
|
||||||
|
header("Location: event_types.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
for ($i = 0; $i < count($ids); $i++) {
|
||||||
|
$sql = "UPDATE event_types SET display_order = ? WHERE id = ?";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$display_orders[$i], $ids[$i]]);
|
||||||
|
}
|
||||||
|
$pdo->commit();
|
||||||
|
$_SESSION['success_message'] = "Display order updated successfully.";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
$_SESSION['error_message'] = "Error updating display order: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: event_types.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(400);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid request.']);
|
||||||
23
_update_function.php
Normal file
23
_update_function.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
include 'db/config.php';
|
||||||
|
|
||||||
|
if (isset($_POST['id'], $_POST['name'])) {
|
||||||
|
$id = $_POST['id'];
|
||||||
|
$name = trim($_POST['name']);
|
||||||
|
$display_order = trim($_POST['display_order']);
|
||||||
|
|
||||||
|
if (!empty($name)) {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("UPDATE functions SET name = :name, display_order = :display_order WHERE id = :id");
|
||||||
|
$stmt->execute(['name' => $name, 'display_order' => $display_order, 'id' => $id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: roles.php');
|
||||||
|
exit();
|
||||||
67
_update_function_order.php
Normal file
67
_update_function_order.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (isset($_POST['order']) && is_array($_POST['order'])) {
|
||||||
|
$ordered_ids = $_POST['order'];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($ordered_ids as $index => $id) {
|
||||||
|
$sql = "UPDATE functions SET display_order = ? WHERE id = ?";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$index + 1, $id]);
|
||||||
|
}
|
||||||
|
$pdo->commit();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Order updated successfully.']);
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Error updating display order: ' . $e->getMessage()]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for old form submission, though it's being deprecated
|
||||||
|
if (isset($_POST['ids']) && isset($_POST['display_order'])) {
|
||||||
|
$ids = $_POST['ids'];
|
||||||
|
$display_orders = $_POST['display_order'];
|
||||||
|
|
||||||
|
if (count($ids) !== count($display_orders)) {
|
||||||
|
$_SESSION['error_message'] = "Something went wrong. Please try again.";
|
||||||
|
header("Location: functions.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
for ($i = 0; $i < count($ids); $i++) {
|
||||||
|
$sql = "UPDATE functions SET display_order = ? WHERE id = ?";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$display_orders[$i], $ids[$i]]);
|
||||||
|
}
|
||||||
|
$pdo->commit();
|
||||||
|
$_SESSION['success_message'] = "Display order updated successfully.";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
$_SESSION['error_message'] = "Error updating display order: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: functions.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(400);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid request.']);
|
||||||
30
_update_instance_status.php
Normal file
30
_update_instance_status.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(401);
|
||||||
|
die('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$instanceId = $_POST['instanceId'] ?? null;
|
||||||
|
$status = $_POST['status'] ?? null;
|
||||||
|
$userId = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
if (!$instanceId || !$status) {
|
||||||
|
http_response_code(400);
|
||||||
|
die('Missing parameters');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update instance status and last activity time
|
||||||
|
$stmt = $pdo->prepare("UPDATE process_instances SET status = ?, lastActivityAt = NOW() WHERE id = ?");
|
||||||
|
$stmt->execute([$status, $instanceId]);
|
||||||
|
|
||||||
|
// Create a status change event
|
||||||
|
$stmt_event = $pdo->prepare("INSERT INTO process_events (processInstanceId, eventType, description, createdBy) VALUES (?, 'status_change', ?, ?)");
|
||||||
|
$stmt_event->execute([$instanceId, "Status changed to $status", $userId]);
|
||||||
|
|
||||||
|
// Redirect back to the dashboard
|
||||||
|
header("Location: process_dashboard.php");
|
||||||
|
exit;
|
||||||
52
_update_person.php
Normal file
52
_update_person.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$personId = $_POST['id'];
|
||||||
|
$firstName = $_POST['firstName'];
|
||||||
|
$lastName = $_POST['lastName'];
|
||||||
|
$email = $_POST['email'];
|
||||||
|
$companyName = $_POST['companyName'];
|
||||||
|
$phone = $_POST['phone'];
|
||||||
|
$role = $_POST['role'] ?? 'członek'; // Default to 'członek'
|
||||||
|
$functions = isset($_POST['functions']) ? $_POST['functions'] : [];
|
||||||
|
$password = $_POST['password'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Update person details
|
||||||
|
if (!empty($password)) {
|
||||||
|
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
$sql = "UPDATE people SET firstName = ?, lastName = ?, email = ?, companyName = ?, phone = ?, password = ?, role = ? WHERE id = ?";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$firstName, $lastName, $email, $companyName, $phone, $passwordHash, $role, $personId]);
|
||||||
|
} else {
|
||||||
|
$sql = "UPDATE people SET firstName = ?, lastName = ?, email = ?, companyName = ?, phone = ?, role = ? WHERE id = ?";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute([$firstName, $lastName, $email, $companyName, $phone, $role, $personId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update functions
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM user_functions WHERE user_id = ?");
|
||||||
|
$stmt->execute([$personId]);
|
||||||
|
|
||||||
|
if (!empty($functions)) {
|
||||||
|
$sql = "INSERT INTO user_functions (user_id, function_id) VALUES (?, ?)";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
foreach ($functions as $functionId) {
|
||||||
|
$stmt->execute([$personId, $functionId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = 'Osoba zaktualizowana pomyślnie.';
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log('Update failed: ' . $e->getMessage());
|
||||||
|
$_SESSION['error_message'] = "Błąd podczas aktualizacji osoby.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
96
assets/css/custom.css
Normal file
96
assets/css/custom.css
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 100;
|
||||||
|
padding: 48px 0 0;
|
||||||
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
||||||
|
width: 250px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-collapsed {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-collapsed .nav-link-text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-collapsed .nav-link i {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
margin-left: 250px;
|
||||||
|
transition: margin-left 0.3s;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content-collapsed {
|
||||||
|
margin-left: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link.active {
|
||||||
|
color: #0d6efd;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
padding-top: .75rem;
|
||||||
|
padding-bottom: .75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
background-color: rgba(0, 0, 0, .25);
|
||||||
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .form-control {
|
||||||
|
padding: .75rem 1rem;
|
||||||
|
border-width: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calendar styles */
|
||||||
|
.calendar {
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
.calendar td {
|
||||||
|
height: 120px;
|
||||||
|
vertical-align: top;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
.calendar .day-number {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.calendar .not-month {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
.events {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
.event {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-item .nav-link {
|
||||||
|
padding-left: 2.5rem;
|
||||||
|
}
|
||||||
12
assets/js/main.js
Normal file
12
assets/js/main.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
const mainContent = document.getElementById('main-content');
|
||||||
|
const sidebarToggler = document.getElementById('sidebar-toggler');
|
||||||
|
|
||||||
|
if (sidebarToggler) {
|
||||||
|
sidebarToggler.addEventListener('click', function () {
|
||||||
|
sidebar.classList.toggle('sidebar-collapsed');
|
||||||
|
mainContent.classList.toggle('main-content-collapsed');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
201
bni_groups.php
Normal file
201
bni_groups.php
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
include '_header.php';
|
||||||
|
include '_navbar.php';
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->query("SELECT * FROM bni_groups ORDER BY display_order");
|
||||||
|
$bni_groups = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<?php include '_sidebar.php'; ?>
|
||||||
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||||
|
<h1 class="h2 pt-3 pb-2 mb-3 border-bottom">BNI Groups</h1>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['success_message'])): ?>
|
||||||
|
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert">
|
||||||
|
<?= $_SESSION['success_message']; ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php unset($_SESSION['success_message']); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end mb-3">
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addModal">
|
||||||
|
Add New BNI Group
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive mt-4">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 30px;"></th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>City</th>
|
||||||
|
<th>Active</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="sortable-list">
|
||||||
|
<?php foreach ($bni_groups as $group): ?>
|
||||||
|
<tr data-id="<?= $group['id'] ?>">
|
||||||
|
<td class="handle"><i class="bi bi-grip-vertical"></i></td>
|
||||||
|
<td><?= htmlspecialchars($group['name']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($group['city']) ?></td>
|
||||||
|
<td><?= $group['active'] ? 'Yes' : 'No' ?></td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#editModal"
|
||||||
|
data-id="<?= $group['id'] ?>"
|
||||||
|
data-name="<?= htmlspecialchars($group['name']) ?>"
|
||||||
|
data-city="<?= htmlspecialchars($group['city']) ?>"
|
||||||
|
data-active="<?= $group['active'] ?>">
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#deleteModal" data-id="<?= $group['id'] ?>">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Modal -->
|
||||||
|
<div class="modal fade" id="addModal" tabindex="-1" aria-labelledby="addModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="addModalLabel">Add BNI Group</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<form action="_add_bni_group.php" method="post">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="addName" class="form-label">Name</label>
|
||||||
|
<input type="text" class="form-control" id="addName" name="name" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="addCity" class="form-label">City</label>
|
||||||
|
<input type="text" class="form-control" id="addCity" name="city">
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-3">
|
||||||
|
<input class="form-check-input" type="checkbox" value="1" id="addActive" name="active" checked>
|
||||||
|
<label class="form-check-label" for="addActive">
|
||||||
|
Active
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="submit" name="add" class="btn btn-primary">Add</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Edit Modal -->
|
||||||
|
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="editModalLabel">Edit BNI Group</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<form action="_update_bni_group.php" method="post">
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="editId" name="id">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editName" class="form-label">Name</label>
|
||||||
|
<input type="text" class="form-control" id="editName" name="name" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editCity" class="form-label">City</label>
|
||||||
|
<input type="text" class="form-control" id="editCity" name="city">
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-3">
|
||||||
|
<input class="form-check-input" type="checkbox" value="1" id="editActive" name="active">
|
||||||
|
<label class="form-check-label" for="editActive">
|
||||||
|
Active
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="submit" name="edit" class="btn btn-primary">Save changes</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Delete Modal -->
|
||||||
|
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="deleteModalLabel">Delete BNI Group</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Are you sure you want to delete this BNI group?
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
<a href="#" id="deleteLink" class="btn btn-danger">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<?php include '_footer.php'; ?>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var editModal = document.getElementById('editModal');
|
||||||
|
editModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
var button = event.relatedTarget;
|
||||||
|
var id = button.getAttribute('data-id');
|
||||||
|
var name = button.getAttribute('data-name');
|
||||||
|
var city = button.getAttribute('data-city');
|
||||||
|
var active = button.getAttribute('data-active');
|
||||||
|
|
||||||
|
var idInput = editModal.querySelector('#editId');
|
||||||
|
var nameInput = editModal.querySelector('#editName');
|
||||||
|
var cityInput = editModal.querySelector('#editCity');
|
||||||
|
var activeInput = editModal.querySelector('#editActive');
|
||||||
|
|
||||||
|
idInput.value = id;
|
||||||
|
nameInput.value = name;
|
||||||
|
cityInput.value = city;
|
||||||
|
activeInput.checked = (active == 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
var deleteModal = document.getElementById('deleteModal');
|
||||||
|
deleteModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
var button = event.relatedTarget;
|
||||||
|
var id = button.getAttribute('data-id');
|
||||||
|
var deleteLink = deleteModal.querySelector('#deleteLink');
|
||||||
|
deleteLink.href = '_delete_bni_group.php?id=' + id;
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#sortable-list").sortable({
|
||||||
|
handle: ".handle",
|
||||||
|
update: function(event, ui) {
|
||||||
|
var order = $(this).sortable('toArray', {attribute: 'data-id'});
|
||||||
|
$.post('_update_bni_group_order.php', { order: order });
|
||||||
|
}
|
||||||
|
}).disableSelection();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
303
calendar.php
Normal file
303
calendar.php
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
include '_header.php';
|
||||||
|
|
||||||
|
// Get current month and year
|
||||||
|
$month = isset($_GET['month']) ? (int)$_GET['month'] : date('m');
|
||||||
|
$year = isset($_GET['year']) ? (int)$_GET['year'] : date('Y');
|
||||||
|
|
||||||
|
// Create a DateTime object for the first day of the month
|
||||||
|
$firstDayOfMonth = new DateTime("$year-$month-01");
|
||||||
|
$daysInMonth = $firstDayOfMonth->format('t');
|
||||||
|
$dayOfWeek = $firstDayOfMonth->format('N'); // 1 (for Monday) through 7 (for Sunday)
|
||||||
|
|
||||||
|
// Get events for the current month
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT c.*, t.name as type_name, t.color as type_color FROM calendar_events c LEFT JOIN event_types t ON c.event_type_id = t.id WHERE MONTH(c.start_datetime) = ? AND YEAR(c.start_datetime) = ? ORDER BY c.start_datetime ASC");
|
||||||
|
$stmt->execute([$month, $year]);
|
||||||
|
$events = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$eventsByDay = [];
|
||||||
|
foreach ($events as $event) {
|
||||||
|
$day = (new DateTime($event['start_datetime']))->format('j');
|
||||||
|
if (!isset($eventsByDay[$day])) {
|
||||||
|
$eventsByDay[$day] = [];
|
||||||
|
}
|
||||||
|
$eventsByDay[$day][] = $event;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get event types for the modal
|
||||||
|
$stmt_types = $pdo->query("SELECT * FROM event_types ORDER BY display_order");
|
||||||
|
$event_types = $stmt_types->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$prevMonth = $month == 1 ? 12 : $month - 1;
|
||||||
|
$prevYear = $month == 1 ? $year - 1 : $year;
|
||||||
|
$nextMonth = $month == 12 ? 1 : $month + 1;
|
||||||
|
$nextYear = $month == 12 ? $year + 1 : $year;
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
|
||||||
|
<?php include '_navbar.php'; ?>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<?php include '_sidebar.php'; ?>
|
||||||
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||||
|
|
||||||
|
<h2 class="text-center"><?php echo $firstDayOfMonth->format('F Y'); ?></h2>
|
||||||
|
<div class="d-flex justify-content-between mb-3">
|
||||||
|
<a href="?month=<?php echo $prevMonth; ?>&year=<?php echo $prevYear; ?>" class="btn btn-primary">< Previous</a>
|
||||||
|
<a href="?month=<?php echo $nextMonth; ?>&year=<?php echo $nextYear; ?>" class="btn btn-primary">Next ></a>
|
||||||
|
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addEventModal">
|
||||||
|
Add Event
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Monday</th>
|
||||||
|
<th>Tuesday</th>
|
||||||
|
<th>Wednesday</th>
|
||||||
|
<th>Thursday</th>
|
||||||
|
<th>Friday</th>
|
||||||
|
<th>Saturday</th>
|
||||||
|
<th>Sunday</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<?php
|
||||||
|
// Print empty cells
|
||||||
|
for ($i = 1; $i < $dayOfWeek; $i++) {
|
||||||
|
echo "<td></td>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentDay = 1;
|
||||||
|
while ($currentDay <= $daysInMonth) {
|
||||||
|
if ($dayOfWeek > 7) {
|
||||||
|
$dayOfWeek = 1;
|
||||||
|
echo "</tr><tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<td class=\"calendar-day\" data-date=\"$year-$month-$currentDay\">";
|
||||||
|
echo "<strong>$currentDay</strong>";
|
||||||
|
if (isset($eventsByDay[$currentDay])) {
|
||||||
|
echo "<ul class=\"list-unstyled\">";
|
||||||
|
foreach ($eventsByDay[$currentDay] as $event) {
|
||||||
|
echo '<li class="badge" style="background-color: '.($event['type_color'] ?? '#007bff').'" data-event-id="'.$event['id'].'">' . htmlspecialchars($event['title']) . "</li>";
|
||||||
|
}
|
||||||
|
echo "</ul>";
|
||||||
|
}
|
||||||
|
echo "</td>";
|
||||||
|
|
||||||
|
$currentDay++;
|
||||||
|
$dayOfWeek++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print remaining empty cells
|
||||||
|
while ($dayOfWeek <= 7) {
|
||||||
|
echo "<td></td>";
|
||||||
|
$dayOfWeek++;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Event Details Modal -->
|
||||||
|
<div class="modal fade" id="eventDetailsModal" tabindex="-1" aria-labelledby="eventDetailsModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="eventDetailsModalLabel">Event Details</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<h5 id="event-title"></h5>
|
||||||
|
<p id="event-description"></p>
|
||||||
|
<p><strong>Starts:</strong> <span id="event-start"></span></p>
|
||||||
|
<p><strong>Ends:</strong> <span id="event-end"></span></p>
|
||||||
|
<p><strong>Type:</strong> <span id="event-type"></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="edit-event-btn">Edit</button>
|
||||||
|
<a href="#" id="delete-event-btn" class="btn btn-danger">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="addEventModal" tabindex="-1" aria-labelledby="addEventModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="addEventModalLabel">Add Calendar Event</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="_add_calendar_event.php" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="title" class="form-label">Title</label>
|
||||||
|
<input type="text" class="form-control" id="title" name="title" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="description" class="form-label">Description</label>
|
||||||
|
<textarea class="form-control" id="description" name="description"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="start_datetime" class="form-label">Start Time</label>
|
||||||
|
<input type="datetime-local" class="form-control" id="start_datetime" name="start_datetime" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="end_datetime" class="form-label">End Time</label>
|
||||||
|
<input type="datetime-local" class="form-control" id="end_datetime" name="end_datetime" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="event_type_id" class="form-label">Type</label>
|
||||||
|
<select class="form-select" id="event_type_id" name="event_type_id">
|
||||||
|
<?php foreach ($event_types as $type): ?>
|
||||||
|
<option value="<?= $type['id'] ?>"><?= htmlspecialchars($type['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="recurrence" class="form-label">Recurrence</label>
|
||||||
|
<select class="form-select" id="recurrence" name="recurrence">
|
||||||
|
<option value="">Does not repeat</option>
|
||||||
|
<option value="daily">Daily</option>
|
||||||
|
<option value="weekly">Weekly</option>
|
||||||
|
<option value="monthly">Monthly</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3" id="recurrence_end_date_container" style="display: none;">
|
||||||
|
<label for="recurrence_end_date" class="form-label">Recurrence End Date</label>
|
||||||
|
<input type="date" class="form-control" id="recurrence_end_date" name="recurrence_end_date">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Save Event</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="editEventModal" tabindex="-1" aria-labelledby="editEventModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="editEventModalLabel">Edit Calendar Event</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="_update_calendar_event.php" method="post">
|
||||||
|
<input type="hidden" id="edit_event_id" name="event_id">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="edit_title" class="form-label">Title</label>
|
||||||
|
<input type="text" class="form-control" id="edit_title" name="title" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="edit_description" class="form-label">Description</label>
|
||||||
|
<textarea class="form-control" id="edit_description" name="description"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="edit_start_datetime" class="form-label">Start Time</label>
|
||||||
|
<input type="datetime-local" class="form-control" id="edit_start_datetime" name="start_datetime" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="edit_end_datetime" class="form-label">End Time</label>
|
||||||
|
<input type="datetime-local" class="form-control" id="edit_end_datetime" name="end_datetime" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="edit_event_type_id" class="form-label">Type</label>
|
||||||
|
<select class="form-select" id="edit_event_type_id" name="event_type_id">
|
||||||
|
<?php foreach ($event_types as $type): ?>
|
||||||
|
<option value="<?= $type['id'] ?>"><?= htmlspecialchars($type['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const addEventModal = new bootstrap.Modal(document.getElementById('addEventModal'));
|
||||||
|
const eventDetailsModal = new bootstrap.Modal(document.getElementById('eventDetailsModal'));
|
||||||
|
const editEventModal = new bootstrap.Modal(document.getElementById('editEventModal'));
|
||||||
|
|
||||||
|
let currentEventData = null;
|
||||||
|
|
||||||
|
document.querySelectorAll('.calendar-day').forEach(day => {
|
||||||
|
day.addEventListener('click', function(e) {
|
||||||
|
if (e.target.classList.contains('badge')) {
|
||||||
|
const eventId = e.target.dataset.eventId;
|
||||||
|
fetch(`_get_event_details.php?id=${eventId}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
currentEventData = data;
|
||||||
|
document.getElementById('event-title').textContent = data.title;
|
||||||
|
document.getElementById('event-description').textContent = data.description;
|
||||||
|
document.getElementById('event-start').textContent = new Date(data.start_datetime).toLocaleString();
|
||||||
|
document.getElementById('event-end').textContent = new Date(data.end_datetime).toLocaleString();
|
||||||
|
document.getElementById('event-type').textContent = data.type_name;
|
||||||
|
document.getElementById('delete-event-btn').href = `_delete_calendar_event.php?id=${eventId}`;
|
||||||
|
eventDetailsModal.show();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const date = this.dataset.date;
|
||||||
|
const now = new Date();
|
||||||
|
const hours = String(now.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||||
|
const currentTime = `${hours}:${minutes}`;
|
||||||
|
|
||||||
|
const startInput = document.getElementById('start_datetime');
|
||||||
|
const month = String(new Date(date).getMonth() + 1).padStart(2, '0');
|
||||||
|
const dayOfMonth = String(new Date(date).getDate()).padStart(2, '0');
|
||||||
|
const year = new Date(date).getFullYear();
|
||||||
|
startInput.value = `${year}-${month}-${dayOfMonth}T${currentTime}`;
|
||||||
|
|
||||||
|
addEventModal.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('edit-event-btn').addEventListener('click', function() {
|
||||||
|
if (currentEventData) {
|
||||||
|
if (currentEventData.parent_event_id !== null) {
|
||||||
|
alert("Editing of recurring events is not supported yet. Please edit the parent event.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.getElementById('edit_event_id').value = currentEventData.id;
|
||||||
|
document.getElementById('edit_title').value = currentEventData.title;
|
||||||
|
document.getElementById('edit_description').value = currentEventData.description;
|
||||||
|
document.getElementById('edit_start_datetime').value = currentEventData.start_datetime.slice(0, 16);
|
||||||
|
document.getElementById('edit_end_datetime').value = currentEventData.end_datetime.slice(0, 16);
|
||||||
|
document.getElementById('edit_event_type_id').value = currentEventData.event_type_id;
|
||||||
|
eventDetailsModal.hide();
|
||||||
|
editEventModal.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const recurrenceSelect = document.getElementById('recurrence');
|
||||||
|
const recurrenceEndDateContainer = document.getElementById('recurrence_end_date_container');
|
||||||
|
|
||||||
|
recurrenceSelect.addEventListener('change', function() {
|
||||||
|
if (this.value) {
|
||||||
|
recurrenceEndDateContainer.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
recurrenceEndDateContainer.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
include '_footer.php';
|
||||||
|
?>
|
||||||
8
db/migrations/001_add_process_definition_json.php
Normal file
8
db/migrations/001_add_process_definition_json.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function migrate_001($pdo) {
|
||||||
|
$sql = "ALTER TABLE process_definitions ADD COLUMN definition_json TEXT NULL AFTER name;";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Migration 001 applied: Added definition_json to process_definitions.\n";
|
||||||
|
}
|
||||||
|
|
||||||
17
db/migrations/002_create_calendar_events_table.php
Normal file
17
db/migrations/002_create_calendar_events_table.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function migrate_002($pdo) {
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS calendar_events (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
title VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
start_datetime DATETIME NOT NULL,
|
||||||
|
end_datetime DATETIME NOT NULL,
|
||||||
|
type VARCHAR(50) NOT NULL, -- 'meeting', 'training'
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Migration 002 applied: Created calendar_events table.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
26
db/migrations/003_create_event_types_table.php
Normal file
26
db/migrations/003_create_event_types_table.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../db/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS event_types (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
color VARCHAR(7) NOT NULL DEFAULT '#007bff'
|
||||||
|
);";
|
||||||
|
|
||||||
|
$pdo->exec($sql);
|
||||||
|
|
||||||
|
// Add some default values
|
||||||
|
$sql_insert = "INSERT INTO event_types (name, color) VALUES
|
||||||
|
('Meeting', '#007bff'),
|
||||||
|
('Training', '#ffc107'),
|
||||||
|
('Other', '#28a745');";
|
||||||
|
$pdo->exec($sql_insert);
|
||||||
|
|
||||||
|
|
||||||
|
echo "Migration 003 completed successfully.\n";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Migration 003 failed: " . $e->getMessage() . "\n");
|
||||||
|
}
|
||||||
34
db/migrations/004_add_event_type_id_to_calendar_events.php
Normal file
34
db/migrations/004_add_event_type_id_to_calendar_events.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../db/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// First, add the column without the foreign key constraint
|
||||||
|
$sql_add_column = "ALTER TABLE calendar_events ADD COLUMN event_type_id INT NULL;";
|
||||||
|
$pdo->exec($sql_add_column);
|
||||||
|
|
||||||
|
// Set a default value for existing rows to avoid foreign key constraint errors
|
||||||
|
// Get the ID of the 'Other' event type
|
||||||
|
$stmt = $pdo->query("SELECT id FROM event_types WHERE name = 'Other' LIMIT 1");
|
||||||
|
$other_type = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$default_type_id = $other_type ? $other_type['id'] : 1; // Fallback to 1 if 'Other' not found
|
||||||
|
|
||||||
|
$sql_update_existing = "UPDATE calendar_events SET event_type_id = ? WHERE event_type_id IS NULL;";
|
||||||
|
$stmt_update = $pdo->prepare($sql_update_existing);
|
||||||
|
$stmt_update->execute([$default_type_id]);
|
||||||
|
|
||||||
|
// Now, add the foreign key constraint
|
||||||
|
$sql_add_fk = "ALTER TABLE calendar_events ADD CONSTRAINT fk_event_type FOREIGN KEY (event_type_id) REFERENCES event_types(id) ON DELETE SET NULL;";
|
||||||
|
$pdo->exec($sql_add_fk);
|
||||||
|
|
||||||
|
echo "Migration 004 completed successfully.\n";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Check if the column already exists, which might happen on re-runs
|
||||||
|
if (strpos($e->getMessage(), 'Duplicate column name') !== false) {
|
||||||
|
echo "Migration 004 seems to be already applied (Column exists). Skipping.\n";
|
||||||
|
} else {
|
||||||
|
die("Migration 004 failed: " . $e->getMessage() . "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
18
db/migrations/005_create_bni_groups_table.php
Normal file
18
db/migrations/005_create_bni_groups_table.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/../config.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS `bni_groups` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`name` VARCHAR(255) NOT NULL,
|
||||||
|
`city` VARCHAR(255),
|
||||||
|
`active` BOOLEAN NOT NULL DEFAULT 1,
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Table 'bni_groups' created successfully." . PHP_EOL;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error creating table 'bni_groups': " . $e->getMessage() . PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
20
db/migrations/006_add_recurrence_to_calendar_events.php
Normal file
20
db/migrations/006_add_recurrence_to_calendar_events.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../db/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
$sql = <<<SQL
|
||||||
|
ALTER TABLE `calendar_events`
|
||||||
|
ADD COLUMN `recurrence` VARCHAR(20) DEFAULT NULL,
|
||||||
|
ADD COLUMN `recurrence_end_date` DATE DEFAULT NULL,
|
||||||
|
ADD COLUMN `parent_event_id` INT DEFAULT NULL;
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
$pdo->exec($sql);
|
||||||
|
|
||||||
|
echo "Migration 006 executed successfully: Added recurrence columns to calendar_events table.\n";
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
die("Error executing migration 006: " . $e->getMessage() . "\n");
|
||||||
|
}
|
||||||
17
db/migrations/007_remove_type_from_calendar_events.php
Normal file
17
db/migrations/007_remove_type_from_calendar_events.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../db/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "ALTER TABLE calendar_events DROP COLUMN type;";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Migration 007 completed successfully: Dropped 'type' column from calendar_events.\n";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Check if the column has already been dropped
|
||||||
|
if (strpos($e->getMessage(), 'column not found') !== false || strpos($e->getMessage(), 'Unknown column') !== false) {
|
||||||
|
echo "Migration 007 seems to be already applied (Column not found). Skipping.\n";
|
||||||
|
} else {
|
||||||
|
die("Migration 007 failed: " . $e->getMessage() . "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
16
db/migrations/008_create_roles_table.php
Normal file
16
db/migrations/008_create_roles_table.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/../config.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS `roles` (
|
||||||
|
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` VARCHAR(255) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Table 'roles' created successfully." . PHP_EOL;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error creating table 'roles': " . $e->getMessage() . PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
18
db/migrations/009_create_user_roles_table.php
Normal file
18
db/migrations/009_create_user_roles_table.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/../config.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS `user_roles` (
|
||||||
|
`user_id` INT(11) UNSIGNED NOT NULL,
|
||||||
|
`role_id` INT(11) UNSIGNED NOT NULL,
|
||||||
|
PRIMARY KEY (`user_id`, `role_id`),
|
||||||
|
FOREIGN KEY (`user_id`) REFERENCES `people`(`id`) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Table 'user_roles' created successfully." . PHP_EOL;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error creating table 'user_roles': " . $e->getMessage() . PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
12
db/migrations/010_add_display_order_to_event_types.php
Normal file
12
db/migrations/010_add_display_order_to_event_types.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../db/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "ALTER TABLE event_types ADD COLUMN display_order INT NOT NULL DEFAULT 0";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Migration 010_add_display_order_to_event_types executed successfully." . PHP_EOL;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error executing migration 010_add_display_order_to_event_types: " . $e->getMessage() . PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
12
db/migrations/011_add_display_order_to_bni_groups.php
Normal file
12
db/migrations/011_add_display_order_to_bni_groups.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../db/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "ALTER TABLE bni_groups ADD COLUMN display_order INT NOT NULL DEFAULT 0";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Migration 011_add_display_order_to_bni_groups executed successfully." . PHP_EOL;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error executing migration 011_add_display_order_to_bni_groups: " . $e->getMessage() . PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
12
db/migrations/012_add_display_order_to_roles.php
Normal file
12
db/migrations/012_add_display_order_to_roles.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../db/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "ALTER TABLE roles ADD COLUMN display_order INT NOT NULL DEFAULT 0";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Migration 012_add_display_order_to_roles executed successfully." . PHP_EOL;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error executing migration 012_add_display_order_to_roles: " . $e->getMessage() . PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
21
db/migrations/013_rename_roles_to_functions.php
Normal file
21
db/migrations/013_rename_roles_to_functions.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/../config.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "RENAME TABLE `roles` TO `functions`;";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Table 'roles' renamed to 'functions' successfully." . PHP_EOL;
|
||||||
|
|
||||||
|
$sql = "RENAME TABLE `user_roles` TO `user_functions`;";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Table 'user_roles' renamed to 'user_functions' successfully." . PHP_EOL;
|
||||||
|
|
||||||
|
$sql = "ALTER TABLE `user_functions` CHANGE `role_id` `function_id` INT(11) UNSIGNED NOT NULL;";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Column 'role_id' renamed to 'function_id' in 'user_functions' table successfully." . PHP_EOL;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error renaming tables or columns: " . $e->getMessage() . PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
21
db/migrations/014_add_role_to_people_table.php
Normal file
21
db/migrations/014_add_role_to_people_table.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../../db/config.php';
|
||||||
|
|
||||||
|
function migrate_014_add_role_to_people_table() {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Check if the column already exists
|
||||||
|
try {
|
||||||
|
$result = $pdo->query("SELECT `role` FROM `people` LIMIT 1");
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Column does not exist, so add it
|
||||||
|
$pdo->exec("ALTER TABLE `people` ADD COLUMN `role` VARCHAR(50) NOT NULL DEFAULT 'członek' AFTER `email`");
|
||||||
|
echo "Migration 014: Added 'role' column to 'people' table.\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Migration 014: 'role' column already exists in 'people' table. No changes made.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate_014_add_role_to_people_table();
|
||||||
|
|
||||||
133
db_setup.php
Normal file
133
db_setup.php
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
echo "Starting database setup...\n";
|
||||||
|
|
||||||
|
// 1. People table (unified table for users and contacts)
|
||||||
|
$pdo->exec("CREATE TABLE IF NOT EXISTS `people` (
|
||||||
|
`id` INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`firstName` VARCHAR(255) NOT NULL,
|
||||||
|
`lastName` VARCHAR(255) NOT NULL,
|
||||||
|
`email` VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
`password` VARCHAR(255) NULL,
|
||||||
|
`companyName` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`phone` VARCHAR(50) DEFAULT NULL,
|
||||||
|
`role` ENUM('admin', 'team_member', 'member', 'guest') NOT NULL DEFAULT 'guest',
|
||||||
|
`is_user` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
`active` BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
`createdAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)");
|
||||||
|
echo "People table created or already exists.\n";
|
||||||
|
|
||||||
|
// Seed default admin user
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM people WHERE email = ?");
|
||||||
|
$stmt->execute(['admin@example.com']);
|
||||||
|
if ($stmt->fetchColumn() === false) {
|
||||||
|
$password = password_hash('password', PASSWORD_DEFAULT);
|
||||||
|
$insert_stmt = $pdo->prepare(
|
||||||
|
"INSERT INTO people (email, password, role, firstName, lastName, is_user, active) VALUES (?, ?, 'admin', 'Admin', 'User', TRUE, TRUE)"
|
||||||
|
);
|
||||||
|
$insert_stmt->execute(['admin@example.com', $password]);
|
||||||
|
echo "Default admin user created. Email: admin@example.com, Password: password\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Process Definitions table
|
||||||
|
$pdo->exec("CREATE TABLE IF NOT EXISTS `process_definitions` (
|
||||||
|
`id` INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`code` VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
`name` VARCHAR(255) NOT NULL,
|
||||||
|
`description` TEXT,
|
||||||
|
`version` INT NOT NULL DEFAULT 1,
|
||||||
|
`is_active` BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
`start_node_id` VARCHAR(255),
|
||||||
|
`definition_json` TEXT,
|
||||||
|
`createdAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)");
|
||||||
|
echo "Process definitions table created or already exists.\n";
|
||||||
|
|
||||||
|
// Seed process_definitions
|
||||||
|
$processes = [
|
||||||
|
['code' => 'mentoring', 'name' => 'Mentoring nowego czlonka', 'description' => 'Proces wdrozenia nowego czlonka do organizacji.'],
|
||||||
|
['code' => 'meeting_preparation', 'name' => 'Przygotowanie spotkania grupy', 'description' => 'Proces przygotowania do spotkania grupy, w tym agenda, materialy, etc.'],
|
||||||
|
['code' => 'guest_handling', 'name' => 'Obsluga goscia', 'description' => 'Proces obslugi gosci odwiedzajacych organizacje.']
|
||||||
|
];
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM process_definitions WHERE code = ?");
|
||||||
|
$insert_stmt = $pdo->prepare("INSERT INTO process_definitions (code, name, description) VALUES (?, ?, ?)");
|
||||||
|
foreach ($processes as $process) {
|
||||||
|
$stmt->execute([$process['code']]);
|
||||||
|
if ($stmt->fetchColumn() === false) {
|
||||||
|
$insert_stmt->execute([$process['code'], $process['name'], $process['description']]);
|
||||||
|
echo "Seeded process: " . $process['name'] . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
`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`)
|
||||||
|
)");
|
||||||
|
echo "Process instances table created or already exists.\n";
|
||||||
|
|
||||||
|
// 4. Process Events table (updated FK)
|
||||||
|
$pdo->exec("CREATE TABLE IF NOT EXISTS `process_events` (
|
||||||
|
`id` INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`processInstanceId` INT(11) UNSIGNED NOT NULL,
|
||||||
|
`event_type` VARCHAR(50) NOT NULL,
|
||||||
|
`message` TEXT,
|
||||||
|
`node_id` VARCHAR(255),
|
||||||
|
`payload_json` TEXT,
|
||||||
|
`createdBy` INT(11) UNSIGNED NOT NULL,
|
||||||
|
`createdAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (processInstanceId) REFERENCES process_instances(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (createdBy) REFERENCES people(id) ON DELETE CASCADE
|
||||||
|
)");
|
||||||
|
echo "Process events table created or already exists.\n";
|
||||||
|
|
||||||
|
// MIGRATIONS
|
||||||
|
echo "Starting migrations...\n";
|
||||||
|
|
||||||
|
// Migration: Rename `processId` to `processDefinitionId` in `process_instances`
|
||||||
|
$stmt = $pdo->query("SHOW COLUMNS FROM `process_instances` LIKE 'processId'");
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
$checkFk = $pdo->query("SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_SCHEMA = SCHEMA() AND TABLE_NAME = 'process_instances' AND COLUMN_NAME = 'processId';")->fetch();
|
||||||
|
if ($checkFk) {
|
||||||
|
$pdo->exec("ALTER TABLE `process_instances` DROP FOREIGN KEY `{$checkFk['CONSTRAINT_NAME']}`;");
|
||||||
|
}
|
||||||
|
$pdo->exec("ALTER TABLE `process_instances` CHANGE `processId` `processDefinitionId` INT(11) UNSIGNED NOT NULL;");
|
||||||
|
$pdo->exec("ALTER TABLE `process_instances` ADD FOREIGN KEY (`processDefinitionId`) REFERENCES `process_definitions`(`id`) ON DELETE CASCADE;");
|
||||||
|
echo "Migrated process_instances: processId -> processDefinitionId.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migration: Rename `contactId` to `personId` and update foreign key in `process_instances`
|
||||||
|
$stmt = $pdo->query("SHOW COLUMNS FROM `process_instances` LIKE 'contactId'");
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
$checkFk = $pdo->query("SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_SCHEMA = SCHEMA() AND TABLE_NAME = 'process_instances' AND COLUMN_NAME = 'contactId';")->fetch();
|
||||||
|
if ($checkFk) {
|
||||||
|
$pdo->exec("ALTER TABLE `process_instances` DROP FOREIGN KEY `{$checkFk['CONSTRAINT_NAME']}`;");
|
||||||
|
}
|
||||||
|
$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`;");
|
||||||
|
echo "Dropped old 'users' and 'contacts' tables.\n";
|
||||||
|
|
||||||
|
echo "\nDatabase setup/update completed successfully.\n";
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Database setup failed: " . $e->getMessage());
|
||||||
|
}
|
||||||
185
event_types.php
Normal file
185
event_types.php
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
include '_header.php';
|
||||||
|
include '_navbar.php';
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->query("SELECT * FROM event_types ORDER BY display_order");
|
||||||
|
$event_types = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<?php include '_sidebar.php'; ?>
|
||||||
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||||
|
<h1 class="h2 pt-3 pb-2 mb-3 border-bottom">Event Types</h1>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['success_message'])): ?>
|
||||||
|
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert">
|
||||||
|
<?= $_SESSION['success_message']; ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php unset($_SESSION['success_message']); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end mb-3">
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addModal">
|
||||||
|
Add New Event Type
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive mt-4">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 30px;"></th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Color</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="sortable-list">
|
||||||
|
<?php foreach ($event_types as $type): ?>
|
||||||
|
<tr data-id="<?= $type['id'] ?>">
|
||||||
|
<td class="handle"><i class="bi bi-grip-vertical"></i></td>
|
||||||
|
<td><?= htmlspecialchars($type['name']) ?></td>
|
||||||
|
<td><span class="badge" style="background-color: <?= htmlspecialchars($type['color']) ?>"><?= htmlspecialchars($type['color']) ?></span></td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#editModal" data-id="<?= $type['id'] ?>" data-name="<?= htmlspecialchars($type['name']) ?>" data-color="<?= htmlspecialchars($type['color']) ?>" data-display-order="<?= htmlspecialchars($type['display_order']) ?>">
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#deleteModal" data-id="<?= $type['id'] ?>">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Modal -->
|
||||||
|
<div class="modal fade" id="addModal" tabindex="-1" aria-labelledby="addModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="addModalLabel">Add Event Type</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<form action="_add_event_type.php" method="post">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="addName" class="form-label">Name</label>
|
||||||
|
<input type="text" class="form-control" id="addName" name="name" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="addColor" class="form-label">Color</label>
|
||||||
|
<input type="color" class="form-control" id="addColor" name="color" value="#007bff" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="submit" name="add" class="btn btn-primary">Add</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Edit Modal -->
|
||||||
|
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="editModalLabel">Edit Event Type</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<form action="_update_event_type.php" method="post">
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="editId" name="id">
|
||||||
|
<input type="hidden" id="editDisplayOrder" name="display_order">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editName" class="form-label">Name</label>
|
||||||
|
<input type="text" class="form-control" id="editName" name="name" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editColor" class="form-label">Color</label>
|
||||||
|
<input type="color" class="form-control" id="editColor" name="color" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="submit" name="edit" class="btn btn-primary">Save changes</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Delete Modal -->
|
||||||
|
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="deleteModalLabel">Delete Event Type</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Are you sure you want to delete this event type?
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
<a href="#" id="deleteLink" class="btn btn-danger">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<?php include '_footer.php'; ?>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var editModal = document.getElementById('editModal');
|
||||||
|
editModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
var button = event.relatedTarget;
|
||||||
|
var id = button.getAttribute('data-id');
|
||||||
|
var name = button.getAttribute('data-name');
|
||||||
|
var color = button.getAttribute('data-color');
|
||||||
|
var display_order = button.getAttribute('data-display-order');
|
||||||
|
|
||||||
|
var modalTitle = editModal.querySelector('.modal-title');
|
||||||
|
var idInput = editModal.querySelector('#editId');
|
||||||
|
var nameInput = editModal.querySelector('#editName');
|
||||||
|
var colorInput = editModal.querySelector('#editColor');
|
||||||
|
var displayOrderInput = editModal.querySelector('#editDisplayOrder');
|
||||||
|
|
||||||
|
idInput.value = id;
|
||||||
|
nameInput.value = name;
|
||||||
|
colorInput.value = color;
|
||||||
|
displayOrderInput.value = display_order;
|
||||||
|
});
|
||||||
|
|
||||||
|
var deleteModal = document.getElementById('deleteModal');
|
||||||
|
deleteModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
var button = event.relatedTarget;
|
||||||
|
var id = button.getAttribute('data-id');
|
||||||
|
var deleteLink = deleteModal.querySelector('#deleteLink');
|
||||||
|
deleteLink.href = '_delete_event_type.php?id=' + id;
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#sortable-list").sortable({
|
||||||
|
handle: ".handle",
|
||||||
|
update: function(event, ui) {
|
||||||
|
var order = $(this).sortable('toArray', {attribute: 'data-id'});
|
||||||
|
$.post('_update_event_type_order.php', { order: order });
|
||||||
|
}
|
||||||
|
}).disableSelection();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
168
functions.php
Normal file
168
functions.php
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
include '_header.php';
|
||||||
|
include '_navbar.php';
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->query("SELECT * FROM functions ORDER BY display_order");
|
||||||
|
$functions = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<?php include '_sidebar.php'; ?>
|
||||||
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||||
|
<h1 class="h2 pt-3 pb-2 mb-3 border-bottom">Functions</h1>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['success_message'])): ?>
|
||||||
|
<div class="alert alert-success alert-dismissible fade show mt-3" function="alert">
|
||||||
|
<?= $_SESSION['success_message']; ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php unset($_SESSION['success_message']); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end mb-3">
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addModal">
|
||||||
|
Add New Function
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive mt-4">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 30px;"></th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="sortable-list">
|
||||||
|
<?php foreach ($functions as $function): ?>
|
||||||
|
<tr data-id="<?= $function['id'] ?>">
|
||||||
|
<td class="handle"><i class="bi bi-grip-vertical"></i></td>
|
||||||
|
<td><?= htmlspecialchars($function['name']) ?></td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#editModal" data-id="<?= $function['id'] ?>" data-name="<?= htmlspecialchars($function['name']) ?>">
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#deleteModal" data-id="<?= $function['id'] ?>">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Modal -->
|
||||||
|
<div class="modal fade" id="addModal" tabindex="-1" aria-labelledby="addModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="addModalLabel">Add Function</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<form action="_add_function.php" method="post">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="addName" class="form-label">Name</label>
|
||||||
|
<input type="text" class="form-control" id="addName" name="name" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="submit" name="add" class="btn btn-primary">Add</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Edit Modal -->
|
||||||
|
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="editModalLabel">Edit Function</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<form action="_update_function.php" method="post">
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="editId" name="id">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editName" class="form-label">Name</label>
|
||||||
|
<input type="text" class="form-control" id="editName" name="name" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="submit" name="edit" class="btn btn-primary">Save changes</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Delete Modal -->
|
||||||
|
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="deleteModalLabel">Delete Function</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Are you sure you want to delete this function?
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
<a href="#" id="deleteLink" class="btn btn-danger">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<?php include '_footer.php'; ?>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var editModal = document.getElementById('editModal');
|
||||||
|
editModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
var button = event.relatedTarget;
|
||||||
|
var id = button.getAttribute('data-id');
|
||||||
|
var name = button.getAttribute('data-name');
|
||||||
|
|
||||||
|
var modalTitle = editModal.querySelector('.modal-title');
|
||||||
|
var idInput = editModal.querySelector('#editId');
|
||||||
|
var nameInput = editModal.querySelector('#editName');
|
||||||
|
|
||||||
|
idInput.value = id;
|
||||||
|
nameInput.value = name;
|
||||||
|
});
|
||||||
|
|
||||||
|
var deleteModal = document.getElementById('deleteModal');
|
||||||
|
deleteModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
var button = event.relatedTarget;
|
||||||
|
var id = button.getAttribute('data-id');
|
||||||
|
var deleteLink = deleteModal.querySelector('#deleteLink');
|
||||||
|
deleteLink.href = '_delete_function.php?id=' + id;
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
$("#sortable-list").sortable({
|
||||||
|
handle: ".handle",
|
||||||
|
update: function(event, ui) {
|
||||||
|
var order = $(this).sortable('toArray', {attribute: 'data-id'});
|
||||||
|
$.post('_update_function_order.php', { order: order });
|
||||||
|
}
|
||||||
|
}).disableSelection();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
716
index.php
716
index.php
@ -1,150 +1,574 @@
|
|||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
require_once 'WorkflowEngine.php';
|
||||||
@ini_set('display_errors', '1');
|
|
||||||
@error_reporting(E_ALL);
|
$workflowEngine = new WorkflowEngine();
|
||||||
@date_default_timezone_set('UTC');
|
$dashboardData = $workflowEngine->getDashboardMatrix();
|
||||||
|
|
||||||
|
$people = $dashboardData['people'];
|
||||||
|
$processes = $dashboardData['definitions'];
|
||||||
|
$instances = $dashboardData['instances'];
|
||||||
|
|
||||||
|
$status_colors = [
|
||||||
|
'none' => 'secondary',
|
||||||
|
'negative' => 'danger',
|
||||||
|
'in_progress' => 'warning',
|
||||||
|
'positive' => 'success',
|
||||||
|
];
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt_functions = $pdo->query("SELECT * FROM functions ORDER BY display_order");
|
||||||
|
$all_functions = $stmt_functions->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$functions_by_id = [];
|
||||||
|
foreach ($all_functions as $function) {
|
||||||
|
$functions_by_id[$function['id']] = $function['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt_person_functions = $pdo->query("SELECT user_id, function_id FROM user_functions");
|
||||||
|
$person_functions_map = [];
|
||||||
|
while ($row = $stmt_person_functions->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
$person_functions_map[$row['user_id']][] = $row['function_id'];
|
||||||
|
}
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
?>
|
?>
|
||||||
<!doctype html>
|
<?php include '_header.php'; ?>
|
||||||
<html lang="en">
|
<?php include '_navbar.php'; ?>
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
<div class="container-fluid">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<div class="row">
|
||||||
<title>New Style</title>
|
<?php include '_sidebar.php'; ?>
|
||||||
<?php
|
|
||||||
// Read project preview data from environment
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
<?php if (isset($_SESSION['success_message'])): ?>
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert">
|
||||||
?>
|
<?= $_SESSION['success_message']; ?>
|
||||||
<?php if ($projectDescription): ?>
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
<!-- Meta description -->
|
</div>
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
<?php unset($_SESSION['success_message']); ?>
|
||||||
<!-- Open Graph meta tags -->
|
<script>
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
setTimeout(function() {
|
||||||
<!-- Twitter meta tags -->
|
let alert = document.querySelector('.alert-success');
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
if (alert) {
|
||||||
<?php endif; ?>
|
new bootstrap.Alert(alert).close();
|
||||||
<?php if ($projectImageUrl): ?>
|
}
|
||||||
<!-- Open Graph image -->
|
}, 5000);
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
</script>
|
||||||
<!-- Twitter image -->
|
<?php endif; ?>
|
||||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<?php endif; ?>
|
<?php if (isset($_SESSION['error_message'])): ?>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<div class="alert alert-danger alert-dismissible fade show mt-3" role="alert">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<?= $_SESSION['error_message']; ?>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
<style>
|
</div>
|
||||||
:root {
|
<?php unset($_SESSION['error_message']); ?>
|
||||||
--bg-color-start: #6a11cb;
|
<script>
|
||||||
--bg-color-end: #2575fc;
|
setTimeout(function() {
|
||||||
--text-color: #ffffff;
|
let alert = document.querySelector('.alert-danger');
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
if (alert) {
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
new bootstrap.Alert(alert).close();
|
||||||
}
|
}
|
||||||
body {
|
}, 5000);
|
||||||
margin: 0;
|
</script>
|
||||||
font-family: 'Inter', sans-serif;
|
<?php endif; ?>
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
|
||||||
color: var(--text-color);
|
<div class="d-flex justify-content-between align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
display: flex;
|
<h1 class="h2">Process Dashboard</h1>
|
||||||
justify-content: center;
|
<div class="btn-toolbar mb-2 mb-md-0">
|
||||||
align-items: center;
|
<div class="btn-group me-2" id="bulk-actions-group" style="display: none;">
|
||||||
min-height: 100vh;
|
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
text-align: center;
|
Bulk Actions
|
||||||
overflow: hidden;
|
</button>
|
||||||
position: relative;
|
<ul class="dropdown-menu">
|
||||||
}
|
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#bulkStatusModal">Bulk Status Update</a></li>
|
||||||
body::before {
|
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#bulkEventModal">Bulk Add Event</a></li>
|
||||||
content: '';
|
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#bulkInitModal">Bulk Initialize Instances</a></li>
|
||||||
position: absolute;
|
</ul>
|
||||||
top: 0;
|
</div>
|
||||||
left: 0;
|
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#createPersonModal">
|
||||||
width: 100%;
|
Create Person
|
||||||
height: 100%;
|
</button>
|
||||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
</div>
|
||||||
animation: bg-pan 20s linear infinite;
|
</div>
|
||||||
z-index: -1;
|
|
||||||
}
|
<div class="table-responsive">
|
||||||
@keyframes bg-pan {
|
<table class="table table-bordered table-sm">
|
||||||
0% { background-position: 0% 0%; }
|
<thead>
|
||||||
100% { background-position: 100% 100%; }
|
<tr class="text-center">
|
||||||
}
|
<th class="bg-light"><input type="checkbox" id="selectAll"></th>
|
||||||
main {
|
<th class="bg-light">Person</th>
|
||||||
padding: 2rem;
|
<?php foreach ($processes as $process): ?>
|
||||||
}
|
<th style="writing-mode: vertical-lr;" class="bg-light"><?= htmlspecialchars($process['name']) ?></th>
|
||||||
.card {
|
<?php endforeach; ?>
|
||||||
background: var(--card-bg-color);
|
</tr>
|
||||||
border: 1px solid var(--card-border-color);
|
</thead>
|
||||||
border-radius: 16px;
|
<tbody>
|
||||||
padding: 2rem;
|
<?php foreach ($people as $person): ?>
|
||||||
backdrop-filter: blur(20px);
|
<tr>
|
||||||
-webkit-backdrop-filter: blur(20px);
|
<td><input type="checkbox" class="person-checkbox" name="person_ids[]" value="<?= $person['id'] ?>"></td>
|
||||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
<td class="d-flex justify-content-between align-items-start">
|
||||||
}
|
<div>
|
||||||
.loader {
|
<?= htmlspecialchars($person['firstName'] . ' ' . $person['lastName']) ?>
|
||||||
margin: 1.25rem auto 1.25rem;
|
<br>
|
||||||
width: 48px;
|
<small class="text-muted"><?= htmlspecialchars($person['companyName']) ?></small>
|
||||||
height: 48px;
|
<br>
|
||||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
<small class="text-info fw-bold"><?= htmlspecialchars(ucfirst($person['role'])) ?></small>
|
||||||
border-top-color: #fff;
|
<br>
|
||||||
border-radius: 50%;
|
<small class="text-muted">
|
||||||
animation: spin 1s linear infinite;
|
<?php
|
||||||
}
|
$person_function_names = [];
|
||||||
@keyframes spin {
|
if (isset($person_functions_map[$person['id']])) {
|
||||||
from { transform: rotate(0deg); }
|
foreach ($person_functions_map[$person['id']] as $function_id) {
|
||||||
to { transform: rotate(360deg); }
|
if (isset($functions_by_id[$function_id])) {
|
||||||
}
|
$person_function_names[] = htmlspecialchars($functions_by_id[$function_id]);
|
||||||
.hint {
|
}
|
||||||
opacity: 0.9;
|
}
|
||||||
}
|
}
|
||||||
.sr-only {
|
echo implode(', ', $person_function_names);
|
||||||
position: absolute;
|
?>
|
||||||
width: 1px; height: 1px;
|
</small>
|
||||||
padding: 0; margin: -1px;
|
</div>
|
||||||
overflow: hidden;
|
<div>
|
||||||
clip: rect(0, 0, 0, 0);
|
<button class="btn btn-sm btn-secondary edit-btn" data-bs-toggle="modal" data-bs-target="#editPersonModal"
|
||||||
white-space: nowrap; border: 0;
|
data-person-id="<?= $person['id'] ?>"
|
||||||
}
|
data-person-name="<?= htmlspecialchars($person['firstName'] . ' ' . $person['lastName']) ?>">
|
||||||
h1 {
|
Edit
|
||||||
font-size: 3rem;
|
</button>
|
||||||
font-weight: 700;
|
</div>
|
||||||
margin: 0 0 1rem;
|
</td>
|
||||||
letter-spacing: -1px;
|
<?php foreach ($processes as $process):
|
||||||
}
|
$instance = isset($instances[$person['id']][$process['id']]) ? $instances[$person['id']][$process['id']] : null;
|
||||||
p {
|
$status = $instance ? $instance['current_status'] : 'none';
|
||||||
margin: 0.5rem 0;
|
$color = $status_colors[$status];
|
||||||
font-size: 1.1rem;
|
$lastActivity = $instance && $instance['lastActivityAt'] ? date('d/m/y', strtotime($instance['lastActivityAt'])) : '';
|
||||||
}
|
?>
|
||||||
code {
|
<td class="text-center" data-bs-toggle="modal" data-bs-target="#instanceModal" data-person-id="<?= $person['id'] ?>" data-process-id="<?= $process['id'] ?>" style="cursor: pointer;">
|
||||||
background: rgba(0,0,0,0.2);
|
<span class="badge rounded-circle bg-<?= $color ?>" style="width: 20px; height: 20px; display: inline-block;" title="<?= ucfirst($status) ?>"> </span>
|
||||||
padding: 2px 6px;
|
<small class="text-muted d-block"><?= $lastActivity ?></small>
|
||||||
border-radius: 4px;
|
</td>
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
<?php endforeach; ?>
|
||||||
}
|
</tr>
|
||||||
footer {
|
<?php endforeach; ?>
|
||||||
position: absolute;
|
</tbody>
|
||||||
bottom: 1rem;
|
</table>
|
||||||
font-size: 0.8rem;
|
</div>
|
||||||
opacity: 0.7;
|
</main>
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<div class="card">
|
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
|
||||||
<span class="sr-only">Loading…</span>
|
|
||||||
</div>
|
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
<footer>
|
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
<!-- Edit Person Modal -->
|
||||||
</footer>
|
<div class="modal fade" id="editPersonModal" tabindex="-1" aria-labelledby="editPersonModalLabel" aria-hidden="true">
|
||||||
</body>
|
<div class="modal-dialog modal-xl">
|
||||||
</html>
|
<div class="modal-content">
|
||||||
|
<form id="editPersonForm" action="_update_person.php" method="post">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="editPersonModalLabel">Edytuj osobę</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" name="id" id="editPersonId">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editFirstName" class="form-label">Imię</label>
|
||||||
|
<input type="text" class="form-control" id="editFirstName" name="firstName" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editLastName" class="form-label">Nazwisko</label>
|
||||||
|
<input type="text" class="form-control" id="editLastName" name="lastName" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editCompanyName" class="form-label">Nazwa firmy</label>
|
||||||
|
<input type="text" class="form-control" id="editCompanyName" name="companyName">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editPhone" class="form-label">Numer telefonu</label>
|
||||||
|
<input type="text" class="form-control" id="editPhone" name="phone">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editEmail" class="form-label">Email (login)</label>
|
||||||
|
<input type="email" class="form-control" id="editEmail" name="email">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editPassword" class="form-label">Nowe hasło</label>
|
||||||
|
<input type="password" class="form-control" id="editPassword" name="password">
|
||||||
|
<small class="form-text text-muted">Pozostaw puste, jeśli nie chcesz zmieniać hasła.</small>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editRole" class="form-label">Rola</label>
|
||||||
|
<select class="form-select" id="editRole" name="role" required>
|
||||||
|
<option value="admin">Admin</option>
|
||||||
|
<option value="member">Członek</option>
|
||||||
|
<option value="guest">Gość</option>
|
||||||
|
<option value="team_member">Pracownik Biura</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="editRoles" class="form-label">Funkcje</label>
|
||||||
|
<select class="form-select" id="editRoles" name="functions[]" multiple size="5">
|
||||||
|
<!-- Opcje zostaną wstawione przez JavaScript -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Zamknij</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Zapisz zmiany</button>
|
||||||
|
<button type="button" class="btn btn-danger ms-auto" id="deleteUserBtn" data-bs-toggle="modal" data-bs-target="#deletePersonModal">Usuń</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Create Person Modal -->
|
||||||
|
<div class="modal fade" id="createPersonModal" tabindex="-1" aria-labelledby="createPersonModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-xl">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="createPersonModalLabel">Create Person</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="createPersonForm" action="_create_person.php" method="post">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="createFirstName" class="form-label">First Name <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control" id="createFirstName" name="firstName" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="createLastName" class="form-label">Last Name <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control" id="createLastName" name="lastName" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="createCompanyName" class="form-label">Company Name</label>
|
||||||
|
<input type="text" class="form-control" id="createCompanyName" name="companyName">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="createPhone" class="form-label">Phone Number</label>
|
||||||
|
<input type="text" class="form-control" id="createPhone" name="phone">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="createEmail" class="form-label">Email (Login) <span class="text-danger">*</span></label>
|
||||||
|
<input type="email" class="form-control" id="createEmail" name="email" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="createPassword" class="form-label">Password <span class="text-danger">*</span></label>
|
||||||
|
<input type="password" class="form-control" id="createPassword" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="createRole" class="form-label">Rola</label>
|
||||||
|
<select class="form-select" id="createRole" name="role" required>
|
||||||
|
<option value="admin">Admin</option>
|
||||||
|
<option value="member" selected>Członek</option>
|
||||||
|
<option value="guest">Gość</option>
|
||||||
|
<option value="team_member">Pracownik Biura</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="createRoles" class="form-label">Funkcje</label>
|
||||||
|
<select class="form-select" id="createRoles" name="functions[]" multiple size="5">
|
||||||
|
<?php foreach ($all_functions as $function): ?>
|
||||||
|
<option value="<?= $function['id'] ?>"><?= htmlspecialchars($function['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Create Person</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Delete Person Modal -->
|
||||||
|
<div class="modal fade" id="deletePersonModal" tabindex="-1" aria-labelledby="deletePersonModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="deletePersonModalLabel">Potwierdź usunięcie</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Czy na pewno chcesz usunąć użytkownika <strong id="personNameToDelete"></strong>? Tej operacji nie można cofnąć.
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Anuluj</button>
|
||||||
|
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">Tak, usuń</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<?php include '_footer.php'; ?>
|
||||||
|
|
||||||
|
<!-- Instance Modal -->
|
||||||
|
<div class="modal fade" id="instanceModal" tabindex="-1" aria-labelledby="instanceModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="instanceModalLabel">Process Details</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<!-- Content will be loaded here via AJAX -->
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var instanceModal = document.getElementById('instanceModal');
|
||||||
|
instanceModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
var button = event.relatedTarget;
|
||||||
|
var personId = button.dataset.personId;
|
||||||
|
var processId = button.dataset.processId;
|
||||||
|
var modalBody = instanceModal.querySelector('.modal-body');
|
||||||
|
|
||||||
|
// Load content via AJAX
|
||||||
|
fetch(`_get_instance_details.php?personId=${personId}&processId=${processId}`)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(html => {
|
||||||
|
modalBody.innerHTML = html;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bulk actions
|
||||||
|
const selectAll = document.getElementById('selectAll');
|
||||||
|
const checkboxes = document.querySelectorAll('.person-checkbox');
|
||||||
|
const bulkActionsGroup = document.getElementById('bulk-actions-group');
|
||||||
|
|
||||||
|
function toggleBulkActions() {
|
||||||
|
const anyChecked = Array.from(checkboxes).some(c => c.checked);
|
||||||
|
bulkActionsGroup.style.display = anyChecked ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
selectAll.addEventListener('change', function () {
|
||||||
|
checkboxes.forEach(c => c.checked = selectAll.checked);
|
||||||
|
toggleBulkActions();
|
||||||
|
});
|
||||||
|
|
||||||
|
checkboxes.forEach(c => c.addEventListener('change', toggleBulkActions));
|
||||||
|
|
||||||
|
// Pass selected people to modals
|
||||||
|
function setupBulkModal(modalId, hiddenInputId) {
|
||||||
|
const modal = document.getElementById(modalId);
|
||||||
|
modal.addEventListener('show.bs.modal', function() {
|
||||||
|
const selectedIds = Array.from(checkboxes).filter(c => c.checked).map(c => c.value);
|
||||||
|
document.getElementById(hiddenInputId).value = JSON.stringify(selectedIds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupBulkModal('bulkStatusModal', 'bulkStatusPersonIds');
|
||||||
|
setupBulkModal('bulkEventModal', 'bulkEventPersonIds');
|
||||||
|
setupBulkModal('bulkInitModal', 'bulkInitPersonIds');
|
||||||
|
|
||||||
|
// Populate edit person modal
|
||||||
|
const editPersonModal = document.getElementById('editPersonModal');
|
||||||
|
if(editPersonModal) {
|
||||||
|
editPersonModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
const button = event.relatedTarget;
|
||||||
|
const personId = button.getAttribute('data-person-id');
|
||||||
|
var personName = button.getAttribute('data-person-name');
|
||||||
|
|
||||||
|
var deleteBtn = document.getElementById('deleteUserBtn');
|
||||||
|
deleteBtn.dataset.personId = personId;
|
||||||
|
deleteBtn.dataset.personName = personName;
|
||||||
|
|
||||||
|
fetch('_get_person_details.php?id=' + personId)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
document.getElementById('editPersonId').value = data.person.id;
|
||||||
|
document.getElementById('editFirstName').value = data.person.firstName;
|
||||||
|
document.getElementById('editLastName').value = data.person.lastName;
|
||||||
|
document.getElementById('editEmail').value = data.person.email;
|
||||||
|
document.getElementById('editCompanyName').value = data.person.companyName;
|
||||||
|
document.getElementById('editPhone').value = data.person.phone;
|
||||||
|
document.getElementById('editRole').value = data.person.role;
|
||||||
|
|
||||||
|
const functionsSelect = document.getElementById('editRoles');
|
||||||
|
functionsSelect.innerHTML = ''; // Clear existing options
|
||||||
|
|
||||||
|
data.all_functions.forEach(func => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = func.id;
|
||||||
|
option.textContent = func.name;
|
||||||
|
if (data.person_functions.includes(func.id)) {
|
||||||
|
option.selected = true;
|
||||||
|
}
|
||||||
|
functionsSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// When the delete button in the edit modal is clicked, pass the data to the delete confirmation modal
|
||||||
|
document.getElementById('deleteUserBtn').addEventListener('click', function() {
|
||||||
|
var personId = this.dataset.personId;
|
||||||
|
var personName = this.dataset.personName;
|
||||||
|
|
||||||
|
var deletePersonModal = document.getElementById('deletePersonModal');
|
||||||
|
deletePersonModal.querySelector('#personNameToDelete').textContent = personName;
|
||||||
|
deletePersonModal.dataset.personId = personId;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Populate delete person modal
|
||||||
|
var deletePersonModal = document.getElementById('deletePersonModal');
|
||||||
|
deletePersonModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
var button = event.relatedTarget;
|
||||||
|
if (button.id !== 'deleteUserBtn') {
|
||||||
|
var personId = button.dataset.personId;
|
||||||
|
var personName = button.dataset.personName;
|
||||||
|
document.getElementById('personNameToDelete').textContent = personName;
|
||||||
|
deletePersonModal.dataset.personId = personId;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('confirmDeleteBtn').addEventListener('click', function() {
|
||||||
|
let personIdToDelete = deletePersonModal.dataset.personId;
|
||||||
|
if (personIdToDelete) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('person_id', personIdToDelete);
|
||||||
|
|
||||||
|
fetch('_delete_person.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const modal = bootstrap.Modal.getInstance(deletePersonModal);
|
||||||
|
modal.hide();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
let errorAlert = document.querySelector('.alert-danger');
|
||||||
|
let errorContainer = errorAlert.parentElement;
|
||||||
|
if (!errorAlert) {
|
||||||
|
errorContainer = document.querySelector('main'); // fallback
|
||||||
|
const alertHTML = `
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show mt-3" role="alert">
|
||||||
|
${data.error}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>`;
|
||||||
|
errorContainer.insertAdjacentHTML('afterbegin', alertHTML);
|
||||||
|
} else {
|
||||||
|
errorAlert.innerHTML = `${data.error} <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>`;
|
||||||
|
errorAlert.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
const modal = bootstrap.Modal.getInstance(deletePersonModal);
|
||||||
|
modal.hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Bulk Modals -->
|
||||||
|
<!-- Bulk Status Update Modal -->
|
||||||
|
<div class="modal fade" id="bulkStatusModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Bulk Status Update</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="_bulk_update_status.php" method="post">
|
||||||
|
<input type="hidden" name="person_ids" id="bulkStatusPersonIds">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Process</label>
|
||||||
|
<select name="process_id" class="form-select" required>
|
||||||
|
<?php foreach($processes as $process): ?>
|
||||||
|
<option value="<?= $process['id'] ?>"><?= htmlspecialchars($process['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">New Status</label>
|
||||||
|
<select name="status" class="form-select" required>
|
||||||
|
<option value="none">None</option>
|
||||||
|
<option value="negative">Negative</option>
|
||||||
|
<option value="in_progress">In Progress</option>
|
||||||
|
<option value="positive">Positive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Update Status</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bulk Add Event Modal -->
|
||||||
|
<div class="modal fade" id="bulkEventModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Bulk Add Event</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="_bulk_add_event.php" method="post">
|
||||||
|
<input type="hidden" name="person_ids" id="bulkEventPersonIds">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Process</label>
|
||||||
|
<select name="process_id" class="form-select" required>
|
||||||
|
<?php foreach($processes as $process): ?>
|
||||||
|
<option value="<?= $process['id'] ?>"><?= htmlspecialchars($process['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Event Description</label>
|
||||||
|
<textarea name="description" class="form-control" required></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Add Event</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bulk Initialize Instances Modal -->
|
||||||
|
<div class="modal fade" id="bulkInitModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Bulk Initialize Instances</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="_bulk_init_instances.php" method="post">
|
||||||
|
<input type="hidden" name="person_ids" id="bulkInitPersonIds">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Process</label> <select name="process_id" class="form-select" required>
|
||||||
|
<?php foreach($processes as $process): ?>
|
||||||
|
<option value="<?= $process['id'] ?>"><?= htmlspecialchars($process['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p>This will initialize this process for all selected people if it is not already initialized.</p>
|
||||||
|
<button type="submit" class="btn btn-primary">Initialize</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
80
login.php
Normal file
80
login.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$email = $_POST['email'] ?? '';
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
if (empty($email) || empty($password)) {
|
||||||
|
$error = 'Proszę podać email i hasło.';
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM people WHERE email = ? AND is_user = TRUE AND active = TRUE");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
$person = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($person && password_verify($password, $person['password'])) {
|
||||||
|
$_SESSION['user_id'] = $person['id'];
|
||||||
|
$_SESSION['user_email'] = $person['email'];
|
||||||
|
$_SESSION['user_role'] = $person['role'];
|
||||||
|
$_SESSION['user_name'] = $person['firstName'] . ' ' . $person['lastName'];
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$error = 'Nieprawidłowe dane logowania. Spróbuj ponownie.';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$error = 'Błąd bazy danych: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Logowanie</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
.login-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="card login-card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-center mb-4">Logowanie do systemu</h3>
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<form method="POST" action="login.php">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Adres email</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Hasło</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary">Zaloguj się</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6
logout.php
Normal file
6
logout.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
session_unset();
|
||||||
|
session_destroy();
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
62
migrate.php
Normal file
62
migrate.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
function run_migrations() {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create migrations table if it doesn't exist
|
||||||
|
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (id INT AUTO_INCREMENT PRIMARY KEY, migration VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
|
||||||
|
|
||||||
|
// Get all run migrations
|
||||||
|
$stmt = $pdo->query("SELECT migration FROM migrations");
|
||||||
|
$run_migrations = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
// Get all migration files
|
||||||
|
$migration_files = glob('db/migrations/*.php');
|
||||||
|
|
||||||
|
foreach ($migration_files as $file) {
|
||||||
|
$migration_name = basename($file, '.php');
|
||||||
|
|
||||||
|
if (!in_array($migration_name, $run_migrations)) {
|
||||||
|
echo "Running migration: $migration_name...\n";
|
||||||
|
require_once $file;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Migration functions are named like migrate_001, migrate_002 etc.
|
||||||
|
$function_name = 'migrate_' . preg_replace('/[^0-9]/', '', $migration_name);
|
||||||
|
if(function_exists($function_name)){
|
||||||
|
$function_name($pdo);
|
||||||
|
} else {
|
||||||
|
// Fallback for older naming convention
|
||||||
|
$function_name = str_replace('.php', '', basename($file));
|
||||||
|
if(function_exists($function_name)) {
|
||||||
|
$function_name($pdo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record migration
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)");
|
||||||
|
$stmt->execute([$migration_name]);
|
||||||
|
echo "Migration $migration_name has been applied.\n";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error running migration $migration_name: " . $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Migration $migration_name already applied.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "All migrations have been run.\n";
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Migration failed: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the script is run directly from the command line
|
||||||
|
if (php_sapi_name() === 'cli') {
|
||||||
|
run_migrations();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
158
process_definitions.php
Normal file
158
process_definitions.php
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'WorkflowEngine.php';
|
||||||
|
|
||||||
|
$workflowEngine = new WorkflowEngine();
|
||||||
|
// TODO: Create a method in WorkflowEngine to get all process definitions
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->query("SELECT * FROM process_definitions ORDER BY name");
|
||||||
|
$processes = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<?php include '_header.php'; ?>
|
||||||
|
<?php include '_navbar.php'; ?>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<?php include '_sidebar.php'; ?>
|
||||||
|
|
||||||
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2">Process Definitions</h1>
|
||||||
|
<div class="btn-toolbar mb-2 mb-md-0">
|
||||||
|
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#createProcessModal">
|
||||||
|
Create Process
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr class="text-center">
|
||||||
|
<th class="bg-light">Name</th>
|
||||||
|
<th class="bg-light">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($processes as $process): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($process['name']) ?></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button class="btn btn-sm btn-secondary edit-process-btn"
|
||||||
|
data-process-id="<?= $process['id'] ?>"
|
||||||
|
data-process-name="<?= htmlspecialchars($process['name']) ?>"
|
||||||
|
data-process-definition='<?= htmlspecialchars($process['definition_json'] ?? '') ?>'>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
<!-- TODO: Add delete functionality -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Create/Edit Process Modal -->
|
||||||
|
<div class="modal fade" id="createProcessModal" tabindex="-1" aria-labelledby="createProcessModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="createProcessModalLabel">Create Process</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="createProcessForm" action="_save_process_definition.php" method="post">
|
||||||
|
<input type="hidden" name="process_id" id="processId">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="processName" class="form-label">Process Name</label>
|
||||||
|
<input type="text" class="form-control" id="processName" name="name" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h5>Statuses</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="initialStatus" class="form-label">Initial Status</label>
|
||||||
|
<select class="form-select" id="initialStatus"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="statuses-list" class="mb-3"></div>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="text" id="newStatusInput" class="form-control" placeholder="New status name">
|
||||||
|
<button class="btn btn-outline-secondary" type="button" id="addStatusBtn">Add Status</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h5>Transitions</h5>
|
||||||
|
<div id="transitions-list" class="mb-3"></div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-5">
|
||||||
|
<select id="fromStatusSelect" class="form-select"></select>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 text-center">=></div>
|
||||||
|
<div class="col-5">
|
||||||
|
<select id="toStatusSelect" class="form-select"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid gap-2 mt-3">
|
||||||
|
<button class="btn btn-outline-secondary" type="button" id="addTransitionBtn">Add Transition</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea name="definition_json" id="definitionJson" class="d-none"></textarea>
|
||||||
|
|
||||||
|
<hr class="mt-4">
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Save Process</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include '_footer.php'; ?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const createProcessModal = document.getElementById('createProcessModal');
|
||||||
|
const modalTitle = createProcessModal.querySelector('.modal-title');
|
||||||
|
const form = createProcessModal.querySelector('#createProcessForm');
|
||||||
|
const processIdInput = createProcessModal.querySelector('#processId');
|
||||||
|
const processNameInput = createProcessModal.querySelector('#processName');
|
||||||
|
const definitionJsonTextarea = createProcessModal.querySelector('#definitionJson');
|
||||||
|
|
||||||
|
// Handle create button click
|
||||||
|
const createButton = document.querySelector('button[data-bs-target="#createProcessModal"]');
|
||||||
|
createButton.addEventListener('click', function() {
|
||||||
|
modalTitle.textContent = 'Create Process';
|
||||||
|
form.action = '_save_process_definition.php';
|
||||||
|
processIdInput.value = '';
|
||||||
|
processNameInput.value = '';
|
||||||
|
definitionJsonTextarea.value = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle edit button click
|
||||||
|
document.querySelectorAll('.edit-process-btn').forEach(button => {
|
||||||
|
button.addEventListener('click', function() {
|
||||||
|
const processId = this.dataset.processId;
|
||||||
|
const processName = this.dataset.processName;
|
||||||
|
const processDefinition = this.dataset.processDefinition;
|
||||||
|
|
||||||
|
modalTitle.textContent = 'Edit Process';
|
||||||
|
form.action = '_save_process_definition.php';
|
||||||
|
processIdInput.value = processId;
|
||||||
|
processNameInput.value = processName;
|
||||||
|
definitionJsonTextarea.value = processDefinition;
|
||||||
|
|
||||||
|
const modal = new bootstrap.Modal(createProcessModal);
|
||||||
|
modal.show();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Loading…
x
Reference in New Issue
Block a user