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); const colors = [ "#00bcd4", "#4caf50", "#f44336", "#ff9800", "#9c27b0", "#e91e63", "#009688", "#3f51b5", "#607d8b", "#795548" ]; function normalizeArabic(str = "") { return str .replace(/[\u064B-\u0652]/g, "") .replace(/[أإآا]/g, "ا") .replace(/ى/g, "ي") .replace(/ؤ/g, "و") .replace(/ئ/g, "ي") .replace(/ة/g, "ه") .toLowerCase(); } function matches(text, q) { if (!q) return true; return normalizeArabic(String(text ?? "")).includes(normalizeArabic(q)); } function highlight(text, q) { if (!q) return String(text ?? ""); const esc = q.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); return String(text ?? "").replace( new RegExp(esc, "gi"), m => `${m}` ); } function renderPagination() { const pages = Math.ceil(filtered.length / rowsPerPage); pagination.innerHTML = ""; if (pages <= 1) return; const makeBtn = (label, page, opts = {}) => { const b = document.createElement("button"); b.textContent = label; b.className = "page-btn" + (opts.ghost ? " ghost" : ""); b.disabled = !!opts.disabled; b.addEventListener("click", () => { currentPage = page; updateView(); window.scrollTo({ top: 0, behavior: "smooth" }); }); return b; }; pagination.appendChild( makeBtn("‹", Math.max(1, currentPage - 1), { ghost: true, disabled: currentPage === 1 }) ); const windowSize = 5; let total = pages; let start = Math.max(1, currentPage - Math.floor(windowSize / 2)); let end = Math.min(total, 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) { pagination.appendChild( Object.assign(document.createElement("span"), { className: "dots", textContent: "..." }) ); } 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 < total - 1) { pagination.appendChild( Object.assign(document.createElement("span"), { className: "dots", textContent: "..." }) ); } if (end < total) { pagination.appendChild( makeBtn(String(total), total, { ghost: currentPage !== total }) ); } pagination.appendChild( makeBtn("›", Math.min(total, currentPage + 1), { ghost: true, disabled: currentPage === total }) ); } function renderTable() { const q = searchBox.value.trim(); tableBody.innerHTML = ""; const start = (currentPage - 1) * rowsPerPage; const pageItems = filtered.slice(start, start + 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((it, i) => { const tr = document.createElement("tr"); const code = it.code ?? ""; const act = it.activity ?? it.name ?? ""; const codeTd = document.createElement("td"); codeTd.innerHTML = highlight(code, q); const actTd = document.createElement("td"); actTd.innerHTML = highlight(act, q); const colorTd = document.createElement("td"); const colorBar = document.createElement("div"); colorBar.className = "color-bar"; colorBar.style.backgroundColor = colors[i % colors.length]; colorTd.appendChild(colorBar); tr.appendChild(codeTd); tr.appendChild(actTd); tr.appendChild(colorTd); tableBody.appendChild(tr); }); } function updateView() { const q = searchBox.value.trim(); filtered = data.filter( it => matches(it.code, q) || matches(it.activity ?? it.name, q) ); if (!q) { counter.textContent = `📊 إجمالي الأنشطة: ${data.length.toLocaleString()}`; } else { counter.textContent = filtered.length ? `✅ عدد النتائج: ${filtered.length.toLocaleString()}` : "🤔 لا توجد نتائج مطابقة"; } if (loading) loading.style.display = "none"; renderTable(); renderPagination(); } function debounce(fn, delay = 200) { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), delay); }; } searchBox.addEventListener( "input", debounce(() => { currentPage = 1; updateView(); }, 180) ); clearSearch.addEventListener("click", () => { searchBox.value = ""; searchBox.focus(); currentPage = 1; updateView(); }); pageSizeEl.addEventListener("change", () => { rowsPerPage = parseInt(pageSizeEl.value, 10); currentPage = 1; updateView(); }); document.addEventListener("keydown", (e) => { if (e.key === "/") { e.preventDefault(); searchBox.focus(); } }); (function initTheme() { const saved = localStorage.getItem("theme") || "light"; document.documentElement.classList.toggle("dark", saved === "dark"); themeBtn.textContent = saved === "dark" ? "☀️ فاتح" : "🌙 ليلي"; themeBtn.addEventListener("click", () => { const isDark = document.documentElement.classList.toggle("dark"); localStorage.setItem("theme", isDark ? "dark" : "light"); themeBtn.textContent = isDark ? "☀️ فاتح" : "🌙 ليلي"; }); })(); fetch("data.json", { cache: "no-cache" }) .then(r => r.json()) .then(json => { data = Array.isArray(json) ? json.map(row => { const rawCode = String( row.code ?? row.Code ?? row["رمز"] ?? "" ).trim(); return { code: rawCode.slice(0, 4), activity: row.activity ?? row.Activity ?? row.name ?? row["النشاط"] ?? "" }; }) : []; updateView(); }) .catch(err => { console.error(err); if (loading) loading.textContent = "تعذر تحميل البيانات ❌"; }); (function () { const tw = document.querySelector(".table-wrap"); if (!tw) return; let t; tw.addEventListener("scroll", () => { tw.classList.add("scrolling"); clearTimeout(t); t = setTimeout(() => tw.classList.remove("scrolling"), 180); }); })();