v3 / static /js /main.js
EGYADMIN's picture
Upload 115 files
82676b8 verified
/**
* ملف السكربت الرئيسي لنظام تسعير المناقصات
*/
// دالة التهيئة عند تحميل الصفحة
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');
});
});
}