/* v10.3: Smart Paste append + reliable modal fallback + safe splitting + hard clear */ const EXPORT_COLUMNS = [ "التصنيف","نوع المشكلة","وقت حدوث المشكلة","اسم صاحب المشكلة", "رقم الهوية","رقم الجهاز","رقم الجوال","المسح","المنطقة","اسم الدعم الفني","الحالة" ]; const FIELD_ALIASES = { "نوع المشكلة": ["نوع المشكله","نوع المشكلة","المشكلة"], "وقت حدوث المشكلة": ["وقت حدوث المشكله","وقت حدوث المشكلة","وقت المشكلة","وقت حدوث"], "اسم صاحب المشكلة": ["اسم صاحب المشكله","اسم صاحب المشكلة","اسم صاحب البلاغ","الاسم"], "رقم الهوية": ["رقم الهويه","رقم الهوية","الهوية"], "رقم الجهاز": ["رقم الجهاز","الجهاز"], "رقم الجوال": ["رقم الجوال","الجوال","الهاتف"], "المسح": ["المسح","اسم المسح"], "المنطقة": ["المنطقة","المنطقه","اسم المنطقة","المدينة","المحافظة","منطقة"] }; const CLASS_RULES = { "استفسار": ["استفسار","سؤال","استعلام","معلومة","استفسارات"], "إضافة أجهزة": ["اضافة جهاز","إضافة أجهزة","اضافة اجهزة","تركيب جهاز","جهاز جديد","تسجيل جهاز","ربط جهاز","اضافة ماسح","إضافة ماسح"], "الاستمارة": ["الاستمارة","استمارة","النموذج","نموذج","الفورم","تعليق الاستمارة","لا استطيع اكمال الاستمارة","التعبئة"], "التقييم": ["التقييم","تقييم","feedback","survey","رضا","نجوم"], "الخرائط": ["الخرائط","خرائط","map","gps","تحديد الموقع","احداثيات","إحداثيات","الموقع الجغرافي"], "السوتي": ["السوتي","سوتي","soti","soti assist","mobicontrol","soti mobicontrol"], "الشبكة": ["الشبكة","شبكة","نت","انترنت","إنترنت","wifi","واي فاي","4g","5g","ضعف الشبكة","stc","mobily","زين","weak signal","no signal"], "النسخة": ["النسخة","نسخة","الإصدار","اصدار","version","build","release","تحديث نسخة","ترقية النسخة"], "النظام المكتبي": ["النظام المكتبي","نسخة ويندوز","ويندوز","windows","pc app","برنامج المكتب","التطبيق على الكمبيوتر","الديسكتوب"], "تسجيل دخول": ["تسجيل دخول","تسجيل الدخول","login","signin","رفض تسجيل الدخول","لا يقبل الدخول","اسم المستخدم","كلمة المرور","نسيت كلمة السر","إعادة تعيين"], "تفعيل حساب": ["تفعيل حساب","تفعيل","activation","activate","رمز التفعيل","كود التفعيل"], "تناقل البيانات": ["تناقل البيانات","ترحيل البيانات","مزامنة","sync","مزامنه","نقل البيانات","رفع البيانات","sync failed","المزامنة"], "صيانة وتحديث الأجهزة": ["صيانة","تحديث الأجهزة","تحديث جهاز","ترقية الجهاز","اعطال الجهاز","تصليح","صيانة وتحديث الأجهزة","صيانة الجهاز"], }; const CLASS_PRIORITY = [ "صيانة وتحديث الأجهزة","إضافة أجهزة","تسجيل دخول","تفعيل حساب", "الاستمارة","التقييم","الخرائط","السوتي","الشبكة","النسخة", "النظام المكتبي","تناقل البيانات","استفسار", ]; const TICKET_SEP = /\n\s*(?:\n|—+|-{3,}|={3,}|🔴+)+\s*\n/; const MIN_SPLIT_SPAN = 80; const arabicDigitsMap = {"٠":"0","١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9"}; function normalizeText(s){ if(typeof s!=="string") return ""; return s.replace(/\r\n/g,"\n") .replace(/[\u200f\u200e\u202a-\u202e\u2066-\u2069\u00a0]/g," ") .replace(/[٠-٩]/g, d => arabicDigitsMap[d] ) .replace(/[ــ]+/g,"") .trim(); } function lettersOnly(ar){ return (ar||"").replace(/[^A-Za-z\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\s]/g,"").replace(/\s{2,}/g," ").trim(); } function alnumAr(s){ return (s||"").replace(/[^0-9A-Za-z\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\s\-\._/]/g,"").replace(/\s{2,}/g," ").trim(); } function digitsOnly(s){ return (s||"").replace(/\D+/g,""); } function normalizeTimeLoose(val){ const t = normalizeText(val); let m = t.match(/(\d{1,2})[:٫\.\-:](\d{2})\s*(ص|صباح|صباحا|am|م|مساء|pm)?/i); if(m){ let h = parseInt(m[1],10), mn = m[2], ampm = (m[3]||"").toLowerCase(); if(ampm){ if((/م|مساء|pm/).test(ampm) && h<12) h+=12; if((/ص|صباح|صباحا|am/).test(ampm) && h===12) h=0; } return `${String(h).padStart(2,"0")}:${mn}`; } m = t.match(/(\d{1,2})\s*(ص|صباح|صباحا|am|م|مساء|pm)/i); if(m){ let h = parseInt(m[1],10); const ampm = (m[2]||"").toLowerCase(); if((/م|مساء|pm/).test(ampm) && h<12) h+=12; if((/ص|صباح|صباحا|am/).test(ampm) && h===12) h=0; return `${String(h).padStart(2,"0")}:00`; } return ""; } function normalizeDate(v){ v=(v||"").trim(); let m=v.match(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2,4})/); if(m){ let d=+m[1], mo=+m[2], y=+m[3]; if(y<100) y+=2000; return `${y.toString().padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).toString().padStart(2,"0")}`; } m=v.match(/(\d{4})[\/\-](\d{1,2})[\/\-](\d{1,2})/); if(m){ let y=+m[1], mo=+m[2], d=+m[3]; return `${y.toString().padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).toString().padStart(2,"0")}`; } return v; } function parseDateTime(raw){ const t = normalizeText(raw); const d = normalizeDate(t); const hhmm = normalizeTimeLoose(t); if(d && /^\d{4}-\d{2}-\d{2}$/.test(d) && hhmm) return `${d} ${hhmm}`; if(d && /^\d{4}-\d{2}-\d{2}$/.test(d)) return d; if(hhmm) return hhmm; return t; } function findStartsByLabels(text, labels){ const lblRe = labels.map(l=>l.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join("|"); const re = new RegExp(`(^|\\n)\\s*(?:[-–—\\*•]+|\\d+[\\)\\.]\\s*)?\\s*(?:${lblRe})(?:\\s*[::]|\\s+)`,"gi"); const idxs=[]; let m; while((m = re.exec(text))){ idxs.push(m.index + (m[1]?m[1].length:0)); } return idxs; } function splitTickets(raw){ const text = normalizeText(raw); if(!text) return []; if(TICKET_SEP.test(text)){ return text.split(TICKET_SEP).map(p=>p.trim()).filter(Boolean); } const starts = findStartsByLabels(text, ["نوع المشكلة","نوع المشكله"]) .sort((a,b)=>a-b) .filter((pos,i,arr)=> i===0 || (pos - arr[i-1]) >= MIN_SPLIT_SPAN); if(starts.length >= 2){ const parts=[]; for(let i=0;ip.trim()).filter(Boolean); if(blocks.length>1) return blocks; return [text]; } function findAfterLabel(text, labels){ const hay = "\n" + normalizeText(text) + "\n"; for(const rawLbl of labels){ const lbl = rawLbl.replace(/[.*+?^${}()|[\]\\]/g,'\\$&'); let m = hay.match(new RegExp(`(?:^|\\n)\\s*${lbl}\\s*[::]\\s*([^\\n]+)`, "i")); if(m) return m[1].trim(); m = hay.match(new RegExp(`(?:^|\\n)\\s*${lbl}\\s+([^\\n]+)`, "i")); if(m) return m[1].trim(); } return ""; } function extractFields(ticketText){ const text = normalizeText(ticketText); const out = { "نوع المشكلة":"", "وقت حدوث المشكلة":"", "اسم صاحب المشكلة":"", "رقم الهوية":"", "رقم الجهاز":"", "رقم الجوال":"", "المسح":"", "المنطقة":"" }; let v = findAfterLabel(text, FIELD_ALIASES["نوع المشكلة"]); if(v) out["نوع المشكلة"] = alnumAr(v); v = findAfterLabel(text, FIELD_ALIASES["وقت حدوث المشكلة"]); if(v) out["وقت حدوث المشكلة"] = parseDateTime(v); v = findAfterLabel(text, FIELD_ALIASES["اسم صاحب المشكلة"]); if(v) out["اسم صاحب المشكلة"] = v; v = findAfterLabel(text, FIELD_ALIASES["رقم الهوية"]); if(v) out["رقم الهوية"] = digitsOnly(v); if(!out["رقم الهوية"]){ const m = text.match(/(?:^|\D)((?:1|2)\d{9})(?:\D|$)/); if(m) out["رقم الهوية"] = m[1]; } v = findAfterLabel(text, FIELD_ALIASES["رقم الجهاز"]); if(v) out["رقم الجهاز"] = digitsOnly(v); if(!out["رقم الجهاز"]){ const m = text.match(/(?:^|\D)(\d{5,20})(?:\D|$)/); if(m) out["رقم الجهاز"] = m[1]; } v = findAfterLabel(text, FIELD_ALIASES["رقم الجوال"]); if(v) out["رقم الجوال"] = digitsOnly(v); if(!out["رقم الجوال"]){ const m = text.match(/(?:^|\D)(05\d{7,})(?:\D|$)/); if(m) out["رقم الجوال"] = m[1]; } v = findAfterLabel(text, FIELD_ALIASES["المسح"]); if(v) out["المسح"] = alnumAr(v); v = findAfterLabel(text, FIELD_ALIASES["المنطقة"]); if(v) out["المنطقة"] = lettersOnly(v); return out; } function classifyTicket(text, fields){ const hay = normalizeText(`${text}\n${fields?.["نوع المشكلة"]||""}`).toLowerCase(); for(const label of CLASS_PRIORITY){ const kws = CLASS_RULES[label] || []; for(const kw of kws){ const needle = normalizeText(kw).toLowerCase(); if(needle && hay.includes(needle)) return label; } } return "استفسار"; } function parseTicketsWithExtras(raw, agentName, defaultRegion){ const regionChosen = (defaultRegion || "").toString(); return splitTickets(raw||"").map(t => { const f = extractFields(t); const cls = classifyTicket(t, f); const region = regionChosen ? regionChosen : (f["المنطقة"] || ""); return { "التصنيف": cls, "نوع المشكلة": f["نوع المشكلة"] || "", "وقت حدوث المشكلة": f["وقت حدوث المشكلة"] || "", "اسم صاحب المشكلة": f["اسم صاحب المشكلة"] || "", "رقم الهوية": f["رقم الهوية"] || "", "رقم الجهاز": f["رقم الجهاز"] || "", "رقم الجوال": f["رقم الجوال"] || "", "المسح": f["المسح"] || "", "المنطقة": region, "اسم الدعم الفني": agentName || "", "الحالة": "تم الحل", }; }); } function buildTable(rows){ const theadRow = document.getElementById("theadRow"); const tbody = document.getElementById("tbody"); theadRow.innerHTML = ""; EXPORT_COLUMNS.forEach(col=>{ const th=document.createElement("th"); th.textContent=col; theadRow.appendChild(th); }); tbody.innerHTML = ""; rows.forEach(r=>{ const tr=document.createElement("tr"); EXPORT_COLUMNS.forEach(col=>{ const td=document.createElement("td"); td.contentEditable="true"; td.textContent = r[col]||""; tr.appendChild(td); }); tbody.appendChild(tr); }); } function readTable(){ const tbody = document.getElementById("tbody"); const rows = []; [...tbody.querySelectorAll("tr")].forEach(tr=>{ const obj={}; [...tr.children].forEach((td,idx)=>{ obj[EXPORT_COLUMNS[idx]] = td.textContent.trim(); }); rows.push(obj); }); return rows; } function updateBadge(n){ const b = document.getElementById("countBadge"); b.textContent = n; b.hidden = (n===0); } function setButtonsEnabled(hasRows){ document.getElementById("btn-export").disabled = !hasRows; document.getElementById("btn-copy").disabled = !hasRows; } function validateCells(){ const tbody=document.getElementById("tbody"); const idxPhone = EXPORT_COLUMNS.indexOf("رقم الجوال"); const idxID = EXPORT_COLUMNS.indexOf("رقم الهوية"); [...tbody.rows].forEach(tr=>{ if(idxPhone>=0){ const td=tr.children[idxPhone]; const raw=(td.textContent||"").trim(); const digits = raw.replace(/\D/g,""); const invalid = !!raw && digits.length < 9; td.classList.toggle("invalid", invalid); } if(idxID>=0){ const td=tr.children[idxID]; const raw=(td.textContent||"").trim(); const digits = raw.replace(/\D/g,""); const invalid = !!raw && digits.length !== 10; td.classList.toggle("invalid", invalid); } }); } document.addEventListener("input",(e)=>{ if(e.target && e.target.closest && e.target.closest("#tbody")){ validateCells(); saveState(); } }); function toast(msg){ const t = document.getElementById("toast"); t.textContent = msg; t.hidden = false; t.classList.remove("show"); void t.offsetWidth; t.classList.add("show"); setTimeout(()=>{ t.hidden = true; }, 2000); } async function exportExcel(){ const rows = readTable(); if(!rows.length){ toast("لا يوجد بيانات لتصديرها."); return; } const TEMPLATE_HEADERS = [ "نوع المشكلة","وصف المشكلة","المنطقة","اسم المسح","اسم المشغل", "رقم الجوال","رقم الهوية ID","رقم الجهاز","تاريخ اليوم بالميلادي","الحالة","اسم الدعم الفني", ]; const mapRow = (r)=>{ const today = new Date(); const yyyy=today.getFullYear(), mm=String(today.getMonth()+1).padStart(2,"0"), dd=String(today.getDate()).padStart(2,"0"); const todayStr = `${yyyy}-${mm}-${dd}`; return { "نوع المشكلة": r["نوع المشكلة"]||"", "وصف المشكلة": r["التصنيف"] || r["نوع المشكلة"] || "", "المنطقة": r["المنطقة"]||"", "اسم المسح": r["المسح"]||"", "اسم المشغل": r["اسم صاحب المشكلة"]||"", "رقم الجوال": (r["رقم الجوال"]||"").toString(), "رقم الهوية ID":(r["رقم الهوية"]||"").toString(), "رقم الجهاز": (r["رقم الجهاز"]||"").toString(), "تاريخ اليوم بالميلادي": todayStr, "الحالة": r["الحالة"]||"تم الحل", "اسم الدعم الفني": r["اسم الدعم الفني"]||"", }; }; const wb = new ExcelJS.Workbook(); const ws = wb.addWorksheet("التذاكر", { views: [{ rightToLeft: true }] }); const colWidths = [18,26,16,18,20,18,18,18,22,14,18]; TEMPLATE_HEADERS.forEach((h,i)=> ws.getColumn(i+1).width = colWidths[i]||18); ws.addRow(TEMPLATE_HEADERS); const headerRow = ws.getRow(1); headerRow.height = 24; headerRow.eachCell((cell)=>{ cell.font = { bold:true, color:{argb:"FFFFFFFF"} }; cell.alignment = { horizontal:"center", vertical:"middle" }; cell.fill = { type:"pattern", pattern:"solid", fgColor:{argb:"FF4137A8"} }; cell.border = { top:{style:"thin",color:{argb:"FFCDD2E1"}}, bottom:{style:"thin",color:{argb:"FFCDD2E1"}}, left:{style:"thin",color:{argb:"FFE5E7EB"}}, right:{style:"thin",color:{argb:"FFE5E7EB"}}, }; }); const toTextCols = new Set(["رقم الجوال","رقم الهوية ID","رقم الجهاز"]); const rawRows = readTable(); rawRows.forEach((r,idx)=>{ const m = mapRow(r); const vals = TEMPLATE_HEADERS.map(h => (m[h] ?? "")); const row = ws.addRow(vals); row.alignment = { horizontal:"center", vertical:"middle" }; const even = (idx % 2) === 1; row.eachCell((cell, colNumber)=>{ cell.border = { top:{style:"thin",color:{argb:"FFE5E7EB"}}, bottom:{style:"thin",color:{argb:"FFE5E7EB"}}, left:{style:"thin",color:{argb:"FFE5E7EB"}}, right:{style:"thin",color:{argb:"FFE5E7EB"}}, }; if(even) cell.fill = { type:"pattern", pattern:"solid", fgColor:{argb:"FFF5F8FF"} }; const header = TEMPLATE_HEADERS[colNumber-1]; if(toTextCols.has(header)) cell.value = String(cell.value ?? ""); }); }); const ts = new Date().toISOString().replace(/\D/g,"").slice(0,14); const base = (document.getElementById("fname").value || "Ticket").trim() || "Ticket"; const filename = `${base}_${ts}.xlsx`; const buffer = await wb.xlsx.writeBuffer(); const blob = new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }); const file = new File([blob], filename, { type: blob.type }); if (navigator.canShare && navigator.canShare({ files: [file] })) { try { await navigator.share({ files: [file], title: "ملف التذاكر" }); toast("تمت المشاركة/الحفظ."); return; } catch(e){} } const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); setTimeout(()=>URL.revokeObjectURL(url), 1000); toast("تم تنزيل الملف بتنسيق القالب."); } async function copyToClipboardTSV(){ const rows = readTable(); if(!rows.length){ toast("لا يوجد بيانات لنسخها."); return; } const textCols = new Set(["رقم الهوية","رقم الجهاز","رقم الجوال"]); const header = EXPORT_COLUMNS.join("\t"); const body = rows.map(r => EXPORT_COLUMNS.map(c => { let v = (r[c] ?? "").toString().replace(/\t/g," "); if(textCols.has(c) && v && /^[0-9]+$/.test(v)) v = "'" + v; return v; }).join("\t") ).join("\r\n"); const tsv = "\uFEFF" + header + "\r\n" + body; try{ await navigator.clipboard.writeText(tsv); toast("تم النسخ — الصق/ي مباشرة في Excel."); } catch(e){ const ta = document.createElement("textarea"); ta.value = tsv; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); document.body.removeChild(ta); toast("تم النسخ — الصق/ي مباشرة في Excel."); } } const SAMPLE = `نوع المشكلة : لا استطيع اكمال الاستمارة بسبب تعليق وقت حدوث المشكلة: 21/8/2025 7 صباحا اسم صاحب المشكلة : نوف الناصر رقم الهوية: 1234567890 رقم الجهاز: 01234 رقم الجوال: 0558174717 اسم المسح: الخبر 2025 اسم المنطقة: الشرقية`; const STATE_KEY = "ticketParserState_v10_3"; const ALL_STATE_KEYS = [ "ticketParserState_v8","ticketParserState_v9","ticketParserState_v10", "ticketParserState_v10_1","ticketParserState_v10_2","ticketParserState_v10_3" ]; function ensureColumns(rows, agentName, defaultRegion){ if(!Array.isArray(rows)) return rows||[]; return rows.map(r=>{ const out = {...r}; if(!("التصنيف" in out) || !out["التصنيف"]){ const fakeText = Object.values(out).join("\n"); out["التصنيف"] = classifyTicket(fakeText, out); } if(!("اسم الدعم الفني" in out)) out["اسم الدعم الفني"] = agentName || out["اسم الدعم الفني"] || ""; if(!("الحالة" in out) || !out["الحالة"]) out["الحالة"] = "تم الحل"; if(defaultRegion) out["المنطقة"] = defaultRegion; return out; }); } function saveState(){ try{ const raw = document.getElementById("raw")?.value || ""; const fname = document.getElementById("fname")?.value || "Ticket"; const agent = document.getElementById("agentName")?.value || ""; const region= document.getElementById("regionDefault")?.value || ""; const rows = readTable(); localStorage.setItem(STATE_KEY, JSON.stringify({ raw, fname, agent, region, rows })); }catch{} } function loadState(){ try{ const s = localStorage.getItem(STATE_KEY); if(!s) return false; let { raw, fname, agent, region, rows } = JSON.parse(s); if(typeof raw === "string"){ const el=document.getElementById("raw"); if(el) el.value = raw; } if(typeof fname === "string"){ const el=document.getElementById("fname"); if(el) el.value = fname; } if(typeof agent === "string"){ const el=document.getElementById("agentName"); if(el) el.value = agent; } if(typeof region === "string"){ const el=document.getElementById("regionDefault"); if(el) el.value = region; } rows = ensureColumns(rows, agent, region); if(Array.isArray(rows) && rows.length){ buildTable(rows); validateCells(); updateBadge(rows.length); setButtonsEnabled(true); } return true; }catch{ return false; } } function clearAll(){ const rawEl = document.getElementById("raw"); const tbody = document.getElementById("tbody"); const fnameEl = document.getElementById("fname"); const agentEl = document.getElementById("agentName"); const regionEl= document.getElementById("regionDefault"); if(rawEl) rawEl.value = ""; if(tbody) tbody.innerHTML = ""; if(fnameEl) fnameEl.value = "Ticket"; if(agentEl) agentEl.value = ""; if(regionEl) regionEl.value = ""; updateBadge(0); setButtonsEnabled(false); try{ ALL_STATE_KEYS.forEach(k=>localStorage.removeItem(k)); }catch{} toast("تم مسح كل البيانات والتخزين."); } function normalizeForPaste(text){ const norm = normalizeText(text||""); const parts = splitTickets(norm); return parts.length ? parts.join("\n\n🔴🔴🔴\n") : norm; } async function smartPasteInto(el){ try{ const txt = await navigator.clipboard.readText(); if(txt && txt.trim()){ const formatted = normalizeForPaste(txt); if(el.value && el.value.trim()){ el.value = el.value.trimEnd() + "\n\n🔴🔴🔴\n" + formatted; }else{ el.value = formatted; } saveState(); toast("تم اللصق والتنظيم."); return; } openPasteModal(el); }catch{ openPasteModal(el); } } function openPasteModal(targetEl){ const modal = document.getElementById("pasteModal"); const input = document.getElementById("pasteInput"); const add = document.getElementById("pasteAdd"); const cancel= document.getElementById("pasteCancel"); input.value = ""; modal.hidden = false; input.focus(); function close(){ modal.hidden = true; add.removeEventListener("click", onAdd); cancel.removeEventListener("click", onCancel); document.removeEventListener("keydown", onEsc); } function onAdd(){ const txt = input.value || ""; const formatted = normalizeForPaste(txt); if(formatted.trim()){ if(targetEl.value && targetEl.value.trim()){ targetEl.value = targetEl.value.trimEnd() + "\n\n🔴🔴🔴\n" + formatted; }else{ targetEl.value = formatted; } saveState(); toast("تمت الإضافة."); } close(); } function onCancel(){ close(); } function onEsc(e){ if(e.key === "Escape"){ e.preventDefault(); close(); } } add.addEventListener("click", onAdd); cancel.addEventListener("click", onCancel); document.addEventListener("keydown", onEsc); } function init(){ const parseBtn = document.getElementById("btn-parse"); const exportBtn = document.getElementById("btn-export"); const copyBtn = document.getElementById("btn-copy"); const clearBtn = document.getElementById("btn-clear"); const sampleBtn = document.getElementById("btn-sample"); const smartPaste = document.getElementById("btn-smartpaste"); const rawEl = document.getElementById("raw"); const fnameEl = document.getElementById("fname"); const agentEl = document.getElementById("agentName"); const regionEl = document.getElementById("regionDefault"); loadState(); smartPaste.addEventListener("click", ()=> smartPasteInto(rawEl)); parseBtn.addEventListener("click", ()=>{ const raw = (rawEl.value || "").trim(); if(!raw){ toast("فضلاً الصق/ي تذاكر أولاً."); return; } const agent = agentEl.value || ""; const defRegion = regionEl.value || ""; const rows = parseTicketsWithExtras(raw, agent, defRegion); buildTable(rows); validateCells(); updateBadge(rows.length); setButtonsEnabled(rows.length>0); saveState(); toast(`تم استخراج ${rows.length} تذكرة.`); }); exportBtn.addEventListener("click", exportExcel); copyBtn.addEventListener("click", copyToClipboardTSV); clearBtn.addEventListener("click", clearAll); sampleBtn.addEventListener("click", ()=>{ rawEl.value = SAMPLE; saveState(); }); rawEl.addEventListener("input", saveState); fnameEl.addEventListener("input", saveState); agentEl.addEventListener("input", saveState); regionEl.addEventListener("change", saveState); document.addEventListener("keydown", (e)=>{ const ctrl = e.ctrlKey || e.metaKey; if(ctrl && e.key === "Enter"){ e.preventDefault(); parseBtn.click(); } else if(ctrl && e.key.toLowerCase() === "e"){ e.preventDefault(); exportBtn.click(); } else if(ctrl && e.shiftKey && e.key.toLowerCase() === "c"){ e.preventDefault(); copyBtn.click(); } else if(e.key === "Escape"){ e.preventDefault(); clearAll(); } }); setButtonsEnabled(!!document.getElementById("tbody")?.children.length); } init();