|
|
|
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
initializeTooltips();
|
|
|
|
|
|
initializeFormValidation();
|
|
|
|
|
|
initializeSearchAndFilters();
|
|
|
|
|
|
initializeExportFunctions();
|
|
|
|
|
|
initializeLiveDataUpdates();
|
|
|
|
|
|
initializeVisualEffects();
|
|
});
|
|
|
|
|
|
|
|
|
|
function initializeTooltips() {
|
|
|
|
if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
|
|
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
|
[...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
|
} else {
|
|
|
|
const tooltips = document.querySelectorAll('[data-tooltip]');
|
|
tooltips.forEach(element => {
|
|
element.addEventListener('mouseenter', function() {
|
|
const tooltipText = this.getAttribute('data-tooltip');
|
|
const tooltip = document.createElement('div');
|
|
tooltip.className = 'custom-tooltip';
|
|
tooltip.textContent = tooltipText;
|
|
document.body.appendChild(tooltip);
|
|
|
|
const rect = this.getBoundingClientRect();
|
|
tooltip.style.left = rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2) + 'px';
|
|
tooltip.style.top = rect.bottom + 10 + 'px';
|
|
|
|
this.addEventListener('mouseleave', function() {
|
|
document.body.removeChild(tooltip);
|
|
}, { once: true });
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
function initializeFormValidation() {
|
|
const forms = document.querySelectorAll('.needs-validation');
|
|
|
|
forms.forEach(form => {
|
|
form.addEventListener('submit', function(event) {
|
|
if (!form.checkValidity()) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
|
|
form.classList.add('was-validated');
|
|
});
|
|
|
|
|
|
const inputs = form.querySelectorAll('input, select, textarea');
|
|
inputs.forEach(input => {
|
|
input.addEventListener('input', function() {
|
|
validateInput(this);
|
|
});
|
|
|
|
input.addEventListener('blur', function() {
|
|
validateInput(this);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
function validateInput(input) {
|
|
|
|
if (input.checkValidity()) {
|
|
input.classList.remove('is-invalid');
|
|
input.classList.add('is-valid');
|
|
} else {
|
|
input.classList.remove('is-valid');
|
|
input.classList.add('is-invalid');
|
|
}
|
|
|
|
|
|
if (input.type === 'number') {
|
|
const min = parseFloat(input.getAttribute('min'));
|
|
const max = parseFloat(input.getAttribute('max'));
|
|
const value = parseFloat(input.value);
|
|
|
|
if (!isNaN(value)) {
|
|
if (!isNaN(min) && value < min) {
|
|
input.setCustomValidity(`القيمة يجب أن تكون أكبر من أو تساوي ${min}`);
|
|
} else if (!isNaN(max) && value > max) {
|
|
input.setCustomValidity(`القيمة يجب أن تكون أقل من أو تساوي ${max}`);
|
|
} else {
|
|
input.setCustomValidity('');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
function initializeSearchAndFilters() {
|
|
|
|
const searchInputs = document.querySelectorAll('.table-search');
|
|
searchInputs.forEach(input => {
|
|
input.addEventListener('input', function() {
|
|
const table = document.querySelector(this.getAttribute('data-table'));
|
|
const term = this.value.toLowerCase();
|
|
|
|
if (table) {
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
rows.forEach(row => {
|
|
const text = row.textContent.toLowerCase();
|
|
row.style.display = text.includes(term) ? '' : 'none';
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
const filterSelects = document.querySelectorAll('.table-filter');
|
|
filterSelects.forEach(select => {
|
|
select.addEventListener('change', function() {
|
|
const table = document.querySelector(this.getAttribute('data-table'));
|
|
const column = parseInt(this.getAttribute('data-column'));
|
|
const value = this.value;
|
|
|
|
if (table) {
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
rows.forEach(row => {
|
|
const cell = row.querySelectorAll('td')[column];
|
|
if (cell) {
|
|
row.style.display = (value === 'all' || cell.textContent === value) ? '' : 'none';
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
function initializeExportFunctions() {
|
|
|
|
const csvButtons = document.querySelectorAll('.export-csv');
|
|
csvButtons.forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const tableId = this.getAttribute('data-table');
|
|
exportTableToCSV(tableId, this.getAttribute('data-filename') || 'export.csv');
|
|
});
|
|
});
|
|
|
|
|
|
const pdfButtons = document.querySelectorAll('.export-pdf');
|
|
pdfButtons.forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const tableId = this.getAttribute('data-table');
|
|
exportTableToPDF(tableId, this.getAttribute('data-filename') || 'export.pdf');
|
|
});
|
|
});
|
|
|
|
|
|
const excelButtons = document.querySelectorAll('.export-excel');
|
|
excelButtons.forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const tableId = this.getAttribute('data-table');
|
|
exportTableToExcel(tableId, this.getAttribute('data-filename') || 'export.xlsx');
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
function exportTableToCSV(tableId, filename) {
|
|
const table = document.getElementById(tableId);
|
|
if (!table) return;
|
|
|
|
let csv = [];
|
|
const rows = table.querySelectorAll('tr');
|
|
|
|
for (let i = 0; i < rows.length; i++) {
|
|
const row = [], cols = rows[i].querySelectorAll('td, th');
|
|
|
|
for (let j = 0; j < cols.length; j++) {
|
|
|
|
let text = cols[j].innerText;
|
|
text = text.replace(/"/g, '""');
|
|
row.push('"' + text + '"');
|
|
}
|
|
|
|
csv.push(row.join(','));
|
|
}
|
|
|
|
|
|
const csvText = csv.join('\n');
|
|
|
|
|
|
const blob = new Blob([csvText], { type: 'text/csv;charset=utf-8;' });
|
|
const link = document.createElement('a');
|
|
|
|
|
|
link.setAttribute('download', filename);
|
|
|
|
|
|
link.href = URL.createObjectURL(blob);
|
|
link.style.visibility = 'hidden';
|
|
|
|
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
}
|
|
|
|
|
|
|
|
|
|
function exportTableToPDF(tableId, filename) {
|
|
|
|
if (typeof jsPDF === 'undefined') {
|
|
console.error('مكتبة jsPDF غير متوفرة. يرجى تضمينها لاستخدام هذه الوظيفة.');
|
|
return;
|
|
}
|
|
|
|
const table = document.getElementById(tableId);
|
|
if (!table) return;
|
|
|
|
|
|
const doc = new jsPDF('l', 'pt', 'a4');
|
|
|
|
|
|
doc.autoTable({
|
|
html: '#' + tableId,
|
|
startY: 20,
|
|
theme: 'grid',
|
|
headStyles: { fillColor: [41, 128, 185], textColor: 255 },
|
|
bodyStyles: { textColor: 50 },
|
|
alternateRowStyles: { fillColor: [245, 245, 245] }
|
|
});
|
|
|
|
|
|
doc.save(filename);
|
|
}
|
|
|
|
|
|
|
|
|
|
function exportTableToExcel(tableId, filename) {
|
|
|
|
if (typeof XLSX === 'undefined') {
|
|
console.error('مكتبة SheetJS (XLSX) غير متوفرة. يرجى تضمينها لاستخدام هذه الوظيفة.');
|
|
return;
|
|
}
|
|
|
|
const table = document.getElementById(tableId);
|
|
if (!table) return;
|
|
|
|
|
|
const wb = XLSX.utils.table_to_book(table);
|
|
|
|
|
|
XLSX.writeFile(wb, filename);
|
|
}
|
|
|
|
|
|
|
|
|
|
function initializeLiveDataUpdates() {
|
|
|
|
const liveDataElements = document.querySelectorAll('[data-live-update]');
|
|
|
|
if (liveDataElements.length > 0) {
|
|
|
|
setInterval(function() {
|
|
liveDataElements.forEach(element => {
|
|
const url = element.getAttribute('data-live-update');
|
|
|
|
|
|
fetch(url)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
|
|
updateElementContent(element, data);
|
|
})
|
|
.catch(error => {
|
|
console.error('خطأ في تحديث البيانات:', error);
|
|
});
|
|
});
|
|
}, 30000);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateElementContent(element, data) {
|
|
const updateType = element.getAttribute('data-update-type') || 'text';
|
|
|
|
switch (updateType) {
|
|
case 'text':
|
|
element.textContent = data.value;
|
|
break;
|
|
case 'html':
|
|
element.innerHTML = data.value;
|
|
break;
|
|
case 'attribute':
|
|
const attributeName = element.getAttribute('data-update-attribute');
|
|
if (attributeName) {
|
|
element.setAttribute(attributeName, data.value);
|
|
}
|
|
break;
|
|
case 'progress':
|
|
element.style.width = data.value + '%';
|
|
element.textContent = data.value + '%';
|
|
break;
|
|
case 'chart':
|
|
|
|
if (typeof Chart !== 'undefined' && element.chart) {
|
|
updateChart(element.chart, data);
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
element.classList.add('updated');
|
|
setTimeout(() => { element.classList.remove('updated'); }, 2000);
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateChart(chart, data) {
|
|
if (data.labels) {
|
|
chart.data.labels = data.labels;
|
|
}
|
|
|
|
if (data.datasets) {
|
|
chart.data.datasets = data.datasets;
|
|
} else if (data.values) {
|
|
|
|
chart.data.datasets[0].data = data.values;
|
|
}
|
|
|
|
chart.update();
|
|
}
|
|
|
|
|
|
|
|
|
|
function initializeVisualEffects() {
|
|
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
const targetId = this.getAttribute('href');
|
|
const targetElement = document.querySelector(targetId);
|
|
|
|
if (targetElement) {
|
|
targetElement.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
const fadeElements = document.querySelectorAll('.fade-in-element');
|
|
if (fadeElements.length > 0) {
|
|
const fadeObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('visible');
|
|
fadeObserver.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.1 });
|
|
|
|
fadeElements.forEach(element => {
|
|
fadeObserver.observe(element);
|
|
});
|
|
}
|
|
|
|
|
|
const animatedElements = document.querySelectorAll('.animated-element');
|
|
animatedElements.forEach(element => {
|
|
element.addEventListener('mouseenter', function() {
|
|
const animation = this.getAttribute('data-animation') || 'pulse';
|
|
this.classList.add(animation);
|
|
});
|
|
|
|
element.addEventListener('animationend', function() {
|
|
this.classList.remove(this.getAttribute('data-animation') || 'pulse');
|
|
});
|
|
});
|
|
} |