Spaces:
Running
Running
Update app.js
Browse files
app.js
CHANGED
|
@@ -64,7 +64,7 @@ const LABEL_FIXES = [
|
|
| 64 |
[/(^|\n)\s*اسم\s*المنطقة/gi, "$1المنطقة"],
|
| 65 |
[/(^|\n)\s*اسم\s*المسح/gi, "$1المسح"],
|
| 66 |
[/(^|\n)\s*الهاتف/gi, "$1رقم الجوال"],
|
| 67 |
-
[/(^|\n)
|
| 68 |
];
|
| 69 |
function fixLabels(s){
|
| 70 |
let t = s;
|
|
@@ -72,6 +72,7 @@ function fixLabels(s){
|
|
| 72 |
return t;
|
| 73 |
}
|
| 74 |
|
|
|
|
| 75 |
const H_MONTHS = ["محرم","صفر","ربيع الأول","ربيع الاول","ربيع الآخر","ربيع الاخر","جمادى الأولى","جمادى الاولى","جمادى الآخرة","جمادى الاخرة","رجب","شعبان","رمضان","شوال","ذو القعدة","ذو القعده","ذو الحجة","ذو الحجه"];
|
| 76 |
function monthIndexHijri(name){
|
| 77 |
const i = H_MONTHS.findIndex(m => new RegExp("^"+m+"$", "i").test(name.trim()));
|
|
@@ -101,31 +102,41 @@ function detectHijriDate(str){
|
|
| 101 |
if(m) return {hy:+m[3], hm:+m[2], hd:+m[1]};
|
| 102 |
return null;
|
| 103 |
}
|
|
|
|
|
|
|
| 104 |
function normalizeDateOnly(raw){
|
| 105 |
const t = normalizeText(raw);
|
| 106 |
const hj = detectHijriDate(t);
|
| 107 |
-
|
| 108 |
-
const
|
| 109 |
-
const timeOnly1 = /(^|\s)\d{1,2}\s*(?:[:٫\.:\-]\d{2})\s*(?:ص|صباح(?:اً|ا)?|am|م|مساء|pm)?($|\s)/i.test(t);
|
| 110 |
const timeOnly2 = /(^|\s)\d{1,2}\s*(?:ص|صباح(?:اً|ا)?|am|م|مساء|pm)($|\s)/i.test(t);
|
| 111 |
-
if((timeOnly1||timeOnly2) && !
|
|
|
|
| 112 |
if(hj){
|
| 113 |
const [gy,gm,gd] = hijriToGregorian(hj.hy, hj.hm, hj.hd);
|
| 114 |
-
return `${gy
|
| 115 |
}
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
let d=+m[1], mo=+m[2], y=+m[3];
|
| 119 |
-
if(y<100) y+=2000;
|
| 120 |
-
return `${y.toString().padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).toString().padStart(2,"0")}`;
|
| 121 |
-
}
|
| 122 |
-
m = t.match(/(\d{4})[\/\-](\d{1,2})[\/\-](\d{1,2})/);
|
| 123 |
if(m){
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
}
|
| 127 |
return t;
|
| 128 |
}
|
|
|
|
| 129 |
|
| 130 |
function findStartsByLabels(text, labels){
|
| 131 |
const lblRe = labels.map(l=>l.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join("|");
|
|
@@ -151,7 +162,7 @@ function findBlockAfterLabel(text, labels, allLabels = START_LABELS){
|
|
| 151 |
const lblAlt = labels.map(esc).join("|");
|
| 152 |
const allAlt = allLabels.map(esc).join("|");
|
| 153 |
const re = new RegExp(
|
| 154 |
-
`(?:^|\\n)\\s*(?:${lblAlt})\\s*(?::|:|\\s)\\s*([
|
| 155 |
"i"
|
| 156 |
);
|
| 157 |
const m = hay.match(re);
|
|
@@ -188,7 +199,9 @@ function extractFields(ticketText){
|
|
| 188 |
"رقم الهوية":"", "رقم الجهاز":"", "رقم الجوال":"", "المسح":"", "المنطقة":""
|
| 189 |
};
|
| 190 |
|
|
|
|
| 191 |
let v = findBlockAfterLabel(text, FIELD_ALIASES["نوع المشكلة"], START_LABELS);
|
|
|
|
| 192 |
if(v) out["نوع المشكلة"] = normalizeText(v);
|
| 193 |
|
| 194 |
v = findAfterLabel(text, FIELD_ALIASES["وقت حدوث المشكلة"]);
|
|
@@ -375,7 +388,7 @@ function toast(msg){
|
|
| 375 |
setTimeout(()=>{ t.hidden = true; }, 2000);
|
| 376 |
}
|
| 377 |
|
| 378 |
-
/*
|
| 379 |
async function exportExcel(){
|
| 380 |
const rows = readTable();
|
| 381 |
if(!rows.length){ toast("لا يوجد بيانات لتصديرها."); return; }
|
|
@@ -457,7 +470,6 @@ async function exportExcel(){
|
|
| 457 |
setTimeout(()=>URL.revokeObjectURL(url),1000);
|
| 458 |
toast("تم تنزيل الملف بتنسيق القالب.");
|
| 459 |
}
|
| 460 |
-
/* === نهاية التعديل === */
|
| 461 |
|
| 462 |
async function copyToClipboardTSV(){
|
| 463 |
const rows = readTable();
|
|
|
|
| 64 |
[/(^|\n)\s*اسم\s*المنطقة/gi, "$1المنطقة"],
|
| 65 |
[/(^|\n)\s*اسم\s*المسح/gi, "$1المسح"],
|
| 66 |
[/(^|\n)\s*الهاتف/gi, "$1رقم الجوال"],
|
| 67 |
+
[/(^|\n)\s*جوال/gi, "$1رقم الجوال"]
|
| 68 |
];
|
| 69 |
function fixLabels(s){
|
| 70 |
let t = s;
|
|
|
|
| 72 |
return t;
|
| 73 |
}
|
| 74 |
|
| 75 |
+
/* ==== تحويل/التقاط التاريخ ==== */
|
| 76 |
const H_MONTHS = ["محرم","صفر","ربيع الأول","ربيع الاول","ربيع الآخر","ربيع الاخر","جمادى الأولى","جمادى الاولى","جمادى الآخرة","جمادى الاخرة","رجب","شعبان","رمضان","شوال","ذو القعدة","ذو القعده","ذو الحجة","ذو الحجه"];
|
| 77 |
function monthIndexHijri(name){
|
| 78 |
const i = H_MONTHS.findIndex(m => new RegExp("^"+m+"$", "i").test(name.trim()));
|
|
|
|
| 102 |
if(m) return {hy:+m[3], hm:+m[2], hd:+m[1]};
|
| 103 |
return null;
|
| 104 |
}
|
| 105 |
+
|
| 106 |
+
/* ✅ تُحافظ على YYYY/MM/DD وتدعم DD/MM/YYYY وتترك “الوقت فقط” فارغ */
|
| 107 |
function normalizeDateOnly(raw){
|
| 108 |
const t = normalizeText(raw);
|
| 109 |
const hj = detectHijriDate(t);
|
| 110 |
+
|
| 111 |
+
const timeOnly1 = /(^|\s)\d{1,2}\s*(?:[:٫\.\-]\d{2})\s*(?:ص|صباح(?:اً|ا)?|am|م|مساء|pm)?($|\s)/i.test(t);
|
|
|
|
| 112 |
const timeOnly2 = /(^|\s)\d{1,2}\s*(?:ص|صباح(?:اً|ا)?|am|م|مساء|pm)($|\s)/i.test(t);
|
| 113 |
+
if((timeOnly1||timeOnly2) && !hj && !/(\d{3,4}).(\d{1,2}).(\d{1,2})/.test(t)) return "";
|
| 114 |
+
|
| 115 |
if(hj){
|
| 116 |
const [gy,gm,gd] = hijriToGregorian(hj.hy, hj.hm, hj.hd);
|
| 117 |
+
return `${String(gy).padStart(4,"0")}-${String(gm).padStart(2,"0")}-${String(gd).padStart(2,"0")}`;
|
| 118 |
}
|
| 119 |
+
|
| 120 |
+
const m = t.match(/(\d{1,4})[\/\-](\d{1,2})[\/\-](\d{1,4})/);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
if(m){
|
| 122 |
+
const a=m[1], b=m[2], c=m[3];
|
| 123 |
+
let A=+a, B=+b, C=+c;
|
| 124 |
+
let y, mo, d;
|
| 125 |
+
|
| 126 |
+
if(a.length===4){ y=A; mo=B; d=C; } // YYYY/MM/DD
|
| 127 |
+
else if(c.length===4){ y=C; mo=B; d=A; } // DD/MM/YYYY
|
| 128 |
+
else {
|
| 129 |
+
if(A>31){ y=A; mo=B; d=C; } // YY/MM/DD أو YYYY/MM/DD مختصر
|
| 130 |
+
else if(C>31){ y=C; mo=B; d=A; } // DD/MM/YY
|
| 131 |
+
else { y=C; mo=B; d=A; } // افتراضيًا DD/MM/YY
|
| 132 |
+
}
|
| 133 |
+
if(y<100) y += 2000;
|
| 134 |
+
if(mo>12 && d<=12){ const tmp=mo; mo=d; d=tmp; } // إصلاح لو حدث قلب
|
| 135 |
+
return `${String(y).padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).padStart(2,"0")}`;
|
| 136 |
}
|
| 137 |
return t;
|
| 138 |
}
|
| 139 |
+
/* ==== نهاية التاريخ ==== */
|
| 140 |
|
| 141 |
function findStartsByLabels(text, labels){
|
| 142 |
const lblRe = labels.map(l=>l.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join("|");
|
|
|
|
| 162 |
const lblAlt = labels.map(esc).join("|");
|
| 163 |
const allAlt = allLabels.map(esc).join("|");
|
| 164 |
const re = new RegExp(
|
| 165 |
+
`(?:^|\\n)\\s*(?:${lblAlt})\\s*(?::|:|\\s)\\s*([\\s\\S]*?)(?=\\n\\s*(?:${allAlt})\\s*(?::|:|\\s)|$)`,
|
| 166 |
"i"
|
| 167 |
);
|
| 168 |
const m = hay.match(re);
|
|
|
|
| 199 |
"رقم الهوية":"", "رقم الجهاز":"", "رقم الجوال":"", "المسح":"", "المنطقة":""
|
| 200 |
};
|
| 201 |
|
| 202 |
+
// ✅ أولاً نحاول كتلة متعددة الأسطر، وإن فشلت نأخذ السطر الأول بعد الوسم
|
| 203 |
let v = findBlockAfterLabel(text, FIELD_ALIASES["نوع المشكلة"], START_LABELS);
|
| 204 |
+
if(!v) v = findAfterLabel(text, FIELD_ALIASES["نوع المشكلة"]);
|
| 205 |
if(v) out["نوع المشكلة"] = normalizeText(v);
|
| 206 |
|
| 207 |
v = findAfterLabel(text, FIELD_ALIASES["وقت حدوث المشكلة"]);
|
|
|
|
| 388 |
setTimeout(()=>{ t.hidden = true; }, 2000);
|
| 389 |
}
|
| 390 |
|
| 391 |
+
/* التصدير — يستخدم نفس تاريخ "وقت حدوث المشكلة" */
|
| 392 |
async function exportExcel(){
|
| 393 |
const rows = readTable();
|
| 394 |
if(!rows.length){ toast("لا يوجد بيانات لتصديرها."); return; }
|
|
|
|
| 470 |
setTimeout(()=>URL.revokeObjectURL(url),1000);
|
| 471 |
toast("تم تنزيل الملف بتنسيق القالب.");
|
| 472 |
}
|
|
|
|
| 473 |
|
| 474 |
async function copyToClipboardTSV(){
|
| 475 |
const rows = readTable();
|