Edycja wyglądu tabeli procesów
This commit is contained in:
parent
47c0b35493
commit
ee89357279
41
_get_future_meetings.php
Normal file
41
_get_future_meetings.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$bni_group_id = isset($_GET['bni_group_id']) ? (int)$_GET['bni_group_id'] : 0;
|
||||
$offset = isset($_GET['offset']) ? (int)$_GET['offset'] : 0;
|
||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 2;
|
||||
|
||||
if ($bni_group_id === 0) {
|
||||
echo json_encode(['error' => 'Invalid BNI Group ID']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$sql = "
|
||||
SELECT ce.*, bg.name as group_name
|
||||
FROM calendar_events ce
|
||||
JOIN calendar_event_groups ceg ON ce.id = ceg.calendar_event_id
|
||||
JOIN bni_groups bg ON ceg.bni_group_id = bg.id
|
||||
WHERE ceg.bni_group_id = :bni_group_id
|
||||
AND ce.start_datetime > NOW()
|
||||
ORDER BY ce.start_datetime ASC
|
||||
LIMIT :limit OFFSET :offset
|
||||
";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->bindParam(':bni_group_id', $bni_group_id, PDO::PARAM_INT);
|
||||
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
|
||||
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
|
||||
$events = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode($events);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
|
||||
}
|
||||
356
index.php
356
index.php
@ -138,17 +138,22 @@ $bni_groups = $stmt_bni_groups->fetchAll(PDO::FETCH_ASSOC);
|
||||
<th rowspan="2" class="align-middle"><input type="checkbox" id="selectAll"></th>
|
||||
<th rowspan="2" class="align-middle">Person</th>
|
||||
<?php if (!empty($spotkania_cols)): ?>
|
||||
<th colspan="<?= count($spotkania_cols) ?>">Spotkania</th>
|
||||
<th id="spotkania-header" colspan="<?= count($spotkania_cols) ?>">Spotkania</th>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($inne_procesy_cols)): ?>
|
||||
<th colspan="<?= count($inne_procesy_cols) ?>">Inne procesy</th>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
<tr class="text-center">
|
||||
<?php foreach ($spotkania_cols as $col): ?>
|
||||
<th>
|
||||
<tr class="text-center" id="processes-header-row">
|
||||
<?php foreach ($spotkania_cols as $index => $col): ?>
|
||||
<th data-group-id="<?= $col['group_id'] ?>" data-col-index="<?= $index ?>">
|
||||
<?= htmlspecialchars($col['group_name']) ?><br>
|
||||
<small><?= $col['next_meeting_date'] ? date('d.m.Y', strtotime($col['next_meeting_date'])) : 'Brak' ?></small>
|
||||
<small>
|
||||
<?= $col['next_meeting_date'] ? date('d.m.Y', strtotime($col['next_meeting_date'])) : 'Brak' ?>
|
||||
<?php if($col['next_meeting_date']): ?>
|
||||
<i class="bi bi-arrow-right-short expand-meeting" style="cursor: pointer;" data-group-id="<?= $col['group_id'] ?>"></i>
|
||||
<?php endif; ?>
|
||||
</small>
|
||||
</th>
|
||||
<?php endforeach; ?>
|
||||
<?php foreach ($inne_procesy_cols as $col): ?>
|
||||
@ -189,8 +194,8 @@ $bni_groups = $stmt_bni_groups->fetchAll(PDO::FETCH_ASSOC);
|
||||
</td>
|
||||
|
||||
<?php // Spotkania Columns ?>
|
||||
<?php foreach ($spotkania_cols as $col): ?>
|
||||
<td class="text-center align-middle">
|
||||
<?php foreach ($spotkania_cols as $index => $col): ?>
|
||||
<td class="text-center align-middle meeting-cell" data-group-id="<?= $col['group_id'] ?>" data-col-index="<?= $index ?>">
|
||||
<?php
|
||||
// Placeholder Status: Logic for meeting attendance is not yet defined.
|
||||
// Display icon only if the person belongs to the group for that column.
|
||||
@ -518,214 +523,169 @@ $bni_groups = $stmt_bni_groups->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
<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');
|
||||
// --- STATE MANAGEMENT ---
|
||||
const meetingsState = {};
|
||||
|
||||
// Load content via AJAX
|
||||
fetch(`_get_instance_details.php?personId=${personId}&processId=${processId}`)
|
||||
// --- MODAL LOGIC ---
|
||||
const instanceModal = document.getElementById('instanceModal');
|
||||
if (instanceModal) {
|
||||
instanceModal.addEventListener('show.bs.modal', function (event) {
|
||||
const button = event.relatedTarget;
|
||||
const personId = button.getAttribute('data-person-id');
|
||||
const processId = button.getAttribute('data-process-id');
|
||||
const modalBody = instanceModal.querySelector('.modal-body');
|
||||
const modalTitle = instanceModal.querySelector('.modal-title');
|
||||
|
||||
modalBody.innerHTML = '<div class="d-flex justify-content-center"><div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div></div>';
|
||||
modalTitle.textContent = 'Ładowanie...';
|
||||
|
||||
fetch(`_get_instance_details.php?person_id=${personId}&process_id=${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';
|
||||
const newTitle = modalBody.querySelector('#instance-modal-title');
|
||||
if (newTitle) {
|
||||
modalTitle.innerHTML = newTitle.innerHTML;
|
||||
newTitle.remove();
|
||||
} else {
|
||||
modalTitle.textContent = 'Szczegóły procesu';
|
||||
}
|
||||
|
||||
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;
|
||||
document.getElementById('editBniGroup').value = data.person.bni_group_id || '';
|
||||
|
||||
document.getElementById('editNip').value = data.person.nip || '';
|
||||
document.getElementById('editIndustry').value = data.person.industry || '';
|
||||
document.getElementById('editCompanySize').value = data.person.company_size_revenue || '';
|
||||
document.getElementById('editBusinessDescription').value = data.person.business_description || '';
|
||||
|
||||
document.getElementById('editCompanyLogoPath').textContent = data.person.company_logo_path || '';
|
||||
document.getElementById('editPersonPhotoPath').textContent = data.person.person_photo_path || '';
|
||||
document.getElementById('editGainsSheetPath').textContent = data.person.gains_sheet_path || '';
|
||||
document.getElementById('editTopWantedPath').textContent = data.person.top_wanted_contacts_path || '';
|
||||
document.getElementById('editTopOwnedPath').textContent = data.person.top_owned_contacts_path || '';
|
||||
|
||||
// Trigger change to show/hide group div and member-only fields
|
||||
const editRoleSelect = document.getElementById('editRole');
|
||||
editRoleSelect.dispatchEvent(new Event('change'));
|
||||
|
||||
const functionsSelect = document.getElementById('editRoles');
|
||||
functionsSelect.innerHTML = ''; // Clear existing options
|
||||
|
||||
// Group functions by group_name
|
||||
const groupedFunctions = data.all_functions.reduce((acc, func) => {
|
||||
const groupName = func.group_name || 'Other';
|
||||
if (!acc[groupName]) {
|
||||
acc[groupName] = [];
|
||||
}
|
||||
acc[groupName].push(func);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Populate select with optgroups
|
||||
for (const groupName in groupedFunctions) {
|
||||
const optgroup = document.createElement('optgroup');
|
||||
optgroup.label = groupName;
|
||||
groupedFunctions[groupName].forEach(func => {
|
||||
const option = document.createElement('option');
|
||||
option.value = func.id;
|
||||
option.textContent = func.name;
|
||||
if (data.person_functions.map(String).includes(String(func.id))) {
|
||||
option.selected = true;
|
||||
}
|
||||
optgroup.appendChild(option);
|
||||
});
|
||||
functionsSelect.appendChild(optgroup);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching instance details:', error);
|
||||
modalBody.innerHTML = '<p class="text-danger">Wystąpił błąd podczas ładowania danych.</p>';
|
||||
modalTitle.textContent = 'Błąd';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- GROUP FILTER LOGIC ---
|
||||
const groupFilter = document.getElementById('groupFilter');
|
||||
if (groupFilter) {
|
||||
groupFilter.addEventListener('change', function () {
|
||||
const selectedGroupId = this.value;
|
||||
document.querySelectorAll('tbody tr').forEach(row => {
|
||||
const rowGroupId = row.getAttribute('data-group-id');
|
||||
row.style.display = (selectedGroupId === '' || rowGroupId === selectedGroupId) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- EXPAND MEETINGS LOGIC ---
|
||||
const headerRow = document.getElementById('processes-header-row');
|
||||
if (headerRow) {
|
||||
headerRow.addEventListener('click', function (event) {
|
||||
const expandBtn = event.target.closest('.expand-meeting');
|
||||
if (!expandBtn) return;
|
||||
|
||||
const groupId = expandBtn.dataset.groupId;
|
||||
expandBtn.style.display = 'none';
|
||||
|
||||
if (!meetingsState[groupId]) {
|
||||
meetingsState[groupId] = { offset: 1 };
|
||||
}
|
||||
|
||||
const limit = 2;
|
||||
const offset = meetingsState[groupId].offset;
|
||||
meetingsState[groupId].offset += limit;
|
||||
|
||||
const fetchUrl = `_get_future_meetings.php?bni_group_id=${groupId}&limit=${limit}&offset=${offset}`;
|
||||
|
||||
fetch(fetchUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const modal = bootstrap.Modal.getInstance(deletePersonModal);
|
||||
modal.hide();
|
||||
if (data.error) {
|
||||
console.error('BŁĄD Z SERWERA:', data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
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');
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
let lastThForGroup = findLastElementForGroup(headerRow, 'th', groupId);
|
||||
if (!lastThForGroup) {
|
||||
console.error('KRYTYCZNY BŁĄD: Nie można znaleźć nagłówka startowego (TH) dla grupy.');
|
||||
return;
|
||||
}
|
||||
|
||||
data.forEach((meeting) => {
|
||||
const newTh = document.createElement('th');
|
||||
newTh.dataset.groupId = groupId;
|
||||
newTh.className = 'text-center';
|
||||
|
||||
const meetingDate = new Date(meeting.start_datetime);
|
||||
const formattedDate = meetingDate.toLocaleDateString('pl-PL', { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||
|
||||
// In a real scenario, you'd get this info from the backend
|
||||
const isLastInBatch = false;
|
||||
const isLastInGroup = false;
|
||||
|
||||
let iconHtml = '';
|
||||
if (isLastInBatch && !isLastInGroup) {
|
||||
iconHtml = ` <i class="bi bi-arrow-right-short expand-meeting" data-group-id="${groupId}" style="cursor: pointer;"></i>`;
|
||||
}
|
||||
|
||||
newTh.innerHTML = `<span class=\"text-muted\">${htmlspecialchars(meeting.group_name || '')}</span><br><small>${formattedDate}${iconHtml}</small>`;
|
||||
|
||||
lastThForGroup.after(newTh);
|
||||
lastThForGroup = newTh;
|
||||
});
|
||||
|
||||
document.querySelectorAll('tbody tr').forEach((personRow) => {
|
||||
let lastTdForGroup = findLastElementForGroup(personRow, 'td.meeting-cell', groupId);
|
||||
|
||||
if (!lastTdForGroup) {
|
||||
// Fallback for rows that might not have the initial meeting cell
|
||||
const allHeaders = Array.from(headerRow.children);
|
||||
const initialTh = findLastElementForGroup(headerRow, 'th', groupId, true);
|
||||
const anchorIndex = allHeaders.indexOf(initialTh);
|
||||
lastTdForGroup = personRow.children[anchorIndex];
|
||||
}
|
||||
|
||||
if (!lastTdForGroup) {
|
||||
return; // Skip row if anchor is still not found
|
||||
}
|
||||
|
||||
data.forEach(() => {
|
||||
const newTd = document.createElement('td');
|
||||
newTd.dataset.groupId = groupId;
|
||||
newTd.className = 'text-center align-middle meeting-cell';
|
||||
|
||||
if (personRow.dataset.groupId === groupId) {
|
||||
newTd.innerHTML = `<span class="badge rounded-circle bg-secondary" style="width: 20px; height: 20px; display: inline-block;" title="Status nieokreślony"></span>`;
|
||||
}
|
||||
|
||||
lastTdForGroup.after(newTd);
|
||||
lastTdForGroup = newTd;
|
||||
});
|
||||
});
|
||||
|
||||
const spotkaniaHeader = document.getElementById('spotkania-header');
|
||||
if (spotkaniaHeader) {
|
||||
spotkaniaHeader.colSpan = (spotkaniaHeader.colSpan || 1) + data.length;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
const modal = bootstrap.Modal.getInstance(deletePersonModal);
|
||||
modal.hide();
|
||||
console.error('KRYTYCZNY BŁĄD SIECI LUB PARSOWANIA:', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle role change for group visibility
|
||||
const createRoleSelect = document.getElementById('createRole');
|
||||
const createGroupDiv = document.getElementById('create-group-selection-div');
|
||||
const createMemberOnlyFields = document.querySelector('#createPersonModal .member-only-fields');
|
||||
|
||||
createRoleSelect.addEventListener('change', function() {
|
||||
const isMember = this.value === 'member';
|
||||
createGroupDiv.style.display = isMember ? 'block' : 'none';
|
||||
createMemberOnlyFields.style.display = isMember ? 'block' : 'none';
|
||||
});
|
||||
// Initial check
|
||||
const isMemberCreate = createRoleSelect.value === 'member';
|
||||
createGroupDiv.style.display = isMemberCreate ? 'block' : 'none';
|
||||
createMemberOnlyFields.style.display = isMemberCreate ? 'block' : 'none';
|
||||
|
||||
|
||||
const editRoleSelect = document.getElementById('editRole');
|
||||
const editGroupDiv = document.getElementById('edit-group-selection-div');
|
||||
const editMemberOnlyFields = document.querySelector('#editPersonModal .member-only-fields');
|
||||
|
||||
editRoleSelect.addEventListener('change', function() {
|
||||
const isMember = this.value === 'member';
|
||||
editGroupDiv.style.display = isMember ? 'block' : 'none';
|
||||
editMemberOnlyFields.style.display = isMember ? 'block' : 'none';
|
||||
});
|
||||
|
||||
function findLastElementForGroup(parent, selector, groupId, findFirst = false) {
|
||||
const elements = parent.querySelectorAll(`${selector}[data-group-id="${groupId}"]`);
|
||||
if (elements.length === 0) return null;
|
||||
return findFirst ? elements[0] : elements[elements.length - 1];
|
||||
}
|
||||
|
||||
function htmlspecialchars(str) {
|
||||
if (typeof str !== 'string') return '';
|
||||
return str.replace(/[&<>"']/g, match => ({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
}[match]));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user