Files
Sentri/pub/bin/php/jsScripts.php
2026-01-01 10:54:18 +01:00

1321 lines
56 KiB
PHP

<?php
if (!defined('APP_INIT')) {
exit;
}
if (isset($this->jsScriptLoadData['form'])) { ?>
<!-- Select2 -->
<script src="/src/js/plugin/select2/select2.full.min.js"></script><!-- Bootstrap Tagsinput -->
<script src="/src/js/plugin/bootstrap-tagsinput/bootstrap-tagsinput.min.js"></script><!-- jQuery Moment.JS -->
<script src="/src/js/plugin/moment/moment.min.js"></script><!-- Bootstrap datetimepicker -->
<script src="/src/js/plugin/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js"></script><!-- valicate forms -->
<script src="/src/js/plugin/jquery.validate/jquery.validate.min.js"></script><!-- dropzone -->
<script src="/src/js/plugin/dropzone/dropzone.min.js"></script>
<script>
$("#FormValidation").validate({
highlight: function (element) {
$(element).closest(".form-group").removeClass("has-success").addClass("has-error");
},
success: function (element) {
$(element).closest(".form-group").removeClass("has-error").addClass("has-success");
},
});
</script>
<?php }
if (isset($this->jsScriptLoadData['datatables'])) { ?>
<script src="/src/js/plugin/datatables/datatables.min.js"></script>
<?php }
# Filter datatables
if (isset($this->jsScriptLoadData['multiFilterSelect'])) { ?>
<script>
document.addEventListener("DOMContentLoaded", function () {
$(".multi-filter-select").each(function () {
const $tableEl = $(this);
const skipColumnsAttr = $tableEl.data("skip-columns");
const skipColumns = skipColumnsAttr
? skipColumnsAttr.toString().split(',').map(Number)
: [];
const orderAttr = $tableEl.attr("data-datatables-order");
const orderConfig = orderAttr ? JSON.parse(orderAttr) : [[0, 'asc']];
const pageLengthAttr = $tableEl.attr("data-page-length");
const pageLength = pageLengthAttr ? parseInt(pageLengthAttr, 10) : 10;
let table = $tableEl.DataTable({
pageLength: pageLength,
dom: "<'row'<'col-sm-12 col-md-9'f><'pl-3 col-sm-12 col-md-3'l>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
language: {
search: "",
lengthMenu: "Entries _MENU_"
},
order: orderConfig,
initComplete: function () {
this.api().columns().every(function (index) {
if (skipColumns.includes(index)) {
return;
}
var column = this;
var select = $('<select class="form-select"><option value=""></option></select>')
.appendTo($(column.footer()).empty())
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
// Collect unique filter values
let uniqueValues = new Set();
column.nodes().each(function (cell) {
const cleanText = $(cell).data("filter") || $(cell).text().trim();
if (cleanText !== "") {
uniqueValues.add(cleanText);
}
});
// Sort + append unique values
Array.from(uniqueValues).sort().forEach(function (val) {
select.append('<option value="' + val + '">' + val + '</option>');
});
});
}
});
// Customize search input
$tableEl.closest('.dataTables_wrapper').find('[type=search]').each(function () {
$(this).attr("placeholder", "<?php echo __('search_') ?>");
$(this).before('<span class="fa fa-search"></span>');
});
});
});
</script>
<?php }
// multipleselect loading
if (isset($this->jsScriptLoadData['multiple_select'])) { ?>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Initialize select2 for all elements with data-multiple-select
document.querySelectorAll("[data-multiple-select]").forEach(select => {
$(select).select2({
theme: "bootstrap"
});
});
});
</script>
<?php }
// Function to generate a random password
if (isset($this->jsScriptLoadData['Generatepassword'])) { ?>
<script>
function generateRandomPassword(length) {
var charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+=<>?";
var password = "";
for (var i = 0; i < length; i++) {
var randomIndex = Math.floor(Math.random() * charset.length);
password += charset.charAt(randomIndex);
}
return password;
}
</script>
<?php }
if (isset($this->jsScriptLoadData['passwordGenOnLoad'])) { ?>
<script>
document.querySelectorAll("[data-password-target]").forEach(input => {
const passwordLength = Math.floor(Math.random() * (25 - 13 + 1)) + 13;
const generatedPassword = generateRandomPassword(passwordLength);
input.setAttribute("value", generatedPassword);
});
</script>
<?php }
if (isset($this->jsScriptLoadData['passwordShowHide'])) { ?>
<script>
document.querySelectorAll(".password-toggle").forEach(button => {
button.addEventListener("click", function () {
const inputId = this.getAttribute("data-toggle-target");
const input = document.getElementById(inputId);
const icon = this.querySelector("i");
if (input.type === "password") {
input.type = "text";
icon.classList.replace("fa-eye", "fa-eye-slash");
} else {
input.type = "password";
icon.classList.replace("fa-eye-slash", "fa-eye");
}
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['passwordRegen'])) { ?>
<script>
document.querySelectorAll(".password-generate").forEach(button => {
button.addEventListener("click", function () {
const inputId = this.getAttribute("data-generate-target");
const input = document.getElementById(inputId);
const passwordLength = Math.floor(Math.random() * (25 - 13 + 1)) + 13;
const newPassword = generateRandomPassword(passwordLength);
input.setAttribute("value", newPassword);
});
});
</script>
<?php }
// Create an datepicker input
if (isset($this->jsScriptLoadData['datepicker'])) { ?>
<script src="/src/js/plugin/moment/moment.min.js"></script>
<script src="/src/js/plugin/datepicker/bootstrap-datetimepicker.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Initialize datepicker for all elements with data-datepicker
document.querySelectorAll("[data-datepicker]").forEach(input => {
$(input).datetimepicker({
format: "DD/MM/YYYY"
});
});
});
</script>
<?php }
// Activate the right panel and tab on refresh based on the #hash in the url
if (isset($this->jsScriptLoadData['activeTabOnRefresh'])) { ?>
<script>
document.addEventListener("DOMContentLoaded", function () {
const cardBody = document.querySelector('.card-body.activeTabOnRefresh');
const hash = window.location.hash;
let selectedTab = hash ? cardBody.querySelector(`[href="${hash}"]`) : null;
let selectedContent = hash ? cardBody.querySelector(hash) : null;
if (!selectedTab || !selectedContent) {
selectedTab = cardBody.querySelector(".nav-link");
selectedContent = cardBody.querySelector(".tab-pane");
}
if (selectedTab && selectedContent) {
cardBody.querySelectorAll('.nav-link').forEach(tab => tab.classList.remove('active'));
cardBody.querySelectorAll('.tab-pane').forEach(content => content.classList.remove('show', 'active'));
selectedTab.classList.add('active');
selectedContent.classList.add('show', 'active');
}
cardBody.querySelectorAll('.nav-link').forEach(tab => {
tab.addEventListener("click", function () {
let targetTab = this.getAttribute("href");
history.replaceState(null, null, targetTab);
});
});
cardBody.style.opacity = "1";
});
</script>
<?php }
if (isset($this->jsScriptLoadData['activateCompany'])) { ?>
<script>
$(document).on('click', '.btn[data-item-company-state]', function (e) {
e.preventDefault();
const btn = $(this);
const companyUuid = btn.data('item-uuid');
const currentState = btn.data('item-state'); // "active" or "imported"
const newState = (currentState === "active") ? "imported" : "active";
const url = "/api/v1/customers/companies/activate/";
const params = {
company_uuid: companyUuid,
company_state: newState,
_method: 'PUT'
};
const rootStyles = getComputedStyle(document.documentElement);
const background = rootStyles.getPropertyValue('--swal-bg')?.trim();
const color = rootStyles.getPropertyValue('--swal-text-color')?.trim();
$.post(url, params)
.done(function (response, status, jqXHR) {
if (jqXHR.status === 200) {
btn.data('item-state', newState);
btn.removeClass('btn-success btn-danger');
btn.addClass(newState === "active" ? "btn-danger" : "btn-success");
btn.find('i')
.removeClass('fa-plus fa-xmark')
.addClass(newState === "active" ? "fa-xmark" : "fa-plus");
const row = btn.closest("tr");
const stateCell = row.find("td").eq(3);
stateCell.text(newState);
}
})
.fail(function (jqXHR) {
let response = jqXHR.responseJSON || {};
Swal.fire({
background: background,
color: color,
icon: "error",
title: "<?php echo __('connection_error_title') ?>",
text: response.message || "<?php echo __('connection_error_text') ?>: " + jqXHR.status,
confirmButtonText: "<?php echo __('close') ?>",
customClass: {confirmButton: "btn btn-danger"},
buttonsStyling: false
});
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['multiFilterSelectServers'])) { ?>
<script>
document.addEventListener("DOMContentLoaded", function () {
function setCookie(name, value, days = 365) {
const expires = new Date(Date.now() + days * 864e5).toUTCString();
document.cookie = name + "=" + encodeURIComponent(value) + "; expires=" + expires + "; path=/";
}
$(".multi-filter-select").each(function () {
const $tableEl = $(this);
//
// AUTO-BUILD columnDefs from <th data-column="">
//
const columnDefs = [];
$tableEl.find("thead th").each(function (index) {
const colName = $(this).data("column") || "";
columnDefs.push({
targets: index,
name: colName
});
});
//
// INITIALIZE DATATABLE
//
let table = $tableEl.DataTable({
autoWidth: false,
pageLength: $tableEl.attr("data-page-length") ? parseInt($tableEl.attr("data-page-length"), 10) : 10,
dom: "<'row'<'col-sm-12 col-md-9'f><'pl-3 col-sm-12 col-md-3'l>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
language: {
search: "",
lengthMenu: "Entries _MENU_"
},
order: (function () {
let idx = 0;
$tableEl.find("thead th").each(function (i) {
if ($(this).data("column") === "server_hostname") {
idx = i;
}
});
return [[idx, 'asc']];
})(),
columnDefs: columnDefs,
initComplete: function () {
const dtApi = this.api();
dtApi.columns().every(function () {
const colIndex = this.index();
const th = $tableEl.find("thead th").eq(colIndex);
const colName = th.data("column");
if (!colName) return;
const footerCell = $(this.footer());
if (!footerCell.length) return;
const select = $('<select class="form-select"><option value=""></option></select>')
.appendTo(footerCell.empty())
.on('change', function () {
const val = $.fn.dataTable.util.escapeRegex($(this).val());
dtApi.column(colName + ":name")
.search(val ? '^' + val + '$' : '', true, false)
.draw();
});
const uniqueValues = new Set();
this.nodes().each(function (cell) {
const cleanText = $(cell).data("filter") || $(cell).text().trim();
if (cleanText !== "") uniqueValues.add(cleanText);
});
Array.from(uniqueValues).sort().forEach(v => {
select.append('<option value="' + v + '">' + v + '</option>');
});
});
//
// APPLY CHECKBOX VISIBILITY DEFAULTS
//
// Set initial visibility based on checkboxes
$(".selectgroup-input").each(function () {
const baseName = $(this).val();
const visible = $(this).is(":checked");
dtApi.columns().every(function () {
const name = this.settings()[0].aoColumns[this.index()].name || '';
if (name === baseName || name.startsWith(baseName + '_')) {
this.visible(visible);
}
});
});
$(".selectgroup-input").on("change", function () {
const selected = [];
$(".selectgroup-input:checked").each(function () {
selected.push($(this).val());
});
// Save selected checkboxes in a cookie (or localStorage)
// Using localStorage here
setCookie("serverTableColumns", JSON.stringify(selected), 365);
const baseName = $(this).val();
const visible = $(this).is(":checked");
// toggle all columns whose data-column === baseName or starts with baseName + '_'
table.columns(`[data-column^='${baseName}']`).visible(visible);
});
}
});
//
// SEARCH FIELD ICON
//
$tableEl.closest('.dataTables_wrapper').find('[type=search]').each(function () {
$(this).attr("placeholder", "<?php echo __('search_') ?>");
$(this).before('<span class="fa fa-search"></span>');
});
//
// HANDLE CHECKBOX TOGGLE
//
$(".selectgroup-input").on("change", function () {
const colName = $(this).val();
table.column(colName + ":name").visible($(this).is(":checked"));
});
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['inserve_source'])) { ?>
<script>
$(document).on('click', '.test-inserve-connection-btn', function (e) {
e.preventDefault();
const url = "/api/v1/sources/inserve/";
const params = {action: "auth/me"};
const rootStyles = getComputedStyle(document.documentElement);
const background = rootStyles.getPropertyValue('--swal-bg')?.trim();
const color = rootStyles.getPropertyValue('--swal-text-color')?.trim();
$.get(url, params)
.done(function (response, status, jqXHR) {
// Check if HTTP code is 200
if (jqXHR.status === 200) {
Swal.fire({
background: background,
color: color,
icon: "success",
title: "<?php echo __('connection_success_title') ?>",
text: "<?php echo __('connection_success_text') ?>",
confirmButtonText: "<?php echo __('close') ?>",
customClass: {confirmButton: "btn btn-success"},
buttonsStyling: true
});
}
})
.fail(function (jqXHR) {
let response = jqXHR.responseJSON || {};
Swal.fire({
background: background,
color: color,
icon: "error",
title: "<?php echo __('connection_error_title') ?>",
text: response.message || "<?php echo __('connection_error_text') ?>: " + jqXHR.status,
confirmButtonText: "<?php echo __('close') ?>",
customClass: {confirmButton: "btn btn-danger"},
buttonsStyling: false
});
});
});
</script>
<?php }
// slugify an input field
if (isset($this->jsScriptLoadData['slugify'])) { ?>
<script>
function slugify(str) {
return str
.trim()
.toLowerCase()
.replace(/[^a-z0-9 -]/g, '') // Remove non-alphanumeric characters
.replace(/\s+/g, '-') // Replace spaces with hyphens
.replace(/-+/g, '-'); // Remove consecutive hyphens
}
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll("[data-slugify]").forEach(input => {
input.addEventListener("input", function () {
const targetId = this.getAttribute("data-slugify");
const targetInput = document.getElementById(targetId);
if (targetInput) {
targetInput.value = slugify(this.value);
}
});
});
});
</script>
<?php }
// Clicking on a btn will copy the value of an input to the clipboard
if (isset($this->jsScriptLoadData['copyInputValue'])) { ?>
<script>
function copyToClipboard(inputId) {
let input = document.getElementById(inputId);
if (input) {
navigator.clipboard.writeText(input.value).then(() => {
Swal.fire({
background: background,
color: color,
title: '<?php echo __('copied') ?>',
text: '<?php echo __('copy_text_success') ?>',
icon: 'success',
confirmButtonText: '<?php echo __('ok') ?>'
});
}).catch(err => {
Swal.fire({
background: background,
color: color,
title: 'Error!',
text: 'Failed to copy the content.',
icon: 'error',
confirmButtonText: 'OK'
});
});
}
}
</script>
<?php }
if (isset($this->jsScriptLoadData['CopyTargetData'])) { ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('[data-copy-target]').forEach(button => {
button.addEventListener('click', function () {
const targetId = this.getAttribute('data-copy-target');
const target = document.getElementById(targetId);
if (target) {
const copyData = target.getAttribute('data-copy-data');
if (copyData) {
navigator.clipboard.writeText(copyData).then(() => {
Swal.fire({
background: background,
color: color,
title: '<?php echo __('copied') ?>',
text: '<?php echo __('copy_text_success') ?>',
icon: 'success',
confirmButtonText: '<?php echo __('ok') ?>'
});
}).catch(err => {
Swal.fire({
background: background,
color: color,
title: 'Error!',
text: 'Failed to copy the content.',
icon: 'error',
confirmButtonText: 'OK'
});
});
}
}
});
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['validateJson'])) { ?>
<script>
function isValidJSON(value) {
try {
JSON.parse(value);
return true;
} catch (e) {
return false;
}
}
document.addEventListener("DOMContentLoaded", function () {
// Attach JSON validation to all textareas with data-validate-json
document.querySelectorAll("[data-validate-json]").forEach(textarea => {
textarea.addEventListener("input", function () {
const value = this.value.trim();
const feedback = this.nextElementSibling; // Assuming <small> is right after <textarea>
if (value === "" || isValidJSON(value)) {
feedback.textContent = "<?php echo __('json_valid') ?>";
feedback.style.color = "green";
this.classList.remove("is-invalid");
this.classList.add("is-valid");
} else {
feedback.textContent = "<?php echo __('json_invalid') ?>";
feedback.style.color = "red";
this.classList.remove("is-valid");
this.classList.add("is-invalid");
}
});
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['stompjes'])) { ?>
<script src="/src/js/plugin/chart.js/chart.min.js"></script>
<script>
// Attach event listener
$(document).on('click', '.stomp-btn', function (e) {
e.preventDefault(); // Prevent default link behavior
const itemUuid = $(this).data('item-uuid');
const itemName = $(this).data('item-name')
const apiUrl = $(this).data('api-url');
const deleteAction = $(this).data('delete-action') || false;
const row = $(this).closest('tr');
const StompOptions = {
itemUuid: itemUuid,
itemName: itemName,
apiUrl: apiUrl,
row: row,
deleteAction: deleteAction,
title: "<?php echo __('action_confirm')?>",
text: "<?php echo __('action_confirm_text')?>",
icon: "question",
responseSuccessTitle: "<?php echo __('stomped')?>",
responseSuccessText: "<?php echo __('stomped_success')?>",
responseSuccessBtnText: "<?php echo __('close') ?>",
responseErrorTitle: "<?php echo __('action_error_title')?>",
responseErrorText: "<?php echo __('action_error_text')?>",
cancelConfirmTitle: "<?php echo __('action_cancel_confirm_title')?>",
cancelConfirmText: "<?php echo __('action_cancel_confirm_text')?>",
cancelConfirmBtnText: "<?php echo __('close') ?>",
};
// Make the POST request to delete the device
$.post(apiUrl, {
[itemName]: itemUuid,
_method: "POST"
})
.done(function (response) {
// Parse the response if necessary
Swal.fire({
background: background,
color: color,
title: StompOptions.responseSuccessTitle || "Added!",
text: response.message || StompOptions.responseSuccessText || "Your stomp has been added.",
confirmButtonText: StompOptions.responseSuccessBtnText,
icon: "success",
customClass: {
confirmButton: "btn btn-success",
},
buttonsStyling: true,
}).then((result) => {
const counterEl = $("#count-" + itemUuid);
if (counterEl.length) {
const currentCount = parseInt(counterEl.text(), 10);
if (!isNaN(currentCount)) {
counterEl.text(currentCount + 1);
}
}
});
})
.fail(function (jqXHR) {
let response = jqXHR.responseJSON || {};
Swal.fire({
background: background,
color: color,
title: StompOptions.responseErrorTitle,
text: response.message || StompOptions.responseErrorText,
icon: "error",
customClass: {confirmButton: "btn btn-danger"},
buttonsStyling: false,
});
});
});
</script>
<script>
// Attach event listener
$(document).on('click', '.stomp-delete-btn', function (e) {
e.preventDefault(); // Prevent default link behavior
const row = $(this).closest('tr');
const deleteStompOptions = {
itemUuid: $(this).data('item-uuid'),
itemName: $(this).data('item-name'),
apiUrl: $(this).data('api-url'),
row: row,
deleteAction: $(this).data('delete-action') || false,
title: "<?php echo __('action_confirm')?>",
text: "<?php echo __('action_confirm_text')?>",
confirmButtonText: "<?php echo __('action_confirm_button')?>",
cancelButtonText: "<?php echo __('action_cancel_button')?>",
responseSuccessTitle: "<?php echo __('deleted')?>",
responseSuccessText: "<?php echo __('action_success_text')?>",
responseSuccessBtnText: "<?php echo __('close') ?>",
responseErrorTitle: "<?php echo __('action_error_title')?>",
responseErrorText: "<?php echo __('action_error_text')?>",
cancelConfirmBtnText: "<?php echo __('close') ?>",
};
// Make the POST request to delete the device
$.post(deleteStompOptions.apiUrl, {
[deleteStompOptions.itemName]: deleteStompOptions.itemUuid,
_method: "DELETE"
})
.done(function (response) {
// Parse the response if necessary
Swal.fire({
background: background,
color: color,
title: deleteStompOptions.responseSuccessTitle || "Deleted!",
text: response.message || deleteStompOptions.responseSuccessText || "Your item has been deleted.",
confirmButtonText: deleteStompOptions.responseSuccessBtnText,
icon: "error",
customClass: {
confirmButton: "btn btn-danger",
},
buttonsStyling: true,
}).then((result) => {
// If deleteAction is false, remove the row; otherwise, process show and hide actions
const userUUID = deleteStompOptions.row.data('user-uuid');
const counterEl = $("#count-" + userUUID);
if (counterEl.length) {
const currentCount = parseInt(counterEl.text(), 10);
if (!isNaN(currentCount) && currentCount > 0) {
counterEl.text(currentCount - 1);
}
}
if (!deleteStompOptions.deleteAction || deleteStompOptions.deleteAction === "false") {
row.fadeOut(500, function () {
$(this).remove();
});
}
});
})
.fail(function (jqXHR) {
let response = jqXHR.responseJSON || {};
Swal.fire({
background: background,
color: color,
title: deleteStompOptions.responseErrorTitle,
text: response.message || deleteStompOptions.responseErrorText,
icon: "error",
customClass: {confirmButton: "btn btn-danger"},
buttonsStyling: false,
});
});
});
</script>
<script>
const gruvboxColors = [
"#fabd2f", // yellow
"#b8bb26", // green
"#fb4934", // red
"#8ec07c", // aqua
"#83a598", // blue
"#fe8019", // orange
];
const assignedColors = {};
const usedColors = new Set();
function hexToRGBA(hex, alpha) {
hex = hex.replace('#', '');
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
return `rgba(${r},${g},${b},${alpha})`;
}
function getColorForUser(name) {
if (assignedColors[name]) return assignedColors[name];
let hash = 0;
for (let i = 0; i < name.length; i++) {
hash = name.charCodeAt(i) + ((hash << 5) - hash);
}
let preferredIndex = Math.abs(hash) % gruvboxColors.length;
let preferredColor = gruvboxColors[preferredIndex];
if (!usedColors.has(preferredColor)) {
assignedColors[name] = preferredColor;
usedColors.add(preferredColor);
return preferredColor;
}
const unused = gruvboxColors.filter(c => !usedColors.has(c));
let finalColor = unused.length > 0
? unused[Math.floor(Math.random() * unused.length)]
: gruvboxColors[Math.floor(Math.random() * gruvboxColors.length)];
assignedColors[name] = finalColor;
usedColors.add(finalColor);
return finalColor;
}
// Helper: get all dates between two dates (inclusive)
function getDatesBetween(startDate, endDate) {
const dates = [];
let current = new Date(startDate);
const end = new Date(endDate);
while (current <= end) {
dates.push(current.toISOString().slice(0, 10));
current.setDate(current.getDate() + 1);
}
return dates;
}
// Read selected date inputs (format: d/m/Y)
const fromInput = document.getElementById("fd").value.split("/");
const toInput = document.getElementById("td").value.split("/");
const fromDate = new Date(`${fromInput[2]}-${fromInput[1]}-${fromInput[0]}`);
const toDate = new Date(`${toInput[2]}-${toInput[1]}-${toInput[0]}`);
// Generate all dates in range
const sortedDates = getDatesBetween(fromDate, toDate);
const users = {};
// Populate user data
stompData.forEach(item => {
const user = item.user_first_name;
const date = new Date(item.stomp_timestamp * 1000).toISOString().slice(0, 10);
if (!users[user]) users[user] = {};
if (!users[user][date]) users[user][date] = 0;
users[user][date] += 1;
});
// Fill missing dates with 0
Object.keys(users).forEach(user => {
sortedDates.forEach(date => {
if (!users[user][date]) users[user][date] = 0;
});
});
// Build datasets
const datasets = Object.keys(users).map(user => {
const color = getColorForUser(user);
return {
label: user,
data: sortedDates.map(date => users[user][date]),
borderColor: color,
backgroundColor: hexToRGBA(color, 0.1),
tension: 0.3
};
});
// Chart config
const data = {
labels: sortedDates,
datasets: datasets
};
const config = {
type: 'line',
data: data,
options: {
responsive: true,
plugins: {
legend: {position: 'top'},
title: {
display: true,
text: 'Stomps per User per Day'
}
},
scales: {y: {beginAtZero: true}}
}
};
// Render chart
new Chart(document.getElementById('stompjesChart'), config);
</script>
<script>
$(document).ready(function () {
$("#datePicker").on("click", function () {
const fd = $("#fd").val(); // start date
const td = $("#td").val(); // end date
const params = new URLSearchParams(window.location.search);
if (fd) params.set("fd", fd);
else params.delete("fd");
if (td) params.set("td", td);
else params.delete("td");
// Reload page with updated GET parameters
window.location.href = window.location.pathname + "?" + params.toString();
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['delete_confirmation'])) { ?>
<script>
const rootStyles = getComputedStyle(document.documentElement);
const background = rootStyles.getPropertyValue('--swal-bg')?.trim();
const color = rootStyles.getPropertyValue('--swal-text-color')?.trim();
function showDeleteConfirmation({
itemUuid,
itemName,
apiUrl,
row,
deleteAction,
title = "Are you sure?",
text = "You won't be able to revert this!",
icon = "question",
confirmButtonText = "Yes, delete",
cancelButtonText = "Cancel!",
background = getComputedStyle(document.documentElement).getPropertyValue('--swal-bg')?.trim(),
color = getComputedStyle(document.documentElement).getPropertyValue('--swal-text-color')?.trim(),
customClass = {
confirmButton: "btn btn-danger mx-2",
cancelButton: "btn btn-secondary"
},
responseSuccessTitle = '',
responseSuccessText = '',
responseSuccessBtnText = '',
responseErrorTitle = '',
responseErrorText = '',
cancelConfirmTitle = '',
cancelConfirmText = '',
cancelConfirmBtnText = ''
}) {
Swal.fire({
title: title,
text: text,
icon: icon,
background: background,
color: color,
showCancelButton: true,
confirmButtonText: confirmButtonText,
cancelButtonText: cancelButtonText,
customClass: customClass,
buttonsStyling: false,
}).then((result) => {
if (result.isConfirmed) {
// Make the POST request to delete the item
$.post(apiUrl, {
[itemName]: itemUuid,
_method: "DELETE"
})
.done(function (response) {
if (!(typeof deleteAction === "string" && deleteAction.startsWith("url:"))) { // do not fire the swal if there will be a redirect
Swal.fire({
background: background,
color: color,
title: responseSuccessTitle || "Deleted!",
text: response.message || responseSuccessText || "Your item has been deleted.",
confirmButtonText: responseSuccessBtnText,
icon: "success",
customClass: {
confirmButton: "btn btn-success",
},
buttonsStyling: true,
});
}
if (typeof deleteAction === "string" && deleteAction.startsWith("url:")) {
const targetUrl = deleteAction.replace("url:", "").trim();
window.location.href = targetUrl;
} else if (!deleteAction || deleteAction === "false") {
row.fadeOut(500, function () {
$(this).remove();
});
} else {
try {
let actions = typeof deleteAction === "string" ? JSON.parse(deleteAction) : deleteAction;
for (const [elementId, action] of Object.entries(actions)) {
let $element = $("#" + elementId);
if (action === "show") {
$element.fadeIn(500); // Fade in element smoothly
} else if (action === "hide") {
$element.fadeOut(0); // Fade out element smoothly
}
}
} catch (error) {
console.error("Error parsing deleteAction JSON:", error);
}
}
})
.fail(function (jqXHR) {
// Parse the error response
let response = jqXHR.responseJSON || {};
Swal.fire({
background: background,
color: color,
title: responseErrorTitle || "Error",
text: response.message || responseErrorText || "An error occurred. Please contact support.",
icon: "error",
customClass: {
confirmButton: "btn btn-danger",
},
buttonsStyling: false,
});
});
} else if (result.dismiss === Swal.DismissReason.cancel) {
Swal.fire({
background: background,
color: color,
title: cancelConfirmTitle,
text: cancelConfirmText,
confirmButtonText: cancelConfirmBtnText,
icon: "info",
customClass: {
confirmButton: "btn btn-black",
},
buttonsStyling: false,
});
}
});
}
// Attach event listener
$(document).on('click', '.delete-btn', function (e) {
e.preventDefault(); // Prevent default link behavior
const itemUuid = $(this).data('item-uuid');
const itemName = $(this).data('item-name')
const apiUrl = $(this).data('api-url');
const deleteAction = $(this).data('delete-action') || false;
const row = $(this).closest('tr');
const deleteOptions = {
itemUuid: itemUuid,
itemName: itemName,
apiUrl: apiUrl,
row: row,
deleteAction: deleteAction,
title: "<?php echo __('action_confirm')?>",
text: "<?php echo __('action_confirm_text')?>",
icon: "question",
confirmButtonText: "<?php echo __('action_confirm_button')?>",
cancelButtonText: "<?php echo __('action_cancel_button')?>",
responseSuccessTitle: "<?php echo __('action_success_title')?>",
responseSuccessText: "<?php echo __('action_success_text')?>",
responseSuccessBtnText: "<?php echo __('close') ?>",
responseErrorTitle: "<?php echo __('action_error_title')?>",
responseErrorText: "<?php echo __('action_error_text')?>",
cancelConfirmTitle: "<?php echo __('action_cancel_confirm_title')?>",
cancelConfirmText: "<?php echo __('action_cancel_confirm_text')?>",
cancelConfirmBtnText: "<?php echo __('close') ?>",
};
showDeleteConfirmation(deleteOptions);
});
</script>
<?php }
if (isset($this->jsScriptLoadData['load_dropzone'])) { ?>
<script>
// Disable Dropzone's auto-discover feature
Dropzone.autoDiscover = false;
document.addEventListener('DOMContentLoaded', function () {
// Select all forms with the Dropzone class
const dropzones = document.querySelectorAll('.dropzone');
dropzones.forEach((form) => {
// Check if Dropzone is already attached
if (form.dropzone) return;
// Get the form type from the data attribute
const formType = form.getAttribute('data-form-type');
const associatedTable = document.querySelector(`table[data-table-type="${formType}"] tbody`);
// Initialize Dropzone for the form
new Dropzone(form, {
url: form.getAttribute('action'),
paramName: 'file', // Name of the file input
autoProcessQueue: true, // Automatically upload files when dropped
maxFilesize: 600, // Maximum file size in MB
acceptedFiles: '.pdf,.rom', // Adjust accepted files as needed
init: function () {
this.on('success', function (file, response) {
const parsedResponse = typeof response === 'string' ? JSON.parse(response) : response;
if (parsedResponse.status === 'success') {
// Check if the table and tbody exist
if (associatedTable) {
// Add the uploaded file to the corresponding table
const newRow = document.createElement('tr');
const newCell = document.createElement('td');
newCell.textContent = parsedResponse.file_name;
newRow.appendChild(newCell);
associatedTable.appendChild(newRow);
} else {
console.error('Associated table not found for form type:', formType);
}
} else {
console.error('Error uploading file:', parsedResponse.message || 'Unknown error');
}
});
this.on('error', function (file, errorMessage) {
console.error('Error uploading file:', errorMessage);
});
}
});
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['updateToggle'])) { ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.checkbox').forEach(function (checkbox) {
checkbox.addEventListener('change', async function (e) {
const input = e.target;
const apiUrl = input.getAttribute('data-api-url');
const changeKey = input.getAttribute('data-api-changevalue');
const dataRaw = input.getAttribute('data-api-data');
let dataObj;
try {
dataObj = JSON.parse(dataRaw);
} catch (err) {
console.error('Invalid JSON in data-api-data:', err);
return;
}
// Toggle value FIRST
if (changeKey in dataObj) {
const currentValue = dataObj[changeKey];
const toggled = currentValue === '1' || currentValue === 1 ? 0 : 1;
dataObj[changeKey] = toggled;
}
// Add method override
dataObj._method = 'PUT';
// Convert to URL-encoded string
const formBody = new URLSearchParams(dataObj).toString();
try {
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: formBody
});
if (response.ok) {
// Remove _method before saving back
delete dataObj._method;
input.setAttribute('data-api-data', JSON.stringify(dataObj));
} else {
input.checked = !input.checked; // revert toggle
console.error('Request failed:', await response.text());
}
} catch (err) {
input.checked = !input.checked;
console.error('Request error:', err);
}
});
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['updatePermissions'])) { ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.checkbox').forEach(checkbox => {
checkbox.addEventListener('change', async (e) => {
const input = e.target;
const permissionUUID = input.dataset.permissionUuid;
const userGroupUUID = input.dataset.userGroupUuid;
const permissionValue = input.dataset.value;
const apiUrl = input.dataset.apiUrl;
// Revert the checkbox immediately; only mark it again on success
input.checked = !input.checked;
input.disabled = true;
// Build URL-encoded form data
const formData = new URLSearchParams();
formData.append('permission_uuid', permissionUUID);
formData.append('user_group_uuid', userGroupUUID);
formData.append('permission_value', permissionValue);
formData.append('_method', 'PUT')
try {
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData.toString()
});
if (response.ok) {
// Uncheck all switches in the same group
const groupSelector = `input[data-permission-uuid="${permissionUUID}"][data-user-group-uuid="${userGroupUUID}"]`;
document.querySelectorAll(groupSelector).forEach(cb => cb.checked = false);
// Then check the selected one
input.checked = true;
} else {
console.error('API error:', response.status);
// Leave checkbox state unchanged (reverted above)
}
} catch (err) {
console.error('Network error:', err);
// Leave checkbox state unchanged (reverted above)
} finally {
input.disabled = false;
}
});
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['enableButtonOnImageUpload'])) { ?>
<script>
document.addEventListener("DOMContentLoaded", function () {
const fileInputs = document.querySelectorAll('input[type="file"][data-enable-button]');
fileInputs.forEach(function (input) {
input.addEventListener("change", function () {
const buttonId = input.getAttribute("data-enable-button");
const button = document.getElementById(buttonId);
if (input.files.length > 0) {
button.removeAttribute("disabled");
button.classList.remove("opacity-0");
button.classList.add("opacity-100");
} else {
button.setAttribute("disabled", "disabled");
button.classList.remove("opacity-100");
button.classList.add("opacity-0");
}
});
});
});
</script>
<?php }
if (isset($this->jsScriptLoadData['breadCrumbs'])) { ?>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Access the PHP array stored in $GLOBALS['breadCrumbArray']
const breadcrumbArray = <?php echo json_encode($GLOBALS['breadCrumbArray']); ?>;
// Get the breadcrumb container
const breadcrumbContainer = document.getElementById("breadCrumb");
// Create the ul element
const ul = document.createElement("ul");
ul.classList.add("breadcrumbs", "mb-3");
// Loop through the array to generate breadcrumb items
breadcrumbArray.forEach((item, index) => {
// Create the li for each item
const li = document.createElement("li");
li.classList.add("nav-item");
// Create the anchor tag
const a = document.createElement("a");
// Check if it's the last item to make it non-clickable (active)
if (index === breadcrumbArray.length - 1) {
// This is the active page, so we remove the href and add a class
a.removeAttribute("href");
a.classList.add("active");
} else {
// For other items, set the href as usual
a.classList.add("text-info");
a.href = item.href;
}
// Insert the HTML content for the display (includes icons)
a.innerHTML = item.display; // Use innerHTML to allow icon rendering
// Append the anchor tag to the li
li.appendChild(a);
// Append the li to the ul
ul.appendChild(li);
// Add separator if not the last item
if (index < breadcrumbArray.length - 1) {
const separator = document.createElement("li");
separator.classList.add("separator");
// Create Font Awesome icon
const arrowIcon = document.createElement("i");
arrowIcon.classList.add("fa-solid", "fa-chevron-right");
separator.appendChild(arrowIcon);
ul.appendChild(separator);
}
});
// Append the generated breadcrumb to the breadcrumb container
breadcrumbContainer.appendChild(ul);
});
</script>
<?php } ?>