stat2025 commited on
Commit
63efdf3
ยท
verified ยท
1 Parent(s): 251979a

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +117 -55
app.js CHANGED
@@ -1,10 +1,29 @@
1
- const ACCESS_PASSWORDS = ["12345", "12345678"];
2
 
3
  const EXPORT_COLUMNS = [
4
  "ุงู„ุชุตู†ูŠู","ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ","ุงุณู… ุตุงุญุจ ุงู„ู…ุดูƒู„ุฉ",
5
  "ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ","ุฑู‚ู… ุงู„ุฌู‡ุงุฒ","ุฑู‚ู… ุงู„ุฌูˆุงู„","ุงู„ู…ุณุญ","ุงู„ู…ู†ุทู‚ุฉ","ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ","ุงู„ุญุงู„ุฉ"
6
  ];
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  const FIELD_ALIASES = {
9
  "ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ": ["ู†ูˆุน ุงู„ู…ุดูƒู„ู‡","ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ","ุงู„ู…ุดูƒู„ุฉ","ู†ูˆุน-ุงู„ู…ุดูƒู„ุฉ","ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ"],
10
  "ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ": ["ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ู‡","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ","ูˆู‚ุช ุงู„ู…ุดูƒู„ุฉ","ูˆู‚ุช ุญุฏูˆุซ","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ู‡:","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ู‡ :"],
@@ -42,11 +61,18 @@ const arabicDigitsMap = {"ู ":"0","ูก":"1","ูข":"2","ูฃ":"3","ูค":"4","ูฅ":"5","
42
 
43
  function normalizeText(s){
44
  if(typeof s!=="string") return "";
45
- return s.replace(/\r\n/g,"\n").replace(/[\u200f\u200e\u202a-\u202e\u2066-\u2069\u00a0]/g," ").replace(/[ู -ูฉ]/g,d=>arabicDigitsMap[d]).replace(/[ู€ู€]+/g,"").replace(/[ \t]+\n/g,"\n").replace(/\n{3,}/g,"\n\n").trim();
 
 
 
 
 
 
46
  }
47
  function lettersOnly(ar){return(ar||"").replace(/[^A-Za-z\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\s]/g,"").replace(/\s{2,}/g," ").trim()}
48
  function alnumAr(s){return(s||"").replace(/[^0-9A-Za-z\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\s\-\._/]/g,"").replace(/\s{2,}/g," ").trim()}
49
  function digitsOnly(s){return(s||"").replace(/\D+/g,"")}
 
50
 
51
  const LABEL_FIXES = [
52
  [/(^|\n)\s*ู†ูˆุน\s*ุงู„ู…ุดูƒู„ู‡/gi,"$1ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ"],
@@ -77,6 +103,15 @@ function hijriToGregorian(hy,hm,hd){
77
  l=Math.floor(j/11);const m=j+2-12*l;const y=100*(n-49)+i+l;
78
  return[y,m,d];
79
  }
 
 
 
 
 
 
 
 
 
80
  function detectHijriDate(str){
81
  const t=normalizeText(str);
82
  let m=t.match(/(\d{1,2})\s+([^\s]+)\s+(\d{3,4})\s*(ู‡ู€|ู‡|ู‡ุฌุฑูŠ)?/i);
@@ -85,15 +120,10 @@ function detectHijriDate(str){
85
  if(m)return{hy:+m[3],hm:+m[2],hd:+m[1]};
86
  return null;
87
  }
88
- function isTimeOnly(t){
89
- const a=/(^|\s)\d{1,2}\s*(?:[:ูซ\.\-]\d{2})\s*(?:ุต|ุตุจุงุญ(?:ุงู‹|ุง)?|am|ู…|ู…ุณุงุก|pm)?($|\s)/i.test(t);
90
- const b=/(^|\s)\d{1,2}\s*(?:ุต|ุตุจุงุญ(?:ุงู‹|ุง)?|am|ู…|ู…ุณุงุก|pm)($|\s)/i.test(t);
91
- return a||b;
92
- }
93
  function normalizeDateOnly(raw){
94
  const t=normalizeText(raw);
 
95
  const hj=detectHijriDate(t);
96
- if(isTimeOnly(t)&&!hj&&!/(\d{3,4}).(\d{1,2}).(\d{1,2})/.test(t))return"";
97
  if(hj){const[gy,gm,gd]=hijriToGregorian(hj.hy,hj.hm,hj.hd);return`${String(gy).padStart(4,"0")}-${String(gm).padStart(2,"0")}-${String(gd).padStart(2,"0")}`}
98
  const m=t.match(/(\d{1,4})[\/\-](\d{1,2})[\/\-](\d{1,4})/);
99
  if(m){
@@ -108,7 +138,7 @@ function normalizeDateOnly(raw){
108
  if(mo<1||mo>12||d<1||d>31)return"";
109
  return`${String(y).padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).padStart(2,"0")}`
110
  }
111
- return t;
112
  }
113
 
114
  function findStartsByLabels(text,labels){
@@ -202,16 +232,48 @@ function catClass(label){
202
  return"default";
203
  }
204
 
 
 
 
 
 
 
 
 
 
205
  function parseTicketsWithExtras(raw,agentName,defaultRegion){
206
  const regionChosen=(defaultRegion||"").toString();
207
  return splitTickets(raw||"").map(t=>{
208
  const f=extractFields(t);
209
  const cls=classifyTicket(t,f);
 
210
  const region=regionChosen?regionChosen:(f["ุงู„ู…ู†ุทู‚ุฉ"]||"");
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  return{
212
- "ุงู„ุชุตู†ูŠู":cls,"ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ":f["ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ"]||"","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ":f["ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ"]||"",
213
- "ุงุณู… ุตุงุญุจ ุงู„ู…ุดูƒู„ุฉ":f["ุงุณู… ุตุงุญุจ ุงู„ู…ุดูƒู„ุฉ"]||"","ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ":f["ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ"]||"","ุฑู‚ู… ุงู„ุฌู‡ุงุฒ":f["ุฑู‚ู… ุงู„ุฌู‡ุงุฒ"]||"",
214
- "ุฑู‚ู… ุงู„ุฌูˆุงู„":f["ุฑู‚ู… ุงู„ุฌูˆุงู„"]||"","ุงู„ู…ุณุญ":f["ุงู„ู…ุณุญ"]||"","ุงู„ู…ู†ุทู‚ุฉ":region,"ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ":agentName||"","ุงู„ุญุงู„ุฉ":"ุชู… ุงู„ุญู„"
 
 
 
 
 
 
 
 
 
215
  };
216
  });
217
  }
@@ -220,21 +282,23 @@ function buildTable(rows){
220
  const theadRow=document.getElementById("theadRow");
221
  const tbody=document.getElementById("tbody");
222
  theadRow.innerHTML="";
223
- EXPORT_COLUMNS.forEach(col=>{const th=document.createElement("th");th.textContent=col;theadRow.appendChild(th)});
224
  tbody.innerHTML="";
225
  rows.forEach(r=>{
226
  const tr=document.createElement("tr");
227
- EXPORT_COLUMNS.forEach(col=>{
228
  const td=document.createElement("td");
 
 
 
229
  if(col==="ุงู„ุชุตู†ูŠู"){
230
  const span=document.createElement("span");
231
- const type=catClass(r[col]||"");
232
- span.className=`cat ${type}`;
233
- span.textContent=r[col]||"";
234
  td.appendChild(span);
235
  }else{
236
  td.contentEditable="true";
237
- td.textContent=r[col]||"";
238
  }
239
  tr.appendChild(td);
240
  });
@@ -247,10 +311,16 @@ function readTable(){
247
  const rows=[];
248
  [...tbody.querySelectorAll("tr")].forEach(tr=>{
249
  const obj={};
250
- [...tr.children].forEach((td,idx)=>{
251
- const col=EXPORT_COLUMNS[idx];
252
- obj[col]=td.textContent.trim();
 
 
 
 
 
253
  });
 
254
  rows.push(obj);
255
  });
256
  return rows;
@@ -258,6 +328,7 @@ function readTable(){
258
 
259
  function updateBadge(n){
260
  const b=document.getElementById("countBadge");
 
261
  b.textContent=n;
262
  b.hidden=(n===0);
263
  }
@@ -268,26 +339,26 @@ function setButtonsEnabled(hasRows){
268
 
269
  function validateCells(){
270
  const tbody=document.getElementById("tbody");
271
- const required=new Set(["ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ","ุงุณู… ุตุงุญุจ ุงู„ู…ุดูƒู„ุฉ","ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ","ุฑู‚ู… ุงู„ุฌู‡ุงุฒ","ุฑู‚ู… ุงู„ุฌูˆุงู„","ุงู„ู…ุณุญ","ุงู„ู…ู†ุทู‚ุฉ","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ"]);
272
  let missing=0;
273
  [...tbody.rows].forEach(tr=>{
274
- [...tr.children].forEach((td,idx)=>{
275
- const col=EXPORT_COLUMNS[idx];
276
  const val=(td.textContent||"").trim();
277
  let invalid=false;
278
  let reason="";
279
- if(required.has(col)&&!val){invalid=true;reason="required";missing++}
280
- if(col==="ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ"){
281
  const digits=val.replace(/\D/g,"");
282
- if(val&&digits.length!==10){invalid=true;if(!reason)reason="id"}
283
  }
284
- if(col==="ุฑู‚ู… ุงู„ุฌูˆุงู„"){
285
  const digits=val.replace(/\D/g,"");
286
- if(val&&digits.length<9){invalid=true;if(!reason)reason="phone"}
287
  }
288
  td.classList.toggle("invalid",invalid);
289
  if(invalid){
290
- const msg=reason==="required"?"ุงู„ุญู‚ู„ ู…ุทู„ูˆุจ":reason==="id"?"ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ูŠุฌุจ ุฃู† ูŠูƒูˆู† 10 ุฎุงู†ุงุช":reason==="phone"?"ุฑู‚ู… ุงู„ุฌูˆุงู„ ุบูŠุฑ ุตุญูŠุญ":"ู‚ูŠู…ุฉ ุบูŠุฑ ุตุญูŠุญุฉ";
291
  td.setAttribute("title",msg);
292
  td.dataset.reason=reason;
293
  }else{
@@ -310,6 +381,7 @@ document.addEventListener("input",e=>{
310
 
311
  function toast(msg){
312
  const t=document.getElementById("toast");
 
313
  t.textContent=msg;
314
  t.hidden=false;
315
  t.classList.remove("show");void t.offsetWidth;t.classList.add("show");
@@ -364,12 +436,18 @@ async function exportExcel(){
364
  async function copyToClipboardTSV(){
365
  const rows=readTable();
366
  if(!rows.length){toast("ู„ุง ูŠูˆุฌุฏ ุจูŠุงู†ุงุช ู„ู†ุณุฎู‡ุง.");return}
367
- const textCols=new Set(["ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ","ุฑู‚ู… ุงู„ุฌู‡ุงุฒ","ุฑู‚ู… ุงู„ุฌูˆุงู„"]);
368
- const header=EXPORT_COLUMNS.join("\t");
369
- const body=rows.map(r=>EXPORT_COLUMNS.map(c=>{let v=(r[c]??"").toString().replace(/\t/g," ");if(textCols.has(c)&&v&&/^[0-9]+$/.test(v))v="'"+v;return v}).join("\t")).join("\r\n");
370
- const tsv="\uFEFF"+header+"\r\n"+body;
371
- try{await navigator.clipboard.writeText(tsv);toast("ุชู… ุงู„ู†ุณุฎ โ€” ุงู„ุตู‚/ูŠ ู…ุจุงุดุฑุฉ ููŠ Excel.")}catch(e){
372
- const ta=document.createElement("textarea");ta.value=tsv;document.body.appendChild(ta);ta.select();document.execCommand("copy");document.body.removeChild(ta);toast("ุชู… ุงู„ู†ุณุฎ โ€” ุงู„ุตู‚/ูŠ ู…ุจุงุดุฑุฉ ููŠ Excel.")
 
 
 
 
 
 
373
  }
374
  }
375
 
@@ -382,34 +460,19 @@ const SAMPLE=`ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ: ู„ุง ุงุณุชุทูŠุน ุงูƒู…ุงู„ ุงู„ุงุณุชู…ุง
382
  ุงู„ู…ุณุญ: ุงู„ุฎุจุฑ
383
  ุงู„ู…ู†ุทู‚ุฉ: ุงู„ุดุฑู‚ูŠุฉ`;
384
 
385
- const STATE_KEY="ticketParserState_v10_16";
386
- const ALL_STATE_KEYS=["ticketParserState_v8","ticketParserState_v9","ticketParserState_v10","ticketParserState_v10_1","ticketParserState_v10_2","ticketParserState_v10_3","ticketParserState_v10_5","ticketParserState_v10_6","ticketParserState_v10_7","ticketParserState_v10_8","ticketParserState_v10_9","ticketParserState_v10_10","ticketParserState_v10_11","ticketParserState_v10_12","ticketParserState_v10_13","ticketParserState_v10_14","ticketParserState_v10_15","ticketParserState_v10_16"];
387
-
388
- function ensureColumns(rows,agentName,defaultRegion){
389
- if(!Array.isArray(rows))return rows||[];
390
- return rows.map(r=>{
391
- const out={...r};
392
- if(!("ุงู„ุชุตู†ูŠู"in out)||!out["ุงู„ุชุตู†ูŠู"]){const fakeText=Object.values(out).join("\n");out["ุงู„ุชุตู†ูŠู"]=classifyTicket(fakeText,out)}
393
- if(!("ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ"in out))out["ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ"]=agentName||out["ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ"]||"";
394
- if(!("ุงู„ุญุงู„ุฉ"in out)||!out["ุงู„ุญุงู„ุฉ"])out["ุงู„ุญุงู„ุฉ"]="ุชู… ุงู„ุญู„";
395
- if(defaultRegion)out["ุงู„ู…ู†ุทู‚ุฉ"]=defaultRegion;
396
- return out;
397
- });
398
- }
399
-
400
  function saveState(){
401
  try{
402
  const raw=document.getElementById("raw")?.value||"";
403
  const agent=document.getElementById("agentName")?.value||"";
404
  const region=document.getElementById("regionDefault")?.value||"";
405
  const rows=readTable();
406
- localStorage.setItem(STATE_KEY,JSON.stringify({raw,agent,region,rows,theme:document.body.classList.contains('dark')?'dark':'light'}));
407
  }catch{}
408
  }
409
 
410
  function loadState(){
411
  try{
412
- const s=localStorage.getItem(STATE_KEY);
413
  if(!s)return false;
414
  let{raw,agent,region,rows,theme}=JSON.parse(s);
415
  const rawEl=document.getElementById("raw");if(typeof raw==="string"&&rawEl)rawEl.value=raw;
@@ -417,7 +480,6 @@ function loadState(){
417
  const regionEl=document.getElementById("regionDefault");if(typeof region==="string"&&regionEl)regionEl.value=region;
418
  if(theme==='dark')document.body.classList.add('dark');
419
  updateThemeLabel();
420
- rows=ensureColumns(rows,agent,region);
421
  if(Array.isArray(rows)&&rows.length){buildTable(rows);validateCells();updateBadge(rows.length);setButtonsEnabled(true)}
422
  return true;
423
  }catch{return false}
@@ -434,7 +496,7 @@ function clearAll(){
434
  if(regionEl)regionEl.value="";
435
  updateBadge(0);setButtonsEnabled(false);
436
  document.getElementById("warn").hidden=true;
437
- try{ALL_STATE_KEYS.forEach(k=>localStorage.removeItem(k))}catch{}
438
  toast("ุชู… ู…ุณุญ ูƒู„ ุงู„ุจูŠุงู†ุงุช ูˆุงู„ุชุฎุฒูŠู†.");
439
  }
440
 
 
1
+ const ACCESS_PASSWORDS = ["12345","12345678"];
2
 
3
  const EXPORT_COLUMNS = [
4
  "ุงู„ุชุตู†ูŠู","ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ","ุงุณู… ุตุงุญุจ ุงู„ู…ุดูƒู„ุฉ",
5
  "ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ","ุฑู‚ู… ุงู„ุฌู‡ุงุฒ","ุฑู‚ู… ุงู„ุฌูˆุงู„","ุงู„ู…ุณุญ","ุงู„ู…ู†ุทู‚ุฉ","ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ","ุงู„ุญุงู„ุฉ"
6
  ];
7
 
8
+ const DISPLAY_COLUMNS = [
9
+ "ุงู„ุชุตู†ูŠู","ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ","ุงู„ู…ู†ุทู‚ุฉ","ุงุณู… ุงู„ู…ุณุญ","ุงุณู… ุงู„ู…ุดุชุบู„",
10
+ "ุฑู‚ู… ุงู„ุฌูˆุงู„","ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ID","ุฑู‚ู… ุงู„ุฌู‡ุงุฒ","ุชุงุฑูŠุฎ ุงู„ูŠูˆู… ุจุงู„ู…ูŠู„ุงุฏูŠ","ุงู„ุญุงู„ุฉ","ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ"
11
+ ];
12
+
13
+ const DISPLAY_TO_BASE = {
14
+ "ุงู„ุชุตู†ูŠู":"ุงู„ุชุตู†ูŠู",
15
+ "ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ":"ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ",
16
+ "ุงู„ู…ู†ุทู‚ุฉ":"ุงู„ู…ู†ุทู‚ุฉ",
17
+ "ุงุณู… ุงู„ู…ุณุญ":"ุงู„ู…ุณุญ",
18
+ "ุงุณู… ุงู„ู…ุดุชุบู„":"ุงุณู… ุตุงุญุจ ุงู„ู…ุดูƒู„ุฉ",
19
+ "ุฑู‚ู… ุงู„ุฌูˆุงู„":"ุฑู‚ู… ุงู„ุฌูˆุงู„",
20
+ "ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ID":"ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ",
21
+ "ุฑู‚ู… ุงู„ุฌู‡ุงุฒ":"ุฑู‚ู… ุงู„ุฌู‡ุงุฒ",
22
+ "ุชุงุฑูŠุฎ ุงู„ูŠูˆู… ุจุงู„ู…ูŠู„ุงุฏูŠ":"ุชุงุฑูŠุฎ ุงู„ูŠูˆู… ุจุงู„ู…ูŠู„ุงุฏูŠ",
23
+ "ุงู„ุญุงู„ุฉ":"ุงู„ุญุงู„ุฉ",
24
+ "ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ":"ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ"
25
+ };
26
+
27
  const FIELD_ALIASES = {
28
  "ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ": ["ู†ูˆุน ุงู„ู…ุดูƒู„ู‡","ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ","ุงู„ู…ุดูƒู„ุฉ","ู†ูˆุน-ุงู„ู…ุดูƒู„ุฉ","ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ"],
29
  "ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ": ["ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ู‡","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ","ูˆู‚ุช ุงู„ู…ุดูƒู„ุฉ","ูˆู‚ุช ุญุฏูˆุซ","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ู‡:","ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ู‡ :"],
 
61
 
62
  function normalizeText(s){
63
  if(typeof s!=="string") return "";
64
+ return s.replace(/\r\n/g,"\n")
65
+ .replace(/[\u200f\u200e\u202a-\u202e\u2066-\u2069\u00a0]/g," ")
66
+ .replace(/[ู -ูฉ]/g,d=>arabicDigitsMap[d])
67
+ .replace(/[ู€ู€]+/g,"")
68
+ .replace(/[ \t]+\n/g,"\n")
69
+ .replace(/\n{3,}/g,"\n\n")
70
+ .trim();
71
  }
72
  function lettersOnly(ar){return(ar||"").replace(/[^A-Za-z\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\s]/g,"").replace(/\s{2,}/g," ").trim()}
73
  function alnumAr(s){return(s||"").replace(/[^0-9A-Za-z\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\s\-\._/]/g,"").replace(/\s{2,}/g," ").trim()}
74
  function digitsOnly(s){return(s||"").replace(/\D+/g,"")}
75
+ function todayYMD(){const d=new Date();const y=d.getFullYear();const m=String(d.getMonth()+1).padStart(2,"0");const dd=String(d.getDate()).padStart(2,"0");return `${y}-${m}-${dd}`;}
76
 
77
  const LABEL_FIXES = [
78
  [/(^|\n)\s*ู†ูˆุน\s*ุงู„ู…ุดูƒู„ู‡/gi,"$1ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ"],
 
103
  l=Math.floor(j/11);const m=j+2-12*l;const y=100*(n-49)+i+l;
104
  return[y,m,d];
105
  }
106
+ function isTimeOnly(t){
107
+ const a=/(^|\s)\d{1,2}\s*(?:[:ูซ\.\-]\d{2})\s*(?:ุต|ุตุจุงุญ(?:ุงู‹|ุง)?|am|ู…|ู…ุณุงุก|pm)?($|\s)/i.test(t);
108
+ const b=/(^|\s)\d{1,2}\s*(?:ุต|ุตุจุงุญ(?:ุงู‹|ุง)?|am|ู…|ู…ุณุงุก|pm)($|\s)/i.test(t);
109
+ const c=/ุงู„ุณุงุนุฉ\s*\d{1,2}(?:[:ูซ\.\-]\d{2})?/i.test(t);
110
+ return a||b||c;
111
+ }
112
+ function isRelativeDatePhrase(t){
113
+ return /(ุฃู…ุณ|ู…ู†\s*ุฃู…ุณ|ู…ู†\s*ุงู…ุณ|ุงู„ูŠูˆู…|ุบุฏู‹ุง|ุบุฏุง|ุจูƒุฑุฉ|ุจุนุฏ\s*ุจูƒุฑุฉ|ู‚ุจู„\s*\d+\s*(?:ุฏู‚ูŠู‚ุฉ|ุฏู‚ุงูŠู‚|ุณุงุน(?:ุฉ|ุงุช)|ูŠูˆู…|ุฃูŠุงู…)|ุงู„ุขู†|ุงู„ุญูŠู†|ู‚ุจู„\s*ุดูˆูŠ)/i.test(t);
114
+ }
115
  function detectHijriDate(str){
116
  const t=normalizeText(str);
117
  let m=t.match(/(\d{1,2})\s+([^\s]+)\s+(\d{3,4})\s*(ู‡ู€|ู‡|ู‡ุฌุฑูŠ)?/i);
 
120
  if(m)return{hy:+m[3],hm:+m[2],hd:+m[1]};
121
  return null;
122
  }
 
 
 
 
 
123
  function normalizeDateOnly(raw){
124
  const t=normalizeText(raw);
125
+ if(isTimeOnly(t)||isRelativeDatePhrase(t))return"";
126
  const hj=detectHijriDate(t);
 
127
  if(hj){const[gy,gm,gd]=hijriToGregorian(hj.hy,hj.hm,hj.hd);return`${String(gy).padStart(4,"0")}-${String(gm).padStart(2,"0")}-${String(gd).padStart(2,"0")}`}
128
  const m=t.match(/(\d{1,4})[\/\-](\d{1,2})[\/\-](\d{1,4})/);
129
  if(m){
 
138
  if(mo<1||mo>12||d<1||d>31)return"";
139
  return`${String(y).padStart(4,"0")}-${String(mo).padStart(2,"0")}-${String(d).padStart(2,"0")}`
140
  }
141
+ return"";
142
  }
143
 
144
  function findStartsByLabels(text,labels){
 
232
  return"default";
233
  }
234
 
235
+ function isValidNationalId(d){
236
+ const x=(d||"").replace(/\D/g,"");
237
+ return /^[12]\d{9}$/.test(x);
238
+ }
239
+ function isPhoneNumber(d){
240
+ const x=(d||"").replace(/\D/g,"");
241
+ return /^05\d{8}$/.test(x);
242
+ }
243
+
244
  function parseTicketsWithExtras(raw,agentName,defaultRegion){
245
  const regionChosen=(defaultRegion||"").toString();
246
  return splitTickets(raw||"").map(t=>{
247
  const f=extractFields(t);
248
  const cls=classifyTicket(t,f);
249
+
250
  const region=regionChosen?regionChosen:(f["ุงู„ู…ู†ุทู‚ุฉ"]||"");
251
+ let survey=f["ุงู„ู…ุณุญ"]||region;
252
+
253
+ let id=(f["ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ"]||"").replace(/\D/g,"");
254
+ let dev=(f["ุฑู‚ู… ุงู„ุฌู‡ุงุฒ"]||"").replace(/\D/g,"");
255
+ let phone=(f["ุฑู‚ู… ุงู„ุฌูˆุงู„"]||"").replace(/\D/g,"");
256
+
257
+ if(!isValidNationalId(id)) id="";
258
+ if(!isPhoneNumber(phone)) phone="";
259
+
260
+ if(!dev && isValidNationalId(id)) dev=id;
261
+ if(!dev && !id && phone) dev=phone;
262
+ if(!id && isValidNationalId(dev)) id=dev;
263
+
264
  return{
265
+ "ุงู„ุชุตู†ูŠู":cls,
266
+ "ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ":f["ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ"]||"",
267
+ "ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ":f["ูˆู‚ุช ุญุฏูˆุซ ุงู„ู…ุดูƒู„ุฉ"]||"",
268
+ "ุงุณู… ุตุงุญุจ ุงู„ู…ุดูƒู„ุฉ":f["ุงุณู… ุตุงุญุจ ุงู„ู…ุดูƒู„ุฉ"]||"",
269
+ "ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ":id,
270
+ "ุฑู‚ู… ุงู„ุฌู‡ุงุฒ":dev,
271
+ "ุฑู‚ู… ุงู„ุฌูˆุงู„":phone,
272
+ "ุงู„ู…ุณุญ":survey||region,
273
+ "ุงู„ู…ู†ุทู‚ุฉ":region,
274
+ "ุงุณู… ุงู„ุฏุนู… ุงู„ูู†ูŠ":agentName||"",
275
+ "ุงู„ุญุงู„ุฉ":"ุชู… ุงู„ุญู„",
276
+ "ุชุงุฑูŠุฎ ุงู„ูŠูˆู… ุจุงู„ู…ูŠู„ุงุฏูŠ": todayYMD()
277
  };
278
  });
279
  }
 
282
  const theadRow=document.getElementById("theadRow");
283
  const tbody=document.getElementById("tbody");
284
  theadRow.innerHTML="";
285
+ DISPLAY_COLUMNS.forEach(col=>{const th=document.createElement("th");th.textContent=col;theadRow.appendChild(th)});
286
  tbody.innerHTML="";
287
  rows.forEach(r=>{
288
  const tr=document.createElement("tr");
289
+ DISPLAY_COLUMNS.forEach(col=>{
290
  const td=document.createElement("td");
291
+ const base=DISPLAY_TO_BASE[col];
292
+ td.dataset.base=base;
293
+ let val=(col==="ุชุงุฑูŠุฎ ุงู„ูŠูˆู… ุจุงู„ู…ูŠู„ุงุฏูŠ")?(r["ุชุงุฑูŠุฎ ุงู„ูŠูˆู… ุจุงู„ู…ูŠู„ุงุฏูŠ"]||todayYMD()):(base?(r[base]||""):"");
294
  if(col==="ุงู„ุชุตู†ูŠู"){
295
  const span=document.createElement("span");
296
+ span.className=`cat ${catClass(val||"")}`;
297
+ span.textContent=val||"";
 
298
  td.appendChild(span);
299
  }else{
300
  td.contentEditable="true";
301
+ td.textContent=val;
302
  }
303
  tr.appendChild(td);
304
  });
 
311
  const rows=[];
312
  [...tbody.querySelectorAll("tr")].forEach(tr=>{
313
  const obj={};
314
+ [...tr.children].forEach(td=>{
315
+ const base=td.dataset.base;
316
+ if(!base)return;
317
+ if(base==="ุงู„ุชุตู†ูŠู"){
318
+ obj[base]=(td.querySelector("span")?.textContent||td.textContent||"").trim();
319
+ }else{
320
+ obj[base]=(td.textContent||"").trim();
321
+ }
322
  });
323
+ if(!obj["ุชุงุฑูŠุฎ ุงู„ูŠูˆู… ุจุงู„ู…ูŠู„ุงุฏูŠ"]) obj["ุชุงุฑูŠุฎ ุงู„ูŠูˆู… ุจุงู„ู…ูŠู„ุงุฏูŠ"]=todayYMD();
324
  rows.push(obj);
325
  });
326
  return rows;
 
328
 
329
  function updateBadge(n){
330
  const b=document.getElementById("countBadge");
331
+ if(!b)return;
332
  b.textContent=n;
333
  b.hidden=(n===0);
334
  }
 
339
 
340
  function validateCells(){
341
  const tbody=document.getElementById("tbody");
342
+ const required=new Set(["ู†ูˆุน ุงู„ู…ุดูƒู„ุฉ","ุงุณู… ุตุงุญุจ ุงู„ู…ุดูƒู„ุฉ","ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ","ุฑู‚ู… ุงู„ุฌู‡ุงุฒ","ุฑู‚ู… ุงู„ุฌูˆุงู„","ุงู„ู…ุณุญ","ุงู„ู…ู†ุทู‚ุฉ"]);
343
  let missing=0;
344
  [...tbody.rows].forEach(tr=>{
345
+ [...tr.children].forEach(td=>{
346
+ const base=td.dataset.base||"";
347
  const val=(td.textContent||"").trim();
348
  let invalid=false;
349
  let reason="";
350
+ if(required.has(base)&&!val){invalid=true;reason="required";missing++}
351
+ if(base==="ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ"){
352
  const digits=val.replace(/\D/g,"");
353
+ if(val && !/^[12]\d{9}$/.test(digits)){invalid=true;if(!reason)reason="id"}
354
  }
355
+ if(base==="ุฑู‚ู… ุงู„ุฌูˆุงู„"){
356
  const digits=val.replace(/\D/g,"");
357
+ if(val && !/^05\d{8}$/.test(digits)){invalid=true;if(!reason)reason="phone"}
358
  }
359
  td.classList.toggle("invalid",invalid);
360
  if(invalid){
361
+ const msg=reason==="required"?"ุงู„ุญู‚ู„ ู…ุทู„ูˆุจ":reason==="id"?"ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ูŠุฌุจ ุฃู† ูŠุจุฏุฃ ุจู€ 1 ุฃูˆ 2 ูˆุทูˆู„ู‡ 10 ุฎุงู†ุงุช":reason==="phone"?"ุฑู‚ู… ุงู„ุฌูˆุงู„ ูŠุฌุจ ุฃู† ูŠุจุฏุฃ ุจู€ 05 ูˆุทูˆู„ู‡ 10 ุฎุงู†ุงุช":"ู‚ูŠู…ุฉ ุบูŠุฑ ุตุญูŠุญุฉ";
362
  td.setAttribute("title",msg);
363
  td.dataset.reason=reason;
364
  }else{
 
381
 
382
  function toast(msg){
383
  const t=document.getElementById("toast");
384
+ if(!t)return;
385
  t.textContent=msg;
386
  t.hidden=false;
387
  t.classList.remove("show");void t.offsetWidth;t.classList.add("show");
 
436
  async function copyToClipboardTSV(){
437
  const rows=readTable();
438
  if(!rows.length){toast("ู„ุง ูŠูˆุฌุฏ ุจูŠุงู†ุงุช ู„ู†ุณุฎู‡ุง.");return}
439
+ const textCols=new Set(["ุฑู‚ู… ุงู„ู‡ูˆูŠุฉ ID","ุฑู‚ู… ุงู„ุฌู‡ุงุฒ","ุฑู‚ู… ุงู„ุฌูˆุงู„"]);
440
+ const body=rows.map(r=>{
441
+ return DISPLAY_COLUMNS.map(c=>{
442
+ const base=DISPLAY_TO_BASE[c];
443
+ let v=(base? (r[base]??"") : "").toString().replace(/\t/g," ");
444
+ if(textCols.has(c)&&v&&/^[0-9]+$/.test(v))v="'"+v;
445
+ return v;
446
+ }).join("\t");
447
+ }).join("\r\n");
448
+ const tsv="\uFEFF"+body;
449
+ try{await navigator.clipboard.writeText(tsv);toast("ุชู… ุงู„ู†ุณุฎ โ€” ุงู„ุตู‚/ูŠ ู…ุจุงุดุฑุฉ ููŠ Excel ุจุฏูˆู† ุนู†ุงูˆูŠู†.")}catch(e){
450
+ const ta=document.createElement("textarea");ta.value=tsv;document.body.appendChild(ta);ta.select();document.execCommand("copy");document.body.removeChild(ta);toast("ุชู… ุงู„ู†ุณุฎ โ€” ุงู„ุตู‚/ูŠ ู…ุจุงุดุฑุฉ ููŠ Excel ุจุฏูˆู† ุนู†ุงูˆูŠู†.")
451
  }
452
  }
453
 
 
460
  ุงู„ู…ุณุญ: ุงู„ุฎุจุฑ
461
  ุงู„ู…ู†ุทู‚ุฉ: ุงู„ุดุฑู‚ูŠุฉ`;
462
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  function saveState(){
464
  try{
465
  const raw=document.getElementById("raw")?.value||"";
466
  const agent=document.getElementById("agentName")?.value||"";
467
  const region=document.getElementById("regionDefault")?.value||"";
468
  const rows=readTable();
469
+ localStorage.setItem("ticketParserState_latest",JSON.stringify({raw,agent,region,rows,theme:document.body.classList.contains('dark')?'dark':'light'}));
470
  }catch{}
471
  }
472
 
473
  function loadState(){
474
  try{
475
+ const s=localStorage.getItem("ticketParserState_latest");
476
  if(!s)return false;
477
  let{raw,agent,region,rows,theme}=JSON.parse(s);
478
  const rawEl=document.getElementById("raw");if(typeof raw==="string"&&rawEl)rawEl.value=raw;
 
480
  const regionEl=document.getElementById("regionDefault");if(typeof region==="string"&&regionEl)regionEl.value=region;
481
  if(theme==='dark')document.body.classList.add('dark');
482
  updateThemeLabel();
 
483
  if(Array.isArray(rows)&&rows.length){buildTable(rows);validateCells();updateBadge(rows.length);setButtonsEnabled(true)}
484
  return true;
485
  }catch{return false}
 
496
  if(regionEl)regionEl.value="";
497
  updateBadge(0);setButtonsEnabled(false);
498
  document.getElementById("warn").hidden=true;
499
+ try{localStorage.removeItem("ticketParserState_latest")}catch{}
500
  toast("ุชู… ู…ุณุญ ูƒู„ ุงู„ุจูŠุงู†ุงุช ูˆุงู„ุชุฎุฒูŠู†.");
501
  }
502