Spaces:
Running
Running
Update app.js
Browse files
app.js
CHANGED
|
@@ -78,7 +78,7 @@ function compileFieldPatterns(){
|
|
| 78 |
const lbls = labels.map(l => l.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join("|");
|
| 79 |
pats[canonical] = [
|
| 80 |
new RegExp(`(?:^|\\n)\\s*(?:${lbls})\\s*${LABEL_SEP}(.+)$`, "mi"),
|
| 81 |
-
new RegExp(`(?:^|\\n)\\s*(?:${lbls})\\s*${LABEL_SEP}\\n
|
| 82 |
];
|
| 83 |
}
|
| 84 |
return pats;
|
|
@@ -192,6 +192,7 @@ function validateCells(){
|
|
| 192 |
document.addEventListener("input",(e)=>{
|
| 193 |
if(e.target && e.target.closest && e.target.closest("#tbody")){
|
| 194 |
validateCells();
|
|
|
|
| 195 |
}
|
| 196 |
});
|
| 197 |
|
|
@@ -217,16 +218,14 @@ async function exportExcel(){
|
|
| 217 |
const nRows = rows.length + 1; // +1 للهيدر
|
| 218 |
EXPORT_COLUMNS.forEach((colName, colIdx)=>{
|
| 219 |
if(!textCols.has(colName)) return;
|
| 220 |
-
for(let r = 1; r < nRows; r++){
|
| 221 |
const addr = XLSX.utils.encode_cell({ c: colIdx, r });
|
| 222 |
const cell = ws[addr];
|
| 223 |
if(cell){
|
| 224 |
-
cell.t = "s";
|
| 225 |
-
cell.z = "@";
|
| 226 |
if(typeof cell.v !== "string") cell.v = String(cell.v ?? "");
|
| 227 |
if(typeof cell.w !== "string") cell.w = cell.v;
|
| 228 |
}else{
|
| 229 |
-
// إن كانت الخلية فارغة، أنشئ خلية نصية فارغة لضمان التنسيق
|
| 230 |
ws[addr] = { t:"s", v:"", z:"@" };
|
| 231 |
}
|
| 232 |
}
|
|
@@ -244,12 +243,10 @@ async function exportExcel(){
|
|
| 244 |
const blob = new Blob([wbArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
|
| 245 |
const file = new File([blob], filename, { type: blob.type });
|
| 246 |
|
| 247 |
-
// موبايل: مشاركة/حفظ من ورقة المشاركة
|
| 248 |
if (navigator.canShare && navigator.canShare({ files: [file] })) {
|
| 249 |
try { await navigator.share({ files: [file], title: "ملف التذاكر" }); toast("تمت المشاركة/الحفظ."); return; }
|
| 250 |
catch(e){ /* المستخدم أغلق ورقة المشاركة */ }
|
| 251 |
}
|
| 252 |
-
// بديل: تنزيل عادي
|
| 253 |
const url = URL.createObjectURL(blob);
|
| 254 |
const a = document.createElement("a"); a.href = url; a.download = filename;
|
| 255 |
document.body.appendChild(a); a.click(); a.remove();
|
|
@@ -267,13 +264,12 @@ async function copyToClipboardTSV(){
|
|
| 267 |
const body = rows.map(r =>
|
| 268 |
EXPORT_COLUMNS.map(c => {
|
| 269 |
let v = (r[c] ?? "").toString().replace(/\t/g," ");
|
| 270 |
-
// إذا العمود نصي ونظنه رقمي/قد يبدأ بصفر، أضف ' لمنع إزالة الأصفار في Excel
|
| 271 |
if(textCols.has(c) && v && /^[0-9]+$/.test(v)) v = "'" + v;
|
| 272 |
return v;
|
| 273 |
}).join("\t")
|
| 274 |
).join("\r\n");
|
| 275 |
|
| 276 |
-
const tsv = "\uFEFF" + header + "\r\n" + body;
|
| 277 |
|
| 278 |
try{
|
| 279 |
await navigator.clipboard.writeText(tsv);
|
|
@@ -296,9 +292,39 @@ const SAMPLE = `نوع المشكلة : لا استطيع اكمال الاست
|
|
| 296 |
اسم المسح: الخبر
|
| 297 |
المنطقة: الشرقية`;
|
| 298 |
|
| 299 |
-
/*
|
| 300 |
-
|
| 301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
const rawEl = document.getElementById("raw");
|
| 303 |
const tbody = document.getElementById("tbody");
|
| 304 |
if(rawEl) rawEl.value = "";
|
|
@@ -315,14 +341,19 @@ function init(){
|
|
| 315 |
const clearBtn = document.getElementById("btn-clear");
|
| 316 |
const sampleBtn = document.getElementById("btn-sample");
|
| 317 |
const rawEl = document.getElementById("raw");
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
|
| 319 |
parseBtn.addEventListener("click", ()=>{
|
| 320 |
-
const raw = rawEl.value || SAMPLE;
|
| 321 |
const rows = parseTickets(raw);
|
| 322 |
buildTable(rows);
|
| 323 |
validateCells();
|
| 324 |
updateBadge(rows.length);
|
| 325 |
setButtonsEnabled(rows.length>0);
|
|
|
|
| 326 |
toast(`تم استخراج ${rows.length} ${rows.length===1 ? "تذكرة" : "تذاكر"}.`);
|
| 327 |
});
|
| 328 |
|
|
@@ -330,18 +361,18 @@ function init(){
|
|
| 330 |
copyBtn.addEventListener("click", copyToClipboardTSV);
|
| 331 |
|
| 332 |
clearBtn.addEventListener("click", ()=>{
|
| 333 |
-
|
|
|
|
| 334 |
toast("تم مسح كل البيانات.");
|
| 335 |
});
|
| 336 |
|
| 337 |
-
sampleBtn.addEventListener("click", ()=>{ rawEl.value = SAMPLE; });
|
| 338 |
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
});
|
| 345 |
|
| 346 |
// اختصارات لوحة المفاتيح
|
| 347 |
document.addEventListener("keydown", (e)=>{
|
|
@@ -352,6 +383,6 @@ function init(){
|
|
| 352 |
else if(e.key === "Escape"){ e.preventDefault(); clearBtn.click(); }
|
| 353 |
});
|
| 354 |
|
| 355 |
-
setButtonsEnabled(
|
| 356 |
}
|
| 357 |
init();
|
|
|
|
| 78 |
const lbls = labels.map(l => l.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join("|");
|
| 79 |
pats[canonical] = [
|
| 80 |
new RegExp(`(?:^|\\n)\\s*(?:${lbls})\\s*${LABEL_SEP}(.+)$`, "mi"),
|
| 81 |
+
new RegExp(`(?:^|\\n)\\s*(?:${lbls})\\s*${LABEL_SEP}\\n\\s*(.+)`, "mi"),
|
| 82 |
];
|
| 83 |
}
|
| 84 |
return pats;
|
|
|
|
| 192 |
document.addEventListener("input",(e)=>{
|
| 193 |
if(e.target && e.target.closest && e.target.closest("#tbody")){
|
| 194 |
validateCells();
|
| 195 |
+
saveState(); // حفظ لحظي عند تعديل الخلايا
|
| 196 |
}
|
| 197 |
});
|
| 198 |
|
|
|
|
| 218 |
const nRows = rows.length + 1; // +1 للهيدر
|
| 219 |
EXPORT_COLUMNS.forEach((colName, colIdx)=>{
|
| 220 |
if(!textCols.has(colName)) return;
|
| 221 |
+
for(let r = 1; r < nRows; r++){
|
| 222 |
const addr = XLSX.utils.encode_cell({ c: colIdx, r });
|
| 223 |
const cell = ws[addr];
|
| 224 |
if(cell){
|
| 225 |
+
cell.t = "s"; cell.z = "@";
|
|
|
|
| 226 |
if(typeof cell.v !== "string") cell.v = String(cell.v ?? "");
|
| 227 |
if(typeof cell.w !== "string") cell.w = cell.v;
|
| 228 |
}else{
|
|
|
|
| 229 |
ws[addr] = { t:"s", v:"", z:"@" };
|
| 230 |
}
|
| 231 |
}
|
|
|
|
| 243 |
const blob = new Blob([wbArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
|
| 244 |
const file = new File([blob], filename, { type: blob.type });
|
| 245 |
|
|
|
|
| 246 |
if (navigator.canShare && navigator.canShare({ files: [file] })) {
|
| 247 |
try { await navigator.share({ files: [file], title: "ملف التذاكر" }); toast("تمت المشاركة/الحفظ."); return; }
|
| 248 |
catch(e){ /* المستخدم أغلق ورقة المشاركة */ }
|
| 249 |
}
|
|
|
|
| 250 |
const url = URL.createObjectURL(blob);
|
| 251 |
const a = document.createElement("a"); a.href = url; a.download = filename;
|
| 252 |
document.body.appendChild(a); a.click(); a.remove();
|
|
|
|
| 264 |
const body = rows.map(r =>
|
| 265 |
EXPORT_COLUMNS.map(c => {
|
| 266 |
let v = (r[c] ?? "").toString().replace(/\t/g," ");
|
|
|
|
| 267 |
if(textCols.has(c) && v && /^[0-9]+$/.test(v)) v = "'" + v;
|
| 268 |
return v;
|
| 269 |
}).join("\t")
|
| 270 |
).join("\r\n");
|
| 271 |
|
| 272 |
+
const tsv = "\uFEFF" + header + "\r\n" + body;
|
| 273 |
|
| 274 |
try{
|
| 275 |
await navigator.clipboard.writeText(tsv);
|
|
|
|
| 292 |
اسم المسح: الخبر
|
| 293 |
المنطقة: الشرقية`;
|
| 294 |
|
| 295 |
+
/* ================= حفظ واسترجاع الحالة ================= */
|
| 296 |
+
const STATE_KEY = "ticketParserState_v1";
|
| 297 |
+
function saveState(){
|
| 298 |
+
try{
|
| 299 |
+
const raw = document.getElementById("raw")?.value || "";
|
| 300 |
+
const fname = document.getElementById("fname")?.value || "Ticket";
|
| 301 |
+
const rows = readTable();
|
| 302 |
+
const state = { raw, fname, rows };
|
| 303 |
+
localStorage.setItem(STATE_KEY, JSON.stringify(state));
|
| 304 |
+
}catch{}
|
| 305 |
+
}
|
| 306 |
+
function loadState(){
|
| 307 |
+
try{
|
| 308 |
+
const s = localStorage.getItem(STATE_KEY);
|
| 309 |
+
if(!s) return false;
|
| 310 |
+
const { raw, fname, rows } = JSON.parse(s);
|
| 311 |
+
if(typeof raw === "string"){ const el=document.getElementById("raw"); if(el) el.value = raw; }
|
| 312 |
+
if(typeof fname === "string"){ const el=document.getElementById("fname"); if(el) el.value = fname; }
|
| 313 |
+
if(Array.isArray(rows) && rows.length){
|
| 314 |
+
buildTable(rows);
|
| 315 |
+
validateCells();
|
| 316 |
+
updateBadge(rows.length);
|
| 317 |
+
setButtonsEnabled(true);
|
| 318 |
+
}
|
| 319 |
+
return true;
|
| 320 |
+
}catch{ return false; }
|
| 321 |
+
}
|
| 322 |
+
function clearState(){
|
| 323 |
+
try{ localStorage.removeItem(STATE_KEY); }catch{}
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
/* تنظيف واجهة فقط (يُستدعى من زر مسح) */
|
| 327 |
+
function wipeUI(){
|
| 328 |
const rawEl = document.getElementById("raw");
|
| 329 |
const tbody = document.getElementById("tbody");
|
| 330 |
if(rawEl) rawEl.value = "";
|
|
|
|
| 341 |
const clearBtn = document.getElementById("btn-clear");
|
| 342 |
const sampleBtn = document.getElementById("btn-sample");
|
| 343 |
const rawEl = document.getElementById("raw");
|
| 344 |
+
const fnameEl = document.getElementById("fname");
|
| 345 |
+
|
| 346 |
+
// استرجاع الحالة المخزنة (لا نمسح شيئًا عند الدخول)
|
| 347 |
+
loadState();
|
| 348 |
|
| 349 |
parseBtn.addEventListener("click", ()=>{
|
| 350 |
+
const raw = (rawEl.value || SAMPLE);
|
| 351 |
const rows = parseTickets(raw);
|
| 352 |
buildTable(rows);
|
| 353 |
validateCells();
|
| 354 |
updateBadge(rows.length);
|
| 355 |
setButtonsEnabled(rows.length>0);
|
| 356 |
+
saveState(); // نحفظ فور التحليل
|
| 357 |
toast(`تم استخراج ${rows.length} ${rows.length===1 ? "تذكرة" : "تذاكر"}.`);
|
| 358 |
});
|
| 359 |
|
|
|
|
| 361 |
copyBtn.addEventListener("click", copyToClipboardTSV);
|
| 362 |
|
| 363 |
clearBtn.addEventListener("click", ()=>{
|
| 364 |
+
clearState(); // امسح التخزين
|
| 365 |
+
wipeUI(); // نظّف الواجهة
|
| 366 |
toast("تم مسح كل البيانات.");
|
| 367 |
});
|
| 368 |
|
| 369 |
+
sampleBtn.addEventListener("click", ()=>{ rawEl.value = SAMPLE; saveState(); });
|
| 370 |
|
| 371 |
+
rawEl.addEventListener("input", saveState);
|
| 372 |
+
fnameEl.addEventListener("input", saveState);
|
| 373 |
+
|
| 374 |
+
// لا نضيف أي مسح عند الخروج/الإغلاق — تلبية لطلبك
|
| 375 |
+
// (أزلنا pagehide / beforeunload / visibilitychange)
|
|
|
|
| 376 |
|
| 377 |
// اختصارات لوحة المفاتيح
|
| 378 |
document.addEventListener("keydown", (e)=>{
|
|
|
|
| 383 |
else if(e.key === "Escape"){ e.preventDefault(); clearBtn.click(); }
|
| 384 |
});
|
| 385 |
|
| 386 |
+
setButtonsEnabled(!!document.getElementById("tbody")?.children.length);
|
| 387 |
}
|
| 388 |
init();
|