Spaces:
Sleeping
Sleeping
/** | |
* ملف السكربت الرئيسي لنظام تسعير المناقصات | |
*/ | |
// دالة التهيئة عند تحميل الصفحة | |
document.addEventListener('DOMContentLoaded', function() { | |
// تفعيل تلميحات الأدوات | |
initializeTooltips(); | |
// تفعيل التحقق من الإدخال في النماذج | |
initializeFormValidation(); | |
// تفعيل وظيفة البحث والتصفية | |
initializeSearchAndFilters(); | |
// تفعيل وظائف التصدير | |
initializeExportFunctions(); | |
// تفعيل تحديثات البيانات المباشرة | |
initializeLiveDataUpdates(); | |
// تفعيل التأثيرات البصرية | |
initializeVisualEffects(); | |
}); | |
/** | |
* تفعيل تلميحات الأدوات | |
*/ | |
function initializeTooltips() { | |
// يمكن استخدام مكتبة Bootstrap للتلميحات إذا تم تحميلها | |
if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) { | |
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); | |
[...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); | |
} else { | |
// تنفيذ بسيط للتلميحات إذا لم تكن مكتبة Bootstrap متاحة | |
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() { | |
// تصدير إلى CSV | |
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'); | |
}); | |
}); | |
// تصدير إلى PDF | |
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'); | |
}); | |
}); | |
// تصدير إلى Excel | |
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'); | |
}); | |
}); | |
} | |
/** | |
* تصدير جدول إلى CSV | |
*/ | |
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++) { | |
// تنظيف النص وإحاطته بعلامات اقتباس للتوافق مع تنسيق CSV | |
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); | |
// إنشاء URL من Blob | |
link.href = URL.createObjectURL(blob); | |
link.style.visibility = 'hidden'; | |
// إضافة الرابط وتنفيذ النقر عليه | |
document.body.appendChild(link); | |
link.click(); | |
document.body.removeChild(link); | |
} | |
/** | |
* تصدير جدول إلى PDF (يتطلب مكتبة خارجية مثل jsPDF) | |
*/ | |
function exportTableToPDF(tableId, filename) { | |
// التحقق من وجود مكتبة jsPDF | |
if (typeof jsPDF === 'undefined') { | |
console.error('مكتبة jsPDF غير متوفرة. يرجى تضمينها لاستخدام هذه الوظيفة.'); | |
return; | |
} | |
const table = document.getElementById(tableId); | |
if (!table) return; | |
// إنشاء مستند PDF جديد | |
const doc = new jsPDF('l', 'pt', 'a4'); | |
// تحويل الجدول إلى PDF | |
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); | |
} | |
/** | |
* تصدير جدول إلى Excel (يتطلب مكتبة خارجية مثل SheetJS) | |
*/ | |
function exportTableToExcel(tableId, filename) { | |
// التحقق من وجود مكتبة SheetJS | |
if (typeof XLSX === 'undefined') { | |
console.error('مكتبة SheetJS (XLSX) غير متوفرة. يرجى تضمينها لاستخدام هذه الوظيفة.'); | |
return; | |
} | |
const table = document.getElementById(tableId); | |
if (!table) return; | |
// تحويل جدول HTML إلى دفتر عمل | |
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); // تحديث كل 30 ثانية | |
} | |
} | |
/** | |
* تحديث محتوى عنصر بناءً على البيانات | |
*/ | |
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': | |
// يفترض وجود مكتبة Chart.js | |
if (typeof Chart !== 'undefined' && element.chart) { | |
updateChart(element.chart, data); | |
} | |
break; | |
} | |
// تطبيق تأثير التحديث | |
element.classList.add('updated'); | |
setTimeout(() => { element.classList.remove('updated'); }, 2000); | |
} | |
/** | |
* تحديث مخطط باستخدام Chart.js | |
*/ | |
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'); | |
}); | |
}); | |
} |