// عناصر الواجهة const searchBox = document.getElementById("searchBox"); const clearSearch = document.getElementById("clearSearch"); const pageSizeEl = document.getElementById("pageSize"); const themeBtn = document.getElementById("themeToggle"); const tableBody = document.getElementById("tableBody"); const pagination = document.getElementById("pagination"); const counter = document.getElementById("counter"); const loading = document.getElementById("loading"); let data = []; let filtered = []; let currentPage = 1; const DEFAULT_PAGE_SIZE = 10; let rowsPerPage = DEFAULT_PAGE_SIZE; // ضبط قيمة عدد الصفوف الافتراضي إن وُجد العنصر if (pageSizeEl) { pageSizeEl.value = String(DEFAULT_PAGE_SIZE); } /* ======================== دوال مساعدة للبحث بالعربية ======================== */ // توحيد الحروف العربية لتسهيل البحث function normalizeArabic(str = "") { return str .replace(/[\u064B-\u0652]/g, "") // إزالة التشكيل .replace(/[أإآا]/g, "ا") .replace(/ى/g, "ي") .replace(/ؤ/g, "و") .replace(/ئ/g, "ي") .replace(/ة/g, "ه") .toLowerCase(); } // التحقق إن كان النص يحتوي على عبارة البحث function matches(text, query) { if (!query) return true; return normalizeArabic(String(text ?? "")).includes( normalizeArabic(query) ); } // تمييز كلمة البحث داخل النص function highlight(text, query) { if (!query) return String(text ?? ""); const safe = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); return String(text ?? "").replace( new RegExp(safe, "gi"), (m) => `${m}` ); } /* ======================== الترقيم (Pagination) ======================== */ function renderPagination() { pagination.innerHTML = ""; const totalRows = filtered.length; const totalPages = Math.ceil(totalRows / rowsPerPage); if (totalPages <= 1) return; const makeBtn = (label, page, options = {}) => { const btn = document.createElement("button"); btn.textContent = label; btn.className = "page-btn" + (options.ghost ? " ghost" : ""); if (options.disabled) btn.disabled = true; btn.addEventListener("click", () => { currentPage = page; updateView(); window.scrollTo({ top: 0, behavior: "smooth" }); }); return btn; }; // السابق pagination.appendChild( makeBtn("‹", Math.max(1, currentPage - 1), { ghost: true, disabled: currentPage === 1, }) ); // منطق إظهار أرقام الصفحات const windowSize = 5; let start = Math.max(1, currentPage - Math.floor(windowSize / 2)); let end = Math.min(totalPages, start + windowSize - 1); if (end - start + 1 < windowSize) { start = Math.max(1, end - windowSize + 1); } if (start > 1) { pagination.appendChild( makeBtn("1", 1, { ghost: currentPage !== 1 }) ); } if (start > 2) { const dots = document.createElement("span"); dots.className = "dots"; dots.textContent = "..."; pagination.appendChild(dots); } for (let p = start; p <= end; p++) { const btn = makeBtn(String(p), p); if (p === currentPage) btn.classList.add("active"); pagination.appendChild(btn); } if (end < totalPages - 1) { const dots = document.createElement("span"); dots.className = "dots"; dots.textContent = "..."; pagination.appendChild(dots); } if (end < totalPages) { pagination.appendChild( makeBtn(String(totalPages), totalPages, { ghost: currentPage !== totalPages, }) ); } // التالي pagination.appendChild( makeBtn("›", Math.min(totalPages, currentPage + 1), { ghost: true, disabled: currentPage === totalPages, }) ); } /* ======================== رسم الجدول (3 أعمدة) ======================== */ function renderTable() { const query = searchBox ? searchBox.value.trim() : ""; tableBody.innerHTML = ""; const startIndex = (currentPage - 1) * rowsPerPage; const pageItems = filtered.slice(startIndex, startIndex + rowsPerPage); if (pageItems.length === 0) { const tr = document.createElement("tr"); const td = document.createElement("td"); td.colSpan = 3; td.className = "no-results"; td.textContent = "لا توجد نتائج مطابقة لبحثك."; tr.appendChild(td); tableBody.appendChild(tr); return; } pageItems.forEach((item) => { const tr = document.createElement("tr"); const nameTd = document.createElement("td"); nameTd.innerHTML = highlight(item.name, query); const qtyTd = document.createElement("td"); qtyTd.innerHTML = highlight(item.quantity, query); const unitTd = document.createElement("td"); unitTd.innerHTML = highlight(item.unit, query); tr.appendChild(nameTd); tr.appendChild(qtyTd); tr.appendChild(unitTd); tableBody.appendChild(tr); }); } /* ======================== تحديث العرض (فلترة + عداد) ======================== */ function updateView() { const query = searchBox ? searchBox.value.trim() : ""; filtered = data.filter((item) => { return ( matches(item.code, query) || matches(item.name, query) || matches(item.quantity, query) || matches(item.unit, query) ); }); if (!counter) return; if (!query) { counter.textContent = `إجمالي البنود: ${data.length.toLocaleString()}`; } else if (filtered.length) { counter.textContent = `✅ عدد النتائج: ${filtered.length.toLocaleString()}`; } else { counter.textContent = "لا توجد نتائج مطابقة"; } if (loading) { loading.style.display = "none"; } renderTable(); renderPagination(); } /* ======================== أدوات مساعدة ======================== */ function debounce(fn, delay = 220) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; } /* ======================== ربط الأحداث ======================== */ // البحث الفوري if (searchBox) { searchBox.addEventListener( "input", debounce(() => { currentPage = 1; updateView(); }) ); } // زر مسح البحث if (clearSearch) { clearSearch.addEventListener("click", () => { searchBox.value = ""; searchBox.focus(); currentPage = 1; updateView(); }); } // تغيير عدد الصفوف if (pageSizeEl) { pageSizeEl.addEventListener("change", () => { const val = parseInt(pageSizeEl.value, 10); rowsPerPage = !isNaN(val) && val > 0 ? val : DEFAULT_PAGE_SIZE; currentPage = 1; updateView(); }); } // اختصار / للتركيز على البحث document.addEventListener("keydown", (e) => { if (e.key === "/") { if (document.activeElement === searchBox) return; e.preventDefault(); if (searchBox) searchBox.focus(); } }); /* ======================== الثيم (فاتح / ليلي) ======================== */ (function initTheme() { if (!themeBtn) return; const saved = localStorage.getItem("theme") || "light"; const root = document.documentElement; if (saved === "dark") { root.classList.add("dark"); themeBtn.textContent = "☀️ فاتح"; } else { root.classList.remove("dark"); themeBtn.textContent = "ليلي"; } themeBtn.addEventListener("click", () => { const isDark = root.classList.toggle("dark"); localStorage.setItem("theme", isDark ? "dark" : "light"); themeBtn.textContent = isDark ? "☀️ فاتح" : "ليلي"; }); })(); /* ======================== تحميل data.json ======================== */ fetch("data.json", { cache: "no-cache" }) .then((res) => res.json()) .then((json) => { // توحيد الحقول + دعم أي تسميات قديمة data = (Array.isArray(json) ? json : []).map((row) => ({ code: row.code ?? row.Code ?? row["رمز البند"] ?? "", name: row.name ?? row["اسم البند"] ?? "", quantity: row.quantity ?? row["الكمية المطلوبة"] ?? "", unit: row.unit ?? row["وحدة البند"] ?? "", })); currentPage = 1; updateView(); }) .catch((err) => { console.error("تعذر تحميل data.json:", err); if (loading) { loading.textContent = "تعذر تحميل البيانات ❌"; } }); /* ======================== تأثير بسيط لحالة التمرير ======================== */ (function attachScrollHint() { const wrap = document.querySelector(".table-wrap"); if (!wrap) return; let timer; wrap.addEventListener("scroll", () => { wrap.classList.add("scrolling"); clearTimeout(timer); timer = setTimeout(() => { wrap.classList.remove("scrolling"); }, 200); }); })();