/** * ملف السكربت الرئيسي لنظام تسعير المناقصات */ // دالة التهيئة عند تحميل الصفحة 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'); }); }); }