stat2025 commited on
Commit
929eb64
·
verified ·
1 Parent(s): 1fb8dea

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +75 -86
app.js CHANGED
@@ -1,9 +1,11 @@
1
  /* ========= منطق التحليل والتصدير والنسخ (Static فقط) ========= */
2
- /* تمت إضافة عمود "التصنيف" + قواعد تصنيف بالكلمات المفتاحية */
3
  const EXPORT_COLUMNS = [
4
- "التصنيف", // جديد: أول عمود
5
  "نوع المشكلة","وقت حدوث المشكلة","اسم صاحب المشكلة",
6
- "رقم الهوية","رقم الجهاز","رقم الجوال","المسح","المنطقة"
 
 
7
  ];
8
 
9
  const FIELD_ALIASES = {
@@ -21,65 +23,26 @@ const LABEL_SEP = "(?::|:)?\\s*";
21
  const TICKET_SEP = /\n\s*(?:\n|—+|-{3,}|={3,}|🔴+)+\s*\n/;
22
  const arabicDigitsMap = {"٠":"0","١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9"};
23
 
24
- /* ===== قواعد التصنيف بالكلمات المفتاحية =====
25
- نبحث في نص التذكرة و"نوع المشكلة" ونرجّع أول تصنيف مطابق بالترتيب */
26
  const CLASS_RULES = {
27
- "استفسار": [
28
- "استفسار","سؤال","استعلام","معلومة","استفسارات"
29
- ],
30
- "إضافة أجهزة": [
31
- "اضافة جهاز","إضافة أجهزة","اضافة اجهزة","تركيب جهاز","جهاز جديد","تسجيل جهاز","ربط جهاز","اضافة ماسح","إضافة ماسح"
32
- ],
33
- "الاستمارة": [
34
- "الاستمارة","استمارة","النموذج","نموذج","الفورم","تعليق الاستمارة","لا استطيع اكمال الاستمارة","التعبئة"
35
- ],
36
- "التقييم": [
37
- "التقييم","تقييم","feedback","survey","رضا","نجوم"
38
- ],
39
- "الخرائط": [
40
- "الخرائط","خرائط","map","gps","تحديد الموقع","احداثيات","إحداثيات","الموقع الجغرافي"
41
- ],
42
- "السوتي": [
43
- "السوتي","سوتي","soti","soti assist","mobicontrol","soti mobicontrol"
44
- ],
45
- "الشبكة": [
46
- "الشبكة","شبكة","نت","انترنت","إنترنت","wifi","واي فاي","4g","5g","ضعف الشبكة","stc","mobily","زين","weak signal","no signal"
47
- ],
48
- "النسخة": [
49
- "النسخة","نسخة","الإصدار","اصدار","version","build","release","تحديث نسخة","ترقية النسخة"
50
- ],
51
- "النظام المكتبي": [
52
- "النظام المكتبي","نسخة ويندوز","ويندوز","windows","pc app","برنامج المكتب","التطبيق على الكمبيوتر","الديسكتوب"
53
- ],
54
- "تسجيل دخول": [
55
- "تسجيل دخول","تسجيل الدخول","login","signin","رفض تسجيل الدخول","لا يقبل الدخول","اسم المستخدم","كلمة المرور","نسيت كلمة السر","إعادة تعيين"
56
- ],
57
- "تفعيل حساب": [
58
- "تفعيل حساب","تفعيل","activation","activate","رمز التفعيل","كود التفعيل"
59
- ],
60
- "تناقل البيانات": [
61
- "تناقل البيانات","ترحيل البيانات","مزامنة","sync","مزامنه","نقل البيانات","رفع البيانات","sync failed","المزامنة"
62
- ],
63
- "صيانة وتحديث الأجهزة": [
64
- "صيانة","تحديث الأجهزة","تحديث جهاز","ترقية الجهاز","اعطال الجهاز","تصليح","صيانة وتحديث الأجهزة","صيانة الجهاز"
65
- ],
66
  };
67
-
68
- /* ترتيب الأفضلية عند تعدد التطابقات */
69
  const CLASS_PRIORITY = [
70
- "صيانة وتحديث الأجهزة",
71
- "إضافة أجهزة",
72
- "تسجيل دخول",
73
- "تفعيل حساب",
74
- "الاستمارة",
75
- "التقييم",
76
- "الخرائط",
77
- "السوتي",
78
- "الشبكة",
79
- "النسخة",
80
- "النظام المكتبي",
81
- "تناقل البيانات",
82
- "استفسار",
83
  ];
84
 
85
  /* ================= أساسيات النص ================= */
@@ -191,7 +154,6 @@ function extractFields(ticketText){
191
  /* ================= التصنيف ================= */
192
  function classifyTicket(text, fields){
193
  const hay = normalizeText(`${text}\n${fields?.["نوع المشكلة"]||""}`).toLowerCase();
194
- // نجرب حسب ترتيب الأفضلية
195
  for(const label of CLASS_PRIORITY){
196
  const kws = CLASS_RULES[label] || [];
197
  for(const kw of kws){
@@ -200,15 +162,31 @@ function classifyTicket(text, fields){
200
  if(needle && hay.includes(needle)) return label;
201
  }
202
  }
203
- return "استفسار"; // افتراضي لطيف إذا لم يُعرف
204
  }
205
 
206
- /* نبني الصفوف مع "التصنيف" */
207
- function parseTickets(raw){
208
  return splitTickets(raw||"").map(t => {
209
  const f = extractFields(t);
210
  const cls = classifyTicket(t, f);
211
- return { "التصنيف": cls, ...f };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  });
213
  }
214
 
@@ -276,7 +254,7 @@ function validateCells(){
276
  document.addEventListener("input",(e)=>{
277
  if(e.target && e.target.closest && e.target.closest("#tbody")){
278
  validateCells();
279
- saveState(); // حفظ لحظي عند تعديل الخلايا
280
  }
281
  });
282
 
@@ -297,9 +275,8 @@ async function exportExcel(){
297
  const ws = XLSX.utils.aoa_to_sheet(aoa);
298
  ws["!rtl"] = true;
299
 
300
- // أعمدة نصية للحفاظ على الأصفار
301
  const textCols = new Set(["رقم الهوية","رقم الجهاز","رقم الجوال"]);
302
- const nRows = rows.length + 1; // +1 للهيدر
303
  EXPORT_COLUMNS.forEach((colName, colIdx)=>{
304
  if(!textCols.has(colName)) return;
305
  for(let r = 1; r < nRows; r++){
@@ -329,7 +306,7 @@ async function exportExcel(){
329
 
330
  if (navigator.canShare && navigator.canShare({ files: [file] })) {
331
  try { await navigator.share({ files: [file], title: "ملف التذاكر" }); toast("تمت المشاركة/الحفظ."); return; }
332
- catch(e){ /* المستخدم أغلق ورقة المش��ركة */ }
333
  }
334
  const url = URL.createObjectURL(blob);
335
  const a = document.createElement("a"); a.href = url; a.download = filename;
@@ -354,7 +331,6 @@ async function copyToClipboardTSV(){
354
  ).join("\r\n");
355
 
356
  const tsv = "\uFEFF" + header + "\r\n" + body;
357
-
358
  try{
359
  await navigator.clipboard.writeText(tsv);
360
  toast("تم النسخ — الصق/ي مباشرة في Excel.");
@@ -377,25 +353,29 @@ const SAMPLE = `نوع المشكلة : لا استطيع اكمال الاست
377
  المنطقة: الشرقية`;
378
 
379
  /* ================= حفظ واسترجاع الحالة ================= */
380
- const STATE_KEY = "ticketParserState_v2"; // رفعنا النسخة لأننا أضفنا "التصنيف"
381
- function ensureClassification(rows){
382
  if(!Array.isArray(rows)) return rows||[];
383
  return rows.map(r=>{
384
- if(!r) return r;
385
- if(!("التصنيف" in r) || !r["التصنيف"]){
386
- const fakeText = Object.values(r).join("\n");
387
- const cls = classifyTicket(fakeText, r);
388
- return { "التصنيف": cls, ...r };
389
  }
390
- return r;
 
 
 
391
  });
392
  }
393
  function saveState(){
394
  try{
395
- const raw = document.getElementById("raw")?.value || "";
396
  const fname = document.getElementById("fname")?.value || "Ticket";
397
- const rows = readTable();
398
- const state = { raw, fname, rows };
 
 
399
  localStorage.setItem(STATE_KEY, JSON.stringify(state));
400
  }catch{}
401
  }
@@ -403,10 +383,12 @@ function loadState(){
403
  try{
404
  const s = localStorage.getItem(STATE_KEY);
405
  if(!s) return false;
406
- let { raw, fname, rows } = JSON.parse(s);
407
  if(typeof raw === "string"){ const el=document.getElementById("raw"); if(el) el.value = raw; }
408
  if(typeof fname === "string"){ const el=document.getElementById("fname"); if(el) el.value = fname; }
409
- rows = ensureClassification(rows);
 
 
410
  if(Array.isArray(rows) && rows.length){
411
  buildTable(rows);
412
  validateCells();
@@ -439,18 +421,22 @@ function init(){
439
  const sampleBtn = document.getElementById("btn-sample");
440
  const rawEl = document.getElementById("raw");
441
  const fnameEl = document.getElementById("fname");
 
 
442
 
443
  // استرجاع الحالة المخزنة
444
  loadState();
445
 
446
  parseBtn.addEventListener("click", ()=>{
447
  const raw = (rawEl.value || SAMPLE);
448
- const rows = parseTickets(raw);
 
 
449
  buildTable(rows);
450
  validateCells();
451
  updateBadge(rows.length);
452
  setButtonsEnabled(rows.length>0);
453
- saveState(); // نحفظ فور التحليل
454
  toast(`تم استخراج ${rows.length} ${rows.length===1 ? "تذكرة" : "تذاكر"}.`);
455
  });
456
 
@@ -458,8 +444,9 @@ function init(){
458
  copyBtn.addEventListener("click", copyToClipboardTSV);
459
 
460
  clearBtn.addEventListener("click", ()=>{
461
- clearState(); // امسح التخزين
462
- wipeUI(); // نظّف الواجهة
 
463
  toast("تم مسح كل البيانات.");
464
  });
465
 
@@ -467,6 +454,8 @@ function init(){
467
 
468
  rawEl.addEventListener("input", saveState);
469
  fnameEl.addEventListener("input", saveState);
 
 
470
 
471
  // اختصارات لوحة المفاتيح
472
  document.addEventListener("keydown", (e)=>{
 
1
  /* ========= منطق التحليل والتصدير والنسخ (Static فقط) ========= */
2
+ /* تمت إضافة: عمود "اسم الدعم الفني" وعمود "الحالة" (تم الحل) وتعبئة منطقة افتراضية عند الفراغ */
3
  const EXPORT_COLUMNS = [
4
+ "التصنيف", // ثابت من نسختك السابقة
5
  "نوع المشكلة","وقت حدوث المشكلة","اسم صاحب المشكلة",
6
+ "رقم الهوية","رقم الجهاز","رقم الجوال","المسح","المنطقة",
7
+ "اسم الدعم الفني", // جديد
8
+ "الحالة" // جديد (في النهاية)
9
  ];
10
 
11
  const FIELD_ALIASES = {
 
23
  const TICKET_SEP = /\n\s*(?:\n|—+|-{3,}|={3,}|🔴+)+\s*\n/;
24
  const arabicDigitsMap = {"٠":"0","١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9"};
25
 
26
+ /* ===== قواعد التصنيف (كما تم سابقًا) ===== */
 
27
  const CLASS_RULES = {
28
+ "استفسار": ["استفسار","سؤال","استعلام","معلومة","استفسارات"],
29
+ "إضافة أجهزة": ["اضافة جهاز","إضافة أجهزة","اضافة اجهزة","تركيب جهاز","جهاز جديد","تسجيل جهاز","ربط جهاز","اضافة ماسح","إضافة ماسح"],
30
+ "الاستمارة": ["الاستمارة","استمارة","النموذج","نموذج","الفورم","تعليق الاستمارة","لا استطيع اكمال الاستمارة","التعبئة"],
31
+ "التقييم": ["التقييم","تقييم","feedback","survey","رضا","نجوم"],
32
+ "الخرائط": ["الخرائط","خرائط","map","gps","تحديد الموقع","احداثيات","إحداثيات","الموقع الجغرافي"],
33
+ "السوتي": ["السوتي","سوتي","soti","soti assist","mobicontrol","soti mobicontrol"],
34
+ "الشبكة": ["الشبكة","شبكة","نت","انترنت","إنترنت","wifi","واي فاي","4g","5g","ضعف الشبكة","stc","mobily","زين","weak signal","no signal"],
35
+ "النسخة": ["ا��نسخة","نسخة","الإصدار","اصدار","version","build","release","تحديث نسخة","ترقية النسخة"],
36
+ "النظام المكتبي": ["النظام المكتبي","نسخة ويندوز","ويندوز","windows","pc app","برنامج المكتب","التطبيق على الكمبيوتر","الديسكتوب"],
37
+ "تسجيل دخول": ["تسجيل دخول","تسجيل الدخول","login","signin","رفض تسجيل الدخول","لا يقبل الدخول","اسم المستخدم","كلمة المرور","نسيت كلمة السر","إعادة تعيين"],
38
+ "تفعيل حساب": ["تفعيل حساب","تفعيل","activation","activate","رمز التفعيل","كود التفعيل"],
39
+ "تناقل البيانات": ["تناقل البيانات","ترحيل البيانات","مزامنة","sync","مزامنه","نقل البيانات","رفع البيانات","sync failed","المزامنة"],
40
+ "صيانة وتحديث الأجهزة": ["صيانة","تحديث الأجهزة","تحديث جهاز","ترقية الجهاز","اعطال الجهاز","تصليح","صيانة وتحديث الأجهزة","صيانة الجهاز"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  };
 
 
42
  const CLASS_PRIORITY = [
43
+ "صيانة وتحديث الأجهزة","إضافة أجهزة","تسجيل دخول","تفعيل حساب",
44
+ "الاستمارة","التقييم","الخرائط","السوتي","الشبكة","النسخة",
45
+ "النظام المكتبي","تناقل البيانات","استفسار",
 
 
 
 
 
 
 
 
 
 
46
  ];
47
 
48
  /* ================= أساسيات النص ================= */
 
154
  /* ================= التصنيف ================= */
155
  function classifyTicket(text, fields){
156
  const hay = normalizeText(`${text}\n${fields?.["نوع المشكلة"]||""}`).toLowerCase();
 
157
  for(const label of CLASS_PRIORITY){
158
  const kws = CLASS_RULES[label] || [];
159
  for(const kw of kws){
 
162
  if(needle && hay.includes(needle)) return label;
163
  }
164
  }
165
+ return "استفسار";
166
  }
167
 
168
+ /* نبني الصفوف مع "التصنيف" + "اسم الدعم الفني" + "الحالة" + تعبئة المنطقة الافتراضية عند الفراغ */
169
+ function parseTicketsWithExtras(raw, agentName, defaultRegion){
170
  return splitTickets(raw||"").map(t => {
171
  const f = extractFields(t);
172
  const cls = classifyTicket(t, f);
173
+
174
+ // املأ المنطقة الافتراضية إذا كانت فارغة
175
+ const region = f["المنطقة"] && f["المنطقة"].trim() ? f["المنطقة"] : (defaultRegion || "");
176
+
177
+ return {
178
+ "التصنيف": cls,
179
+ "نوع المشكلة": f["نوع المشكلة"] || "",
180
+ "وقت حدوث المشكلة": f["وقت حدوث المشكلة"] || "",
181
+ "اسم صاحب المشكلة": f["اسم صاحب المشكلة"] || "",
182
+ "رقم الهوية": f["رقم الهوية"] || "",
183
+ "رقم الجهاز": f["رقم الجهاز"] || "",
184
+ "رقم الجوال": f["رقم الجوال"] || "",
185
+ "المسح": f["المسح"] || "",
186
+ "المنطقة": region,
187
+ "اسم الدعم الفني": agentName || "",
188
+ "الحالة": "تم الحل",
189
+ };
190
  });
191
  }
192
 
 
254
  document.addEventListener("input",(e)=>{
255
  if(e.target && e.target.closest && e.target.closest("#tbody")){
256
  validateCells();
257
+ saveState(); // حفظ عند التعديل
258
  }
259
  });
260
 
 
275
  const ws = XLSX.utils.aoa_to_sheet(aoa);
276
  ws["!rtl"] = true;
277
 
 
278
  const textCols = new Set(["رقم الهوية","رقم الجهاز","رقم الجوال"]);
279
+ const nRows = rows.length + 1;
280
  EXPORT_COLUMNS.forEach((colName, colIdx)=>{
281
  if(!textCols.has(colName)) return;
282
  for(let r = 1; r < nRows; r++){
 
306
 
307
  if (navigator.canShare && navigator.canShare({ files: [file] })) {
308
  try { await navigator.share({ files: [file], title: "ملف التذاكر" }); toast("تمت المشاركة/الحفظ."); return; }
309
+ catch(e){ /* أغلق المشاركة */ }
310
  }
311
  const url = URL.createObjectURL(blob);
312
  const a = document.createElement("a"); a.href = url; a.download = filename;
 
331
  ).join("\r\n");
332
 
333
  const tsv = "\uFEFF" + header + "\r\n" + body;
 
334
  try{
335
  await navigator.clipboard.writeText(tsv);
336
  toast("تم النسخ — الصق/ي مباشرة في Excel.");
 
353
  المنطقة: الشرقية`;
354
 
355
  /* ================= حفظ واسترجاع الحالة ================= */
356
+ const STATE_KEY = "ticketParserState_v3"; // نسخة جديدة لأننا أضفنا أعمدة وخيارات
357
+ function ensureColumns(rows, agentName, defaultRegion){
358
  if(!Array.isArray(rows)) return rows||[];
359
  return rows.map(r=>{
360
+ const out = {...r};
361
+ if(!("التصنيف" in out) || !out["التصنيف"]){
362
+ const fakeText = Object.values(out).join("\n");
363
+ out["التصنيف"] = classifyTicket(fakeText, out);
 
364
  }
365
+ if(!("اسم الدعم الفني" in out)) out["اسم الدعم الفني"] = agentName || out["اسم الدعم الفني"] || "";
366
+ if(!("الحالة" in out) || !out["الحالة"]) out["الحالة"] = "تم الحل";
367
+ if((!out["المنطقة"] || !out["المنطقة"].trim()) && defaultRegion) out["المنطقة"] = defaultRegion;
368
+ return out;
369
  });
370
  }
371
  function saveState(){
372
  try{
373
+ const raw = document.getElementById("raw")?.value || "";
374
  const fname = document.getElementById("fname")?.value || "Ticket";
375
+ const agent = document.getElementById("agentName")?.value || "";
376
+ const region= document.getElementById("regionDefault")?.value || "";
377
+ const rows = readTable();
378
+ const state = { raw, fname, agent, region, rows };
379
  localStorage.setItem(STATE_KEY, JSON.stringify(state));
380
  }catch{}
381
  }
 
383
  try{
384
  const s = localStorage.getItem(STATE_KEY);
385
  if(!s) return false;
386
+ let { raw, fname, agent, region, rows } = JSON.parse(s);
387
  if(typeof raw === "string"){ const el=document.getElementById("raw"); if(el) el.value = raw; }
388
  if(typeof fname === "string"){ const el=document.getElementById("fname"); if(el) el.value = fname; }
389
+ if(typeof agent === "string"){ const el=document.getElementById("agentName"); if(el) el.value = agent; }
390
+ if(typeof region === "string"){ const el=document.getElementById("regionDefault"); if(el) el.value = region; }
391
+ rows = ensureColumns(rows, agent, region);
392
  if(Array.isArray(rows) && rows.length){
393
  buildTable(rows);
394
  validateCells();
 
421
  const sampleBtn = document.getElementById("btn-sample");
422
  const rawEl = document.getElementById("raw");
423
  const fnameEl = document.getElementById("fname");
424
+ const agentEl = document.getElementById("agentName");
425
+ const regionEl = document.getElementById("regionDefault");
426
 
427
  // استرجاع الحالة المخزنة
428
  loadState();
429
 
430
  parseBtn.addEventListener("click", ()=>{
431
  const raw = (rawEl.value || SAMPLE);
432
+ const agent = agentEl.value || "";
433
+ const defRegion = regionEl.value || "";
434
+ const rows = parseTicketsWithExtras(raw, agent, defRegion);
435
  buildTable(rows);
436
  validateCells();
437
  updateBadge(rows.length);
438
  setButtonsEnabled(rows.length>0);
439
+ saveState();
440
  toast(`تم استخراج ${rows.length} ${rows.length===1 ? "تذكرة" : "تذاكر"}.`);
441
  });
442
 
 
444
  copyBtn.addEventListener("click", copyToClipboardTSV);
445
 
446
  clearBtn.addEventListener("click", ()=>{
447
+ clearState();
448
+ wipeUI();
449
+ // لا نمسح اسم الدعم والمنطقة الافتراضية لراحَتِك: فقط نمسح المدخلات والجدول
450
  toast("تم مسح كل البيانات.");
451
  });
452
 
 
454
 
455
  rawEl.addEventListener("input", saveState);
456
  fnameEl.addEventListener("input", saveState);
457
+ agentEl.addEventListener("input", saveState);
458
+ regionEl.addEventListener("change", saveState);
459
 
460
  // اختصارات لوحة المفاتيح
461
  document.addEventListener("keydown", (e)=>{