MVP z ruchomym paskiem
This commit is contained in:
parent
803226678c
commit
b6d5f17197
@ -42,4 +42,11 @@ if (!isset($_SESSION['user_id'])) {
|
|||||||
<link rel="icon" href="assets/pasted-20260111-144117-aba8ec29.jpg" type="image/jpeg">
|
<link rel="icon" href="assets/pasted-20260111-144117-aba8ec29.jpg" type="image/jpeg">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="<?php echo isset($_COOKIE['sidebar_collapsed']) && $_COOKIE['sidebar_collapsed'] === 'true' ? 'sidebar-collapsed' : ''; ?>">
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
if (localStorage.getItem('sidebarCollapsed') === 'true') {
|
||||||
|
document.body.classList.add('sidebar-collapsed');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
|
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
|
||||||
|
<button class="navbar-toggler d-none d-md-block" type="button" id="sidebar-toggler" aria-label="Toggle sidebar" aria-expanded="true">
|
||||||
|
<i class="bi bi-list"></i>
|
||||||
|
</button>
|
||||||
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#"><img src="assets/pasted-20260111-143449-befa41d3.png" class="d-inline-block align-top navbar-logo" alt=""> <?php echo getenv('PROJECT_NAME') ?: 'BNI obsługa regionu'; ?></a>
|
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#"><img src="assets/pasted-20260111-143449-befa41d3.png" class="d-inline-block align-top navbar-logo" alt=""> <?php echo getenv('PROJECT_NAME') ?: 'BNI obsługa regionu'; ?></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">
|
<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>
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ body {
|
|||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
#sidebar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -12,35 +12,46 @@ body {
|
|||||||
padding: 48px 0 0;
|
padding: 48px 0 0;
|
||||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
||||||
width: 250px;
|
width: 250px;
|
||||||
transition: all 0.3s;
|
transition: width 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-collapsed {
|
#main-content {
|
||||||
|
margin-left: 250px;
|
||||||
|
transition: margin-left 0.3s ease-in-out;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.sidebar-collapsed #sidebar {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-collapsed .nav-link-text {
|
body.sidebar-collapsed #main-content {
|
||||||
|
margin-left: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.sidebar-collapsed #sidebar .nav-link-text {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-collapsed .nav-link i {
|
body.sidebar-collapsed #sidebar .nav-link i {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.sidebar-collapsed #sidebar .nav-item {
|
||||||
.main-content {
|
text-align: center;
|
||||||
margin-left: 250px;
|
|
||||||
transition: margin-left 0.3s;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content-collapsed {
|
|
||||||
margin-left: 80px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
color: #333;
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link i {
|
||||||
|
margin-right: 10px;
|
||||||
|
width: 24px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link.active {
|
.nav-link.active {
|
||||||
@ -144,5 +155,14 @@ body {
|
|||||||
.navbar-logo {
|
.navbar-logo {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
width: auto;
|
width: auto;
|
||||||
margin-right: 50px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#sidebar-toggler i {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sidebar link text visible */
|
||||||
|
.nav-link-text {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
@ -1,247 +1,253 @@
|
|||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
const sidebar = document.getElementById('sidebar');
|
|
||||||
const mainContent = document.getElementById('main-content');
|
|
||||||
const sidebarToggler = document.getElementById('sidebar-toggler');
|
const sidebarToggler = document.getElementById('sidebar-toggler');
|
||||||
|
|
||||||
if (sidebarToggler) {
|
if (sidebarToggler) {
|
||||||
sidebarToggler.addEventListener('click', function () {
|
sidebarToggler.addEventListener('click', function () {
|
||||||
sidebar.classList.toggle('sidebar-collapsed');
|
const isCollapsed = document.body.classList.toggle('sidebar-collapsed');
|
||||||
mainContent.classList.toggle('main-content-collapsed');
|
localStorage.setItem('sidebarCollapsed', isCollapsed);
|
||||||
|
sidebarToggler.setAttribute('aria-expanded', !isCollapsed);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Set initial aria-expanded state
|
||||||
|
const isInitiallyCollapsed = localStorage.getItem('sidebarCollapsed') === 'true';
|
||||||
|
sidebarToggler.setAttribute('aria-expanded', !isInitiallyCollapsed);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
// --- The rest of your original main.js --- //
|
||||||
// Handler for showing the edit person modal
|
|
||||||
$('#editPersonModal').on('show.bs.modal', function (event) {
|
|
||||||
var button = $(event.relatedTarget); // Button that triggered the modal
|
|
||||||
var personId = button.data('person-id'); // Extract info from data-* attributes
|
|
||||||
var modal = $(this);
|
|
||||||
|
|
||||||
// Clear previous data
|
$(document).ready(function() {
|
||||||
modal.find('form').trigger('reset');
|
// Handler for showing the edit person modal
|
||||||
modal.find('#editPersonId').val('');
|
$('#editPersonModal').on('show.bs.modal', function (event) {
|
||||||
modal.find('#editRoles').empty();
|
var button = $(event.relatedTarget); // Button that triggered the modal
|
||||||
modal.find('#followUpSummaryContainer').empty(); // Clear summary container
|
var personId = button.data('person-id'); // Extract info from data-* attributes
|
||||||
// Clear file paths
|
var modal = $(this);
|
||||||
modal.find('#editCompanyLogoPath, #editPersonPhotoPath, #editGainsSheetPath, #editTopWantedPath, #editTopOwnedPath').text('');
|
|
||||||
|
// Clear previous data
|
||||||
if (personId) {
|
modal.find('form').trigger('reset');
|
||||||
// AJAX request to get person details
|
modal.find('#editPersonId').val('');
|
||||||
$.ajax({
|
modal.find('#editRoles').empty();
|
||||||
url: '_get_person_details.php',
|
modal.find('#followUpSummaryContainer').empty(); // Clear summary container
|
||||||
type: 'GET',
|
// Clear file paths
|
||||||
data: { id: personId },
|
modal.find('#editCompanyLogoPath, #editPersonPhotoPath, #editGainsSheetPath, #editTopWantedPath, #editTopOwnedPath').text('');
|
||||||
dataType: 'json',
|
|
||||||
success: function(response) {
|
if (personId) {
|
||||||
if (response.error) {
|
// AJAX request to get person details
|
||||||
alert('Error fetching person details: ' + response.error);
|
$.ajax({
|
||||||
return;
|
url: '_get_person_details.php',
|
||||||
}
|
type: 'GET',
|
||||||
|
data: { id: personId },
|
||||||
var person = response.person;
|
dataType: 'json',
|
||||||
var all_functions = response.all_functions;
|
success: function(response) {
|
||||||
var person_functions = response.person_functions;
|
if (response.error) {
|
||||||
var followUpSummary = response.follow_up_summary;
|
alert('Error fetching person details: ' + response.error);
|
||||||
|
return;
|
||||||
if (!person) {
|
|
||||||
alert('Could not find person data.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate the Follow-up Summary
|
|
||||||
var summaryContainer = modal.find('#followUpSummaryContainer');
|
|
||||||
if (followUpSummary) {
|
|
||||||
let summaryHtml = '<h5>Follow-up Process Summary</h5>';
|
|
||||||
summaryHtml += '<dl class="row">';
|
|
||||||
|
|
||||||
if (followUpSummary.last_call_outcome) {
|
|
||||||
summaryHtml += `<dt class="col-sm-4">Last Call Outcome</dt><dd class="col-sm-8">${followUpSummary.last_call_outcome.replace(/_/g, ' ')}</dd>`;
|
|
||||||
}
|
}
|
||||||
if (followUpSummary.last_call_date) {
|
|
||||||
summaryHtml += `<dt class="col-sm-4">Last Call Date</dt><dd class="col-sm-8">${new Date(followUpSummary.last_call_date).toLocaleString()}</dd>`;
|
var person = response.person;
|
||||||
|
var all_functions = response.all_functions;
|
||||||
|
var person_functions = response.person_functions;
|
||||||
|
var followUpSummary = response.follow_up_summary;
|
||||||
|
|
||||||
|
if (!person) {
|
||||||
|
alert('Could not find person data.');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (followUpSummary.next_contact_date) {
|
|
||||||
summaryHtml += `<dt class="col-sm-4">Next Contact Date</dt><dd class="col-sm-8">${new Date(followUpSummary.next_contact_date).toLocaleString()}</dd>`;
|
// Populate the Follow-up Summary
|
||||||
}
|
var summaryContainer = modal.find('#followUpSummaryContainer');
|
||||||
if (followUpSummary.final_outcome) {
|
if (followUpSummary) {
|
||||||
summaryHtml += `<dt class="col-sm-4">Final Status</dt><dd class="col-sm-8">${followUpSummary.final_outcome} (${followUpSummary.reason || 'N/A'})</dd>`;
|
let summaryHtml = '<h5>Follow-up Process Summary</h5>';
|
||||||
}
|
summaryHtml += '<dl class="row">';
|
||||||
|
|
||||||
summaryHtml += '</dl>';
|
if (followUpSummary.last_call_outcome) {
|
||||||
summaryContainer.html(summaryHtml);
|
summaryHtml += `<dt class="col-sm-4">Last Call Outcome</dt><dd class="col-sm-8">${followUpSummary.last_call_outcome.replace(/_/g, ' ')}</dd>`;
|
||||||
} else {
|
|
||||||
summaryContainer.html('<p class="text-muted">No Follow-up process data found for this person.</p>');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate the form fields
|
|
||||||
modal.find('#editPersonId').val(person.id);
|
|
||||||
modal.find('#editFirstName').val(person.first_name);
|
|
||||||
modal.find('#editLastName').val(person.last_name);
|
|
||||||
modal.find('#editPhone').val(person.phone);
|
|
||||||
modal.find('#editEmail').val(person.email);
|
|
||||||
modal.find('#editRole').val(person.role);
|
|
||||||
modal.find('#editBniGroup').val(person.bni_group_id);
|
|
||||||
modal.find('#editCompanyName').val(person.company_name);
|
|
||||||
modal.find('#editNip').val(person.nip);
|
|
||||||
modal.find('#editIndustry').val(person.industry);
|
|
||||||
modal.find('#editCompanySize').val(person.company_size_revenue);
|
|
||||||
modal.find('#editBusinessDescription').val(person.business_description);
|
|
||||||
|
|
||||||
// Populate file paths
|
|
||||||
if (person.company_logo_path) {
|
|
||||||
modal.find('#editCompanyLogoPath').text('Current file: ' + person.company_logo_path.split('/').pop());
|
|
||||||
}
|
|
||||||
if (person.person_photo_path) {
|
|
||||||
modal.find('#editPersonPhotoPath').text('Current file: ' + person.person_photo_path.split('/').pop());
|
|
||||||
}
|
|
||||||
if (person.gains_sheet_path) {
|
|
||||||
modal.find('#editGainsSheetPath').text('Current file: ' + person.gains_sheet_path.split('/').pop());
|
|
||||||
}
|
|
||||||
if (person.top_wanted_contacts_path) {
|
|
||||||
modal.find('#editTopWantedPath').text('Current file: ' + person.top_wanted_contacts_path.split('/').pop());
|
|
||||||
}
|
|
||||||
if (person.top_owned_contacts_path) {
|
|
||||||
modal.find('#editTopOwnedPath').text('Current file: ' + person.top_owned_contacts_path.split('/').pop());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate functions/roles dropdown and select assigned ones
|
|
||||||
var rolesSelect = modal.find('#editRoles');
|
|
||||||
rolesSelect.empty(); // Clear existing options
|
|
||||||
|
|
||||||
if (all_functions && all_functions.length > 0) {
|
|
||||||
const groupedFunctions = all_functions.reduce((acc, func) => {
|
|
||||||
const groupName = func.group_name || 'General';
|
|
||||||
if (!acc[groupName]) {
|
|
||||||
acc[groupName] = [];
|
|
||||||
}
|
}
|
||||||
acc[groupName].push(func);
|
if (followUpSummary.last_call_date) {
|
||||||
return acc;
|
summaryHtml += `<dt class="col-sm-4">Last Call Date</dt><dd class="col-sm-8">${new Date(followUpSummary.last_call_date).toLocaleString()}</dd>`;
|
||||||
}, {});
|
}
|
||||||
|
if (followUpSummary.next_contact_date) {
|
||||||
for (const groupName in groupedFunctions) {
|
summaryHtml += `<dt class="col-sm-4">Next Contact Date</dt><dd class="col-sm-8">${new Date(followUpSummary.next_contact_date).toLocaleString()}</dd>`;
|
||||||
const optgroup = $('<optgroup>').attr('label', groupName);
|
}
|
||||||
groupedFunctions[groupName].forEach(function(func) {
|
if (followUpSummary.final_outcome) {
|
||||||
var option = $('<option></option>').val(func.id).text(func.name);
|
summaryHtml += `<dt class="col-sm-4">Final Status</dt><dd class="col-sm-8">${followUpSummary.final_outcome} (${followUpSummary.reason || 'N/A'})</dd>`;
|
||||||
if (person_functions && person_functions.includes(String(func.id))) {
|
}
|
||||||
option.prop('selected', true);
|
|
||||||
}
|
summaryHtml += '</dl>';
|
||||||
optgroup.append(option);
|
summaryContainer.html(summaryHtml);
|
||||||
});
|
} else {
|
||||||
rolesSelect.append(optgroup);
|
summaryContainer.html('<p class="text-muted">No Follow-up process data found for this person.</p>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populate the form fields
|
||||||
|
modal.find('#editPersonId').val(person.id);
|
||||||
|
modal.find('#editFirstName').val(person.first_name);
|
||||||
|
modal.find('#editLastName').val(person.last_name);
|
||||||
|
modal.find('#editPhone').val(person.phone);
|
||||||
|
modal.find('#editEmail').val(person.email);
|
||||||
|
modal.find('#editRole').val(person.role);
|
||||||
|
modal.find('#editBniGroup').val(person.bni_group_id);
|
||||||
|
modal.find('#editCompanyName').val(person.company_name);
|
||||||
|
modal.find('#editNip').val(person.nip);
|
||||||
|
modal.find('#editIndustry').val(person.industry);
|
||||||
|
modal.find('#editCompanySize').val(person.company_size_revenue);
|
||||||
|
modal.find('#editBusinessDescription').val(person.business_description);
|
||||||
|
|
||||||
|
// Populate file paths
|
||||||
|
if (person.company_logo_path) {
|
||||||
|
modal.find('#editCompanyLogoPath').text('Current file: ' + person.company_logo_path.split('/').pop());
|
||||||
|
}
|
||||||
|
if (person.person_photo_path) {
|
||||||
|
modal.find('#editPersonPhotoPath').text('Current file: ' + person.person_photo_path.split('/').pop());
|
||||||
|
}
|
||||||
|
if (person.gains_sheet_path) {
|
||||||
|
modal.find('#editGainsSheetPath').text('Current file: ' + person.gains_sheet_path.split('/').pop());
|
||||||
|
}
|
||||||
|
if (person.top_wanted_contacts_path) {
|
||||||
|
modal.find('#editTopWantedPath').text('Current file: ' + person.top_wanted_contacts_path.split('/').pop());
|
||||||
|
}
|
||||||
|
if (person.top_owned_contacts_path) {
|
||||||
|
modal.find('#editTopOwnedPath').text('Current file: ' + person.top_owned_contacts_path.split('/').pop());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate functions/roles dropdown and select assigned ones
|
||||||
|
var rolesSelect = modal.find('#editRoles');
|
||||||
|
rolesSelect.empty(); // Clear existing options
|
||||||
|
|
||||||
|
if (all_functions && all_functions.length > 0) {
|
||||||
|
const groupedFunctions = all_functions.reduce((acc, func) => {
|
||||||
|
const groupName = func.group_name || 'General';
|
||||||
|
if (!acc[groupName]) {
|
||||||
|
acc[groupName] = [];
|
||||||
|
}
|
||||||
|
acc[groupName].push(func);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
for (const groupName in groupedFunctions) {
|
||||||
|
const optgroup = $('<optgroup>').attr('label', groupName);
|
||||||
|
groupedFunctions[groupName].forEach(function(func) {
|
||||||
|
var option = $('<option></option>').val(func.id).text(func.name);
|
||||||
|
if (person_functions && person_functions.includes(String(func.id))) {
|
||||||
|
option.prop('selected', true);
|
||||||
|
}
|
||||||
|
optgroup.append(option);
|
||||||
|
});
|
||||||
|
rolesSelect.append(optgroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger change to show/hide conditional fields
|
||||||
|
modal.find('#editRole').trigger('change');
|
||||||
|
|
||||||
|
// Also set up the delete button
|
||||||
|
$('#deleteUserBtn').data('person-id', person.id);
|
||||||
|
$('#personNameToDelete').text(person.firstName + ' ' + person.lastName);
|
||||||
|
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
alert('An error occurred while fetching person data. Please try again.');
|
||||||
|
console.error("AJAX Error:", status, error);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
// Trigger change to show/hide conditional fields
|
}
|
||||||
modal.find('#editRole').trigger('change');
|
});
|
||||||
|
|
||||||
// Also set up the delete button
|
// Show/hide group selection based on role for both Edit and Create modals
|
||||||
$('#deleteUserBtn').data('person-id', person.id);
|
$(document).on('change', '#editRole, #createRole', function() {
|
||||||
$('#personNameToDelete').text(person.firstName + ' ' + person.lastName);
|
const role = $(this).val();
|
||||||
|
const isMember = role === 'member';
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
// Find the correct context (modal) for the elements
|
||||||
alert('An error occurred while fetching person data. Please try again.');
|
const modal = $(this).closest('.modal-content');
|
||||||
console.error("AJAX Error:", status, error);
|
|
||||||
|
modal.find('.member-only-fields').toggle(isMember);
|
||||||
|
modal.find('#edit-group-selection-div, #create-group-selection-div').toggle(isMember);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle Delete Person confirmation
|
||||||
|
$('#confirmDeleteBtn').on('click', function() {
|
||||||
|
var personId = $('#deleteUserBtn').data('person-id');
|
||||||
|
if (personId) {
|
||||||
|
// Use a form submission to perform the delete
|
||||||
|
var form = $('<form></form>');
|
||||||
|
form.attr("method", "post");
|
||||||
|
form.attr("action", "_delete_person.php");
|
||||||
|
|
||||||
|
var field = $('<input></input>');
|
||||||
|
field.attr("type", "hidden");
|
||||||
|
field.attr("name", "id");
|
||||||
|
field.attr("value", personId);
|
||||||
|
form.append(field);
|
||||||
|
|
||||||
|
$(document.body).append(form);
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set initial state for create form
|
||||||
|
$('#createPersonModal').on('show.bs.modal', function () {
|
||||||
|
$('#createRole').trigger('change');
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleFormSubmit(form, errorContainer, successCallback) {
|
||||||
|
event.preventDefault();
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const errorDiv = $(errorContainer).hide();
|
||||||
|
|
||||||
|
fetch(form.action, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
// Clone the response so we can read it twice (once as JSON, once as text if needed)
|
||||||
|
const responseClone = response.clone();
|
||||||
|
return response.json()
|
||||||
|
.then(data => ({ status: response.status, ok: response.ok, body: data }))
|
||||||
|
.catch(() => responseClone.text().then(text => ({ status: response.status, ok: response.ok, body: text, isText: true })));
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
const { status, ok, body, isText } = res;
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
if (isText) {
|
||||||
|
throw new Error(`Server Error: ${status}. Response: ${body}`);
|
||||||
|
}
|
||||||
|
throw new Error(body.error?.message || `An unknown server error occurred (Status: ${status})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isText) {
|
||||||
|
console.error("Received non-JSON response:", body);
|
||||||
|
throw new Error("The server sent an invalid response that could not be parsed. See console for details.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.success) {
|
||||||
|
if (successCallback) {
|
||||||
|
successCallback(body);
|
||||||
|
} else {
|
||||||
|
// Default success behavior: close modal and reload
|
||||||
|
$(form).closest('.modal').modal('hide');
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(body.error?.message || 'An operation error occurred.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
errorDiv.text(error.message).show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Show/hide group selection based on role for both Edit and Create modals
|
$('#createPersonForm').on('submit', function(event) {
|
||||||
$(document).on('change', '#editRole, #createRole', function() {
|
handleFormSubmit(this, '#createPersonError');
|
||||||
const role = $(this).val();
|
|
||||||
const isMember = role === 'member';
|
|
||||||
|
|
||||||
// Find the correct context (modal) for the elements
|
|
||||||
const modal = $(this).closest('.modal-content');
|
|
||||||
|
|
||||||
modal.find('.member-only-fields').toggle(isMember);
|
|
||||||
modal.find('#edit-group-selection-div, #create-group-selection-div').toggle(isMember);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle Delete Person confirmation
|
|
||||||
$('#confirmDeleteBtn').on('click', function() {
|
|
||||||
var personId = $('#deleteUserBtn').data('person-id');
|
|
||||||
if (personId) {
|
|
||||||
// Use a form submission to perform the delete
|
|
||||||
var form = $('<form></form>');
|
|
||||||
form.attr("method", "post");
|
|
||||||
form.attr("action", "_delete_person.php");
|
|
||||||
|
|
||||||
var field = $('<input></input>');
|
|
||||||
field.attr("type", "hidden");
|
|
||||||
field.attr("name", "id");
|
|
||||||
field.attr("value", personId);
|
|
||||||
form.append(field);
|
|
||||||
|
|
||||||
$(document.body).append(form);
|
|
||||||
form.submit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set initial state for create form
|
|
||||||
$('#createPersonModal').on('show.bs.modal', function () {
|
|
||||||
$('#createRole').trigger('change');
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleFormSubmit(form, errorContainer, successCallback) {
|
|
||||||
event.preventDefault();
|
|
||||||
const formData = new FormData(form);
|
|
||||||
const errorDiv = $(errorContainer).hide();
|
|
||||||
|
|
||||||
fetch(form.action, {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
// Clone the response so we can read it twice (once as JSON, once as text if needed)
|
|
||||||
const responseClone = response.clone();
|
|
||||||
return response.json()
|
|
||||||
.then(data => ({ status: response.status, ok: response.ok, body: data }))
|
|
||||||
.catch(() => responseClone.text().then(text => ({ status: response.status, ok: response.ok, body: text, isText: true })));
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
const { status, ok, body, isText } = res;
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
if (isText) {
|
|
||||||
throw new Error(`Server Error: ${status}. Response: ${body}`);
|
|
||||||
}
|
|
||||||
throw new Error(body.error?.message || `An unknown server error occurred (Status: ${status})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isText) {
|
|
||||||
console.error("Received non-JSON response:", body);
|
|
||||||
throw new Error("The server sent an invalid response that could not be parsed. See console for details.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (body.success) {
|
|
||||||
if (successCallback) {
|
|
||||||
successCallback(body);
|
|
||||||
} else {
|
|
||||||
// Default success behavior: close modal and reload
|
|
||||||
$(form).closest('.modal').modal('hide');
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error(body.error?.message || 'An operation error occurred.');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
errorDiv.text(error.message).show();
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
$('#editPersonForm').on('submit', function(event) {
|
||||||
$('#createPersonForm').on('submit', function(event) {
|
handleFormSubmit(this, '#editPersonError', function(data) {
|
||||||
handleFormSubmit(this, '#createPersonError');
|
// close modal and reload page
|
||||||
});
|
$('#editPersonModal').modal('hide');
|
||||||
|
window.location.reload();
|
||||||
$('#editPersonForm').on('submit', function(event) {
|
});
|
||||||
handleFormSubmit(this, '#editPersonError', function(data) {
|
|
||||||
// close modal and reload page
|
|
||||||
$('#editPersonModal').modal('hide');
|
|
||||||
window.location.reload();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -78,7 +78,7 @@ $status_colors = [
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<?php include '_sidebar.php'; ?>
|
<?php include '_sidebar.php'; ?>
|
||||||
|
|
||||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
<main id="main-content" class="col-md-9 col-lg-10 px-md-4">
|
||||||
<?php if (isset($_SESSION['success_message'])): ?>
|
<?php if (isset($_SESSION['success_message'])): ?>
|
||||||
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert">
|
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert">
|
||||||
<?= $_SESSION['success_message']; ?>
|
<?= $_SESSION['success_message']; ?>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user