37338-vm/index.php
2026-01-10 07:48:27 +00:00

574 lines
29 KiB
PHP

<?php
require_once 'WorkflowEngine.php';
$workflowEngine = new WorkflowEngine();
$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'];
}
?>
<?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">
<?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']); ?>
<script>
setTimeout(function() {
let alert = document.querySelector('.alert-success');
if (alert) {
new bootstrap.Alert(alert).close();
}
}, 5000);
</script>
<?php endif; ?>
<?php if (isset($_SESSION['error_message'])): ?>
<div class="alert alert-danger alert-dismissible fade show mt-3" role="alert">
<?= $_SESSION['error_message']; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php unset($_SESSION['error_message']); ?>
<script>
setTimeout(function() {
let alert = document.querySelector('.alert-danger');
if (alert) {
new bootstrap.Alert(alert).close();
}
}, 5000);
</script>
<?php endif; ?>
<div class="d-flex justify-content-between align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Process Dashboard</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2" id="bulk-actions-group" style="display: none;">
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
Bulk Actions
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#bulkStatusModal">Bulk Status Update</a></li>
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#bulkEventModal">Bulk Add Event</a></li>
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#bulkInitModal">Bulk Initialize Instances</a></li>
</ul>
</div>
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#createPersonModal">
Create Person
</button>
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered table-sm">
<thead>
<tr class="text-center">
<th class="bg-light"><input type="checkbox" id="selectAll"></th>
<th class="bg-light">Person</th>
<?php foreach ($processes as $process): ?>
<th style="writing-mode: vertical-lr;" class="bg-light"><?= htmlspecialchars($process['name']) ?></th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($people as $person): ?>
<tr>
<td><input type="checkbox" class="person-checkbox" name="person_ids[]" value="<?= $person['id'] ?>"></td>
<td class="d-flex justify-content-between align-items-start">
<div>
<?= htmlspecialchars($person['firstName'] . ' ' . $person['lastName']) ?>
<br>
<small class="text-muted"><?= htmlspecialchars($person['companyName']) ?></small>
<br>
<small class="text-info fw-bold"><?= htmlspecialchars(ucfirst($person['role'])) ?></small>
<br>
<small class="text-muted">
<?php
$person_function_names = [];
if (isset($person_functions_map[$person['id']])) {
foreach ($person_functions_map[$person['id']] as $function_id) {
if (isset($functions_by_id[$function_id])) {
$person_function_names[] = htmlspecialchars($functions_by_id[$function_id]);
}
}
}
echo implode(', ', $person_function_names);
?>
</small>
</div>
<div>
<button class="btn btn-sm btn-secondary edit-btn" data-bs-toggle="modal" data-bs-target="#editPersonModal"
data-person-id="<?= $person['id'] ?>"
data-person-name="<?= htmlspecialchars($person['firstName'] . ' ' . $person['lastName']) ?>">
Edit
</button>
</div>
</td>
<?php foreach ($processes as $process):
$instance = isset($instances[$person['id']][$process['id']]) ? $instances[$person['id']][$process['id']] : null;
$status = $instance ? $instance['current_status'] : 'none';
$color = $status_colors[$status];
$lastActivity = $instance && $instance['lastActivityAt'] ? date('d/m/y', strtotime($instance['lastActivityAt'])) : '';
?>
<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;">
<span class="badge rounded-circle bg-<?= $color ?>" style="width: 20px; height: 20px; display: inline-block;" title="<?= ucfirst($status) ?>">&nbsp;</span>
<small class="text-muted d-block"><?= $lastActivity ?></small>
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</main>
</div>
</div>
<!-- Edit Person Modal -->
<div class="modal fade" id="editPersonModal" tabindex="-1" aria-labelledby="editPersonModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<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>