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