stat2025 commited on
Commit
4ad53b0
·
verified ·
1 Parent(s): 4a05b37

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +63 -16
app.js CHANGED
@@ -39,16 +39,30 @@ function normalizeTime(val){
39
  }
40
  function normalizeDate(v){
41
  v=(v||"").trim();
 
42
  let m=v.match(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2,4})/);
43
  if(m){
44
  let d=+m[1], mo=+m[2], y=+m[3]; if(y<100) y+=2000;
45
  return `${y.toString().padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).padStart(2,"0")}`;
46
  }
 
47
  m=v.match(/(\d{4})[\/\-](\d{1,2})[\/\-](\d{1,2})/);
48
  if(m){
49
  let y=+m[1], mo=+m[2], d=+m[3];
50
  return `${y.toString().padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).padStart(2,"0")}`;
51
  }
 
 
 
 
 
 
 
 
 
 
 
 
52
  return v;
53
  }
54
  function splitTickets(raw){
@@ -64,7 +78,7 @@ function compileFieldPatterns(){
64
  const lbls = labels.map(l => l.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join("|");
65
  pats[canonical] = [
66
  new RegExp(`(?:^|\\n)\\s*(?:${lbls})\\s*${LABEL_SEP}(.+)$`, "mi"),
67
- new RegExp(`(?:^|\\n)\\s*(?:${lbls})\\s*${LABEL_SEP}\\n\\s*(.+)`, "mi"),
68
  ];
69
  }
70
  return pats;
@@ -189,7 +203,7 @@ function toast(msg){
189
  setTimeout(()=>{ t.hidden = true; }, 2000);
190
  }
191
 
192
- /* تصدير/مشاركة Excel: Web Share أولاً ثم تنزيل كبديل */
193
  async function exportExcel(){
194
  const rows = readTable();
195
  if(!rows.length){ toast("لا يوجد بيانات لتصديرها."); return; }
@@ -197,6 +211,27 @@ async function exportExcel(){
197
  const aoa = [EXPORT_COLUMNS, ...rows.map(r=>EXPORT_COLUMNS.map(c=>r[c]||""))];
198
  const ws = XLSX.utils.aoa_to_sheet(aoa);
199
  ws["!rtl"] = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  const wb = XLSX.utils.book_new();
201
  XLSX.utils.book_append_sheet(wb, ws, "التذاكر");
202
 
@@ -209,10 +244,12 @@ async function exportExcel(){
209
  const blob = new Blob([wbArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
210
  const file = new File([blob], filename, { type: blob.type });
211
 
 
212
  if (navigator.canShare && navigator.canShare({ files: [file] })) {
213
  try { await navigator.share({ files: [file], title: "ملف التذاكر" }); toast("تمت المشاركة/الحفظ."); return; }
214
  catch(e){ /* المستخدم أغلق ورقة المشاركة */ }
215
  }
 
216
  const url = URL.createObjectURL(blob);
217
  const a = document.createElement("a"); a.href = url; a.download = filename;
218
  document.body.appendChild(a); a.click(); a.remove();
@@ -220,18 +257,32 @@ async function exportExcel(){
220
  toast("تم تنزيل الملف.");
221
  }
222
 
223
- /* نسخ الجدول كـTSV للصق مباشر في Excel */
224
  async function copyToClipboardTSV(){
225
  const rows = readTable();
226
  if(!rows.length){ toast("لا يوجد بيانات لنسخها."); return; }
 
 
227
  const header = EXPORT_COLUMNS.join("\t");
228
- const body = rows.map(r=>EXPORT_COLUMNS.map(c=>(r[c]||"").replace(/\t/g," ")).join("\t")).join("\n");
229
- const tsv = `${header}\n${body}`;
230
- try{ await navigator.clipboard.writeText(tsv); toast("تم النسخ — الصق/ي في Excel."); }
231
- catch(e){
232
- const ta = document.createElement("textarea"); ta.value = tsv; document.body.appendChild(ta);
 
 
 
 
 
 
 
 
 
 
 
 
233
  ta.select(); document.execCommand("copy"); document.body.removeChild(ta);
234
- toast("تم النسخ — الصق/ي في Excel.");
235
  }
236
  }
237
 
@@ -245,13 +296,9 @@ const SAMPLE = `نوع المشكلة : لا استطيع اكمال الاست
245
  اسم المسح: الخبر
246
  المنطقة: الشرقية`;
247
 
248
- /* تنظيف كامل للبيانات (للسرعة والخصوصية) */
249
  function wipeAll(){
250
- try{
251
- // لا نخزّن شيئًا؛ احتياطًا نمسح أي أثر جلسة
252
- sessionStorage.clear();
253
- localStorage.removeItem("rawTickets");
254
- }catch{}
255
  const rawEl = document.getElementById("raw");
256
  const tbody = document.getElementById("tbody");
257
  if(rawEl) rawEl.value = "";
@@ -289,7 +336,7 @@ function init(){
289
 
290
  sampleBtn.addEventListener("click", ()=>{ rawEl.value = SAMPLE; });
291
 
292
- // عند الخروج/إغلاق الصفحة: امسح كل شيء
293
  window.addEventListener("pagehide", wipeAll, { once:true });
294
  window.addEventListener("beforeunload", wipeAll, { once:true });
295
  document.addEventListener("visibilitychange", ()=>{
 
39
  }
40
  function normalizeDate(v){
41
  v=(v||"").trim();
42
+ // 1) 21/8/2025 أو 21-08-25
43
  let m=v.match(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2,4})/);
44
  if(m){
45
  let d=+m[1], mo=+m[2], y=+m[3]; if(y<100) y+=2000;
46
  return `${y.toString().padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).padStart(2,"0")}`;
47
  }
48
+ // 2) 2025/08/21
49
  m=v.match(/(\d{4})[\/\-](\d{1,2})[\/\-](\d{1,2})/);
50
  if(m){
51
  let y=+m[1], mo=+m[2], d=+m[3];
52
  return `${y.toString().padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).padStart(2,"0")}`;
53
  }
54
+ // 3) 21 أغسطس 2025 / 21 آب 2025
55
+ const months = {
56
+ "يناير":1,"فبراير":2,"مارس":3,"أبريل":4,"ابريل":4,"مايو":5,"يونيو":6,"يوليو":7,"أغسطس":8,"اغسطس":8,"سبتمبر":9,"أكتوبر":10,"اكتوبر":10,"نوفمبر":11,"ديسمبر":12,
57
+ "كانون الثاني":1,"شباط":2,"آذار":3,"نيسان":4,"أيار":5,"حزيران":6,"تموز":7,"آب":8,"أيلول":9,"تشرين الأول":10,"تشرين الثاني":11,"كانون الأول":12
58
+ };
59
+ const rx = new RegExp(`(\\d{1,2})\\s+(${Object.keys(months).join("|")})\\s+(\\d{2,4})`,"i");
60
+ m = v.match(rx);
61
+ if(m){
62
+ const d = +m[1]; const mo = months[m[2].toLowerCase()] || months[m[2]];
63
+ let y = +m[3]; if(y<100) y+=2000;
64
+ if(mo) return `${y.toString().padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).padStart(2,"0")}`;
65
+ }
66
  return v;
67
  }
68
  function splitTickets(raw){
 
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\\س*(.+)`, "mi"),
82
  ];
83
  }
84
  return pats;
 
203
  setTimeout(()=>{ t.hidden = true; }, 2000);
204
  }
205
 
206
+ /* ======== تصدير/مشاركة Excel مع حفظ الأصفار كنص (هوية/جهاز/جوال) ======== */
207
  async function exportExcel(){
208
  const rows = readTable();
209
  if(!rows.length){ toast("لا يوجد بيانات لتصديرها."); return; }
 
211
  const aoa = [EXPORT_COLUMNS, ...rows.map(r=>EXPORT_COLUMNS.map(c=>r[c]||""))];
212
  const ws = XLSX.utils.aoa_to_sheet(aoa);
213
  ws["!rtl"] = true;
214
+
215
+ // أعمدة نصية للحفاظ على الأصفار
216
+ const textCols = new Set(["رقم الهوية","رقم الجهاز","رقم الجوال"]);
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++){ // ابدأ من 1 (تجاهل الهيدر في 0)
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
+ }
233
+ });
234
+
235
  const wb = XLSX.utils.book_new();
236
  XLSX.utils.book_append_sheet(wb, ws, "التذاكر");
237
 
 
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();
 
257
  toast("تم تنزيل الملف.");
258
  }
259
 
260
+ /* ======== نسخ إلى الحافظة (TSV) مع BOM وحفظ الأصفار قدر الإمكان ======== */
261
  async function copyToClipboardTSV(){
262
  const rows = readTable();
263
  if(!rows.length){ toast("لا يوجد بيانات لنسخها."); return; }
264
+
265
+ const textCols = new Set(["رقم الهوية","رقم الجهاز","رقم الجوال"]);
266
  const header = EXPORT_COLUMNS.join("\t");
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; // BOM + CRLF
277
+
278
+ try{
279
+ await navigator.clipboard.writeText(tsv);
280
+ toast("تم النسخ — الصق/ي مباشرة في Excel.");
281
+ }catch(e){
282
+ const ta = document.createElement("textarea");
283
+ ta.value = tsv; document.body.appendChild(ta);
284
  ta.select(); document.execCommand("copy"); document.body.removeChild(ta);
285
+ toast("تم النسخ — الصق/ي مباشرة في Excel.");
286
  }
287
  }
288
 
 
296
  اسم المسح: الخبر
297
  المنطقة: الشرقية`;
298
 
299
+ /* تنظيف كامل عند المسح أو الخروج لسرعة الصفحة */
300
  function wipeAll(){
301
+ try{ sessionStorage.clear(); localStorage.removeItem("rawTickets"); }catch{}
 
 
 
 
302
  const rawEl = document.getElementById("raw");
303
  const tbody = document.getElementById("tbody");
304
  if(rawEl) rawEl.value = "";
 
336
 
337
  sampleBtn.addEventListener("click", ()=>{ rawEl.value = SAMPLE; });
338
 
339
+ // عند الخروج/إغلاق الصفحة
340
  window.addEventListener("pagehide", wipeAll, { once:true });
341
  window.addEventListener("beforeunload", wipeAll, { once:true });
342
  document.addEventListener("visibilitychange", ()=>{