stat2025 commited on
Commit
7b0dd56
·
verified ·
1 Parent(s): cf762fc

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +73 -57
app.js CHANGED
@@ -1,4 +1,4 @@
1
- /* ========= منطق التحليل والتصدير (متصفح فقط) ========= */
2
  const EXPORT_COLUMNS = [
3
  "نوع المشكلة","وقت حدوث المشكلة","اسم صاحب المشكلة",
4
  "رقم الهوية","رقم الجهاز","رقم الجوال","المسح","المنطقة"
@@ -15,14 +15,13 @@ const FIELD_ALIASES = {
15
  "المنطقة": ["المنطقة","المنطقه","المدينة","المحافظة","منطقة"],
16
  };
17
 
18
- const LABEL_SEP = "(?::|:)?\\s*"; // نقطتان اختيارية
19
- const TICKET_SEP = /\n\s*(?:\n|—+|-{3,}|={3,}|🔴+)+\s*\n/; // فواصل بين التذاكر
20
 
21
- /* --------- Helpers --------- */
22
  const arabicDigitsMap = {"٠":"0","١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9"};
 
23
  function normalizeText(s){
24
  if(typeof s!=="string") return "";
25
- // أرقام عربية -> إنجليزية + إزالة محارف اتجاه ومسافات خاصة + المدود
26
  return s.replace(/[\u200f\u200e\u202a-\u202e\u2066-\u2069\u00a0]/g," ")
27
  .replace(/[٠-٩]/g, d => arabicDigitsMap[d] )
28
  .replace(/[ــ]+/g,"")
@@ -59,8 +58,6 @@ function splitTickets(raw){
59
  if(parts.length===1){ parts = raw.split(/\n\s*\n+/).filter(p=>p.trim()); }
60
  return parts.map(p=>p.trim()).filter(Boolean);
61
  }
62
-
63
- /* compile field regexes (نفس السطر / السطر التالي) */
64
  function compileFieldPatterns(){
65
  const pats = {};
66
  for(const [canonical, labels] of Object.entries(FIELD_ALIASES)){
@@ -74,7 +71,6 @@ function compileFieldPatterns(){
74
  }
75
  const FIELD_PATTERNS = compileFieldPatterns();
76
 
77
- /* استخراج الحقول من تذكرة واحدة */
78
  function extractFields(ticketText){
79
  const data = {};
80
  for(const k of Object.keys(FIELD_ALIASES)) data[k]="";
@@ -91,7 +87,6 @@ function extractFields(ticketText){
91
  }
92
  }
93
  }
94
- // احتياط للأرقام حتى بدون عناوين
95
  if(!data["رقم الجهاز"]){
96
  const m = text.match(/(?:رقم\s*الجهاز|الجهاز)\D*([0-9][0-9\-\s]{2,})/i);
97
  if(m) data["رقم الجهاز"]=m[1].replace(/\D/g,"").slice(0,20);
@@ -108,7 +103,6 @@ function extractFields(ticketText){
108
  const m = text.match(/(?:اسم\s*المسح|المسح)\s*[::]?\s*(.+)/);
109
  if(m) data["المسح"]=normalizeText(m[1].split(/\r?\n/)[0]);
110
  }
111
- // دمج التاريخ مع الوقت إن وُجد تاريخ
112
  const dm = text.match(/(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}|\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2})/);
113
  if(dm){
114
  const date = normalizeDate(dm[1]);
@@ -118,25 +112,20 @@ function extractFields(ticketText){
118
  return data;
119
  }
120
 
121
- /* تحليل مجموعة تذاكر -> صفوف */
122
  function parseTickets(raw){
123
  const tickets = splitTickets(raw||"");
124
- const rows = tickets.map(extractFields);
125
- return rows;
126
  }
127
 
128
- /* بناء الجدول وتوسيط المحتوى */
129
  function buildTable(rows){
130
  const theadRow = document.getElementById("theadRow");
131
  const tbody = document.getElementById("tbody");
132
- // رؤوس الأعمدة
133
  theadRow.innerHTML = "";
134
  EXPORT_COLUMNS.forEach(col=>{
135
  const th=document.createElement("th");
136
  th.textContent=col;
137
  theadRow.appendChild(th);
138
  });
139
- // جسم الجدول
140
  tbody.innerHTML = "";
141
  rows.forEach(r=>{
142
  const tr=document.createElement("tr");
@@ -150,82 +139,109 @@ function buildTable(rows){
150
  });
151
  }
152
 
153
- /* قراءة الجدول إلى مصفوفة كائنات */
154
  function readTable(){
155
  const tbody = document.getElementById("tbody");
156
  const rows = [];
157
  [...tbody.querySelectorAll("tr")].forEach(tr=>{
158
  const obj={};
159
- [...tr.children].forEach((td,idx)=>{
160
- obj[EXPORT_COLUMNS[idx]] = td.textContent.trim();
161
- });
162
  rows.push(obj);
163
  });
164
  return rows;
165
  }
166
 
167
- /* تصدير إلى Excel (تحميل تلقائي) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  function exportExcel(){
169
  const rows = readTable();
170
- const aoa = [EXPORT_COLUMNS];
171
- rows.forEach(r=>{
172
- aoa.push(EXPORT_COLUMNS.map(c=>r[c]||""));
173
- });
174
  const ws = XLSX.utils.aoa_to_sheet(aoa);
175
- // تفعيل اتجاه RTL (مدعوم في بعض العارضات)
176
  ws["!rtl"] = true;
177
  const wb = XLSX.utils.book_new();
178
  XLSX.utils.book_append_sheet(wb, ws, "التذاكر");
179
 
180
  const now = new Date();
181
- const ts = now.toISOString().replace(/\D/g,'').slice(0,14); // YYYYMMDDHHMMSS
182
  const base = (document.getElementById("fname").value || "Ticket").trim() || "Ticket";
183
  XLSX.writeFile(wb, `${base}_${ts}.xlsx`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  }
185
 
186
- /* مسح كل شيء */
187
- function clearAll(){
188
- document.getElementById("raw").value = "";
189
- document.getElementById("tbody").innerHTML = "";
190
  }
191
 
192
- /* مثال سريع */
193
- const SAMPLE = `🔴🔴🔴
194
- نوع المشكلة : لا أقدر أكمل الدخول
195
- وقت حدوث المشكلة: 21/8/2025 7:00
196
- اسم صاحب المشكلة : محمد بن علي
197
- رقم الهوية: 1068891991
198
- رقم الجهاز: 01438
199
- رقم الجوال: 0556665323
200
- اسم المسح: الجبيل
201
- المنطقة: الشرقية
202
-
203
- 🔴🔴🔴
204
- نوع المشكلة: تعليق مستمر رغم إعادة التشغيل
205
- وقت حدوث المشكلة: 20-08-2025 18:10
206
- اسم صاحب المشكلة: هدى صالح
207
- رقم الهوية: 1086892231
208
- رقم الجهاز: 868190043822887
209
- رقم الجوال: 0552259541
210
- اسم المسح: الرخص البلدية
211
- المنطقة: الرياض`;
212
-
213
- /* ربط الأزرار */
214
  document.addEventListener("DOMContentLoaded", ()=>{
215
  const parseBtn = document.getElementById("btn-parse");
216
  const exportBtn = document.getElementById("btn-export");
 
217
  const clearBtn = document.getElementById("btn-clear");
218
  const sampleBtn = document.getElementById("btn-sample");
 
219
 
220
  parseBtn.addEventListener("click", ()=>{
221
- const raw = document.getElementById("raw").value || SAMPLE;
222
  const rows = parseTickets(raw);
223
  buildTable(rows);
 
 
 
224
  });
225
 
226
  exportBtn.addEventListener("click", exportExcel);
227
- clearBtn.addEventListener("click", clearAll);
228
- sampleBtn.addEventListener("click", ()=>{
229
- document.getElementById("raw").value = SAMPLE;
 
 
 
 
230
  });
 
 
 
 
 
231
  });
 
1
+ /* ========= منطق التحليل والتصدير والنسخ (Static فقط) ========= */
2
  const EXPORT_COLUMNS = [
3
  "نوع المشكلة","وقت حدوث المشكلة","اسم صاحب المشكلة",
4
  "رقم الهوية","رقم الجهاز","رقم الجوال","المسح","المنطقة"
 
15
  "المنطقة": ["المنطقة","المنطقه","المدينة","المحافظة","منطقة"],
16
  };
17
 
18
+ const LABEL_SEP = "(?::|:)?\\s*";
19
+ const TICKET_SEP = /\n\s*(?:\n|—+|-{3,}|={3,}|🔴+)+\s*\n/;
20
 
 
21
  const arabicDigitsMap = {"٠":"0","١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9"};
22
+
23
  function normalizeText(s){
24
  if(typeof s!=="string") return "";
 
25
  return s.replace(/[\u200f\u200e\u202a-\u202e\u2066-\u2069\u00a0]/g," ")
26
  .replace(/[٠-٩]/g, d => arabicDigitsMap[d] )
27
  .replace(/[ــ]+/g,"")
 
58
  if(parts.length===1){ parts = raw.split(/\n\s*\n+/).filter(p=>p.trim()); }
59
  return parts.map(p=>p.trim()).filter(Boolean);
60
  }
 
 
61
  function compileFieldPatterns(){
62
  const pats = {};
63
  for(const [canonical, labels] of Object.entries(FIELD_ALIASES)){
 
71
  }
72
  const FIELD_PATTERNS = compileFieldPatterns();
73
 
 
74
  function extractFields(ticketText){
75
  const data = {};
76
  for(const k of Object.keys(FIELD_ALIASES)) data[k]="";
 
87
  }
88
  }
89
  }
 
90
  if(!data["رقم الجهاز"]){
91
  const m = text.match(/(?:رقم\s*الجهاز|الجهاز)\D*([0-9][0-9\-\s]{2,})/i);
92
  if(m) data["رقم الجهاز"]=m[1].replace(/\D/g,"").slice(0,20);
 
103
  const m = text.match(/(?:اسم\s*المسح|المسح)\s*[::]?\s*(.+)/);
104
  if(m) data["المسح"]=normalizeText(m[1].split(/\r?\n/)[0]);
105
  }
 
106
  const dm = text.match(/(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}|\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2})/);
107
  if(dm){
108
  const date = normalizeDate(dm[1]);
 
112
  return data;
113
  }
114
 
 
115
  function parseTickets(raw){
116
  const tickets = splitTickets(raw||"");
117
+ return tickets.map(extractFields);
 
118
  }
119
 
 
120
  function buildTable(rows){
121
  const theadRow = document.getElementById("theadRow");
122
  const tbody = document.getElementById("tbody");
 
123
  theadRow.innerHTML = "";
124
  EXPORT_COLUMNS.forEach(col=>{
125
  const th=document.createElement("th");
126
  th.textContent=col;
127
  theadRow.appendChild(th);
128
  });
 
129
  tbody.innerHTML = "";
130
  rows.forEach(r=>{
131
  const tr=document.createElement("tr");
 
139
  });
140
  }
141
 
 
142
  function readTable(){
143
  const tbody = document.getElementById("tbody");
144
  const rows = [];
145
  [...tbody.querySelectorAll("tr")].forEach(tr=>{
146
  const obj={};
147
+ [...tr.children].forEach((td,idx)=>{ obj[EXPORT_COLUMNS[idx]] = td.textContent.trim(); });
 
 
148
  rows.push(obj);
149
  });
150
  return rows;
151
  }
152
 
153
+ function updateCount(n){
154
+ const chip = document.getElementById("countChip");
155
+ chip.textContent = `عدد التذاكر: ${n}`;
156
+ }
157
+
158
+ function toast(msg){
159
+ const t = document.getElementById("toast");
160
+ t.textContent = msg;
161
+ t.hidden = false;
162
+ t.classList.remove("show");
163
+ void t.offsetWidth; // reflow
164
+ t.classList.add("show");
165
+ setTimeout(()=>{ t.hidden = true; }, 2400);
166
+ }
167
+
168
  function exportExcel(){
169
  const rows = readTable();
170
+ if(!rows.length){ toast("لا يوجد بيانات لتصديرها."); return; }
171
+ const aoa = [EXPORT_COLUMNS, ...rows.map(r=>EXPORT_COLUMNS.map(c=>r[c]||""))];
 
 
172
  const ws = XLSX.utils.aoa_to_sheet(aoa);
 
173
  ws["!rtl"] = true;
174
  const wb = XLSX.utils.book_new();
175
  XLSX.utils.book_append_sheet(wb, ws, "التذاكر");
176
 
177
  const now = new Date();
178
+ const ts = now.toISOString().replace(/\D/g,'').slice(0,14);
179
  const base = (document.getElementById("fname").value || "Ticket").trim() || "Ticket";
180
  XLSX.writeFile(wb, `${base}_${ts}.xlsx`);
181
+ toast("تم تصدير الملف بنجاح.");
182
+ }
183
+
184
+ async function copyToClipboardTSV(){
185
+ const rows = readTable();
186
+ if(!rows.length){ toast("لا يوجد بيانات لنسخها."); return; }
187
+ const header = EXPORT_COLUMNS.join("\t");
188
+ const body = rows.map(r=>EXPORT_COLUMNS.map(c=>(r[c]||"").replace(/\t/g," ")).join("\t")).join("\n");
189
+ const tsv = `${header}\n${body}`;
190
+ try{
191
+ await navigator.clipboard.writeText(tsv);
192
+ toast("تم النسخ — الصق/ي مباشرة في Excel.");
193
+ }catch(e){
194
+ // بديل: تحديد نص مخفي
195
+ const ta = document.createElement("textarea");
196
+ ta.value = tsv; document.body.appendChild(ta);
197
+ ta.select(); document.execCommand("copy"); document.body.removeChild(ta);
198
+ toast("تم النسخ — الصق/ي مباشرة في Excel.");
199
+ }
200
  }
201
 
202
+ function setButtonsEnabled(hasRows){
203
+ document.getElementById("btn-export").disabled = !hasRows;
204
+ document.getElementById("btn-copy").disabled = !hasRows;
 
205
  }
206
 
207
+ const SAMPLE = `نوع المشكلة : لا استطيع اكمال الاستمارة بسبب تعليق
208
+ وقت حدوث المشكلة: 21/8/2025
209
+ اسم صاحب المشكلة : نوف الناصر
210
+ رقم الهوية: 1234567890
211
+ رقم الجهاز: 01234
212
+ رقم الجوال: 0558174717
213
+ اسم المسح: الخبر
214
+ المنطقة: الشرقية`;
215
+
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  document.addEventListener("DOMContentLoaded", ()=>{
217
  const parseBtn = document.getElementById("btn-parse");
218
  const exportBtn = document.getElementById("btn-export");
219
+ const copyBtn = document.getElementById("btn-copy");
220
  const clearBtn = document.getElementById("btn-clear");
221
  const sampleBtn = document.getElementById("btn-sample");
222
+ const rawEl = document.getElementById("raw");
223
 
224
  parseBtn.addEventListener("click", ()=>{
225
+ const raw = rawEl.value || SAMPLE;
226
  const rows = parseTickets(raw);
227
  buildTable(rows);
228
+ updateCount(rows.length);
229
+ setButtonsEnabled(rows.length>0);
230
+ toast(`تم استخراج ${rows.length} ${rows.length===1 ? "تذكرة" : "تذاكر"}.`);
231
  });
232
 
233
  exportBtn.addEventListener("click", exportExcel);
234
+ copyBtn.addEventListener("click", copyToClipboardTSV);
235
+
236
+ clearBtn.addEventListener("click", ()=>{
237
+ rawEl.value = "";
238
+ document.getElementById("tbody").innerHTML = "";
239
+ updateCount(0);
240
+ setButtonsEnabled(false);
241
  });
242
+
243
+ sampleBtn.addEventListener("click", ()=>{ rawEl.value = SAMPLE; });
244
+
245
+ // تعطيل أزرار التصدير/النسخ مبدئيًا
246
+ setButtonsEnabled(false);
247
  });