ticket-parser / app.js
stat2025's picture
Update app.js
acf7905 verified
raw
history blame
25.5 kB
/* 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;i<starts.length;i++){
const s = starts[i];
const e = i+1<starts.length ? starts[i+1] : text.length;
const slice = text.slice(s,e).trim();
if(slice) parts.push(slice);
}
return parts;
}
const blocks = text.split(/\n\s*\n+/).map(p=>p.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();