stat2025 commited on
Commit
4455dc1
·
verified ·
1 Parent(s): 269e393

Upload 6 files

Browse files
Files changed (4) hide show
  1. README.md +1 -0
  2. app.js +81 -35
  3. index.html +37 -0
  4. style.css +96 -0
README.md CHANGED
@@ -23,6 +23,7 @@ short_description: بوابة الباحث الميداني الشاملة
23
  - يتوفر دليل ثابت لممثلي المدن الصناعية مع نسخ الرقم والتواصل عبر واتساب.
24
  - تتضمن كل عينة نافذة مشاركة تطلب رقم جوال المستلم، وتعرض رسالة رسمية منظمة قبل إرسال رابط الاستمارة عبر واتساب.
25
  - واجهة الجوال مدمجة: أدوات سريعة في صف واحد، مدن وإحصاءات قابلة للسحب، ودليل ممثلين قابل للطي.
 
26
  - البيانات مشفرة داخل `data.js`، ورمز الدخول الافتراضي هو `20302030`.
27
 
28
  ## تحديث البيانات
 
23
  - يتوفر دليل ثابت لممثلي المدن الصناعية مع نسخ الرقم والتواصل عبر واتساب.
24
  - تتضمن كل عينة نافذة مشاركة تطلب رقم جوال المستلم، وتعرض رسالة رسمية منظمة قبل إرسال رابط الاستمارة عبر واتساب.
25
  - واجهة الجوال مدمجة: أدوات سريعة في صف واحد، مدن وإحصاءات قابلة للسحب، ودليل ممثلين قابل للطي.
26
+ - زر رسالة مسؤول مدن يفتح قائمة المسؤولين، ويختار مسؤول المدينة تلقائيًا مع إمكانية تغييره قبل الإرسال.
27
  - البيانات مشفرة داخل `data.js`، ورمز الدخول الافتراضي هو `20302030`.
28
 
29
  ## تحديث البيانات
app.js CHANGED
@@ -27,6 +27,8 @@ const elements = Object.fromEntries(
27
  "noResults", "loadMoreWrap", "loadMoreButton", "loadMoreMeta", "toast",
28
  "shareDialog", "closeShareDialog", "shareForm", "recipientPhone", "recipientPhoneError",
29
  "messagePreview", "copyShareMessage", "sendShareButton",
 
 
30
  ].map((id) => [id, document.getElementById(id)]),
31
  );
32
 
@@ -37,6 +39,8 @@ let visibleRows = [];
37
  let renderedCount = 0;
38
  let toastTimer;
39
  let shareRow = null;
 
 
40
  const mobileQuery = window.matchMedia("(max-width: 560px)");
41
 
42
  function base64ToBytes(value) {
@@ -300,20 +304,24 @@ function actionLink(label, href, icon) {
300
 
301
  function representativeMessage(representative, row = null) {
302
  const lines = [
303
- "السلام عليكم ورحمة الله وبركاته",
 
 
304
  "",
305
- `الأستاذ ${representative.name} المحترم،`,
306
- "نأمل تزويدنا بموقع المنشأة التالية، وذلك لاستكمال بيانات الزيارة الميدانية:",
307
  ];
308
  if (row) {
309
  lines.push(
310
  "",
311
- `المنشأة: ${row.establishmentName || "-"}`,
312
- `السجل التجاري: ${row.commercialRecord || "-"}`,
313
- `المدينة: ${row.city || "-"}`,
 
 
314
  );
 
315
  }
316
- lines.push("", "شاكرين لكم تعاونكم.");
317
  return lines.join("\n");
318
  }
319
 
@@ -468,21 +476,6 @@ function createMadonInfo(row) {
468
  return section;
469
  }
470
 
471
- function sampleMessage(row) {
472
- return [
473
- "السلام عليكم ورحمة الله وبركاته",
474
- "",
475
- "أستاذي الكريم،",
476
- "نأمل تزويدنا بموقع المنشأة التالية، وذلك لاستكمال بيانات الزيارة الميدانية:",
477
- "",
478
- `المنشأة: ${row.establishmentName || "-"}`,
479
- `السجل التجاري: ${row.commercialRecord || "-"}`,
480
- `المدينة: ${row.city || "-"}`,
481
- "",
482
- "شاكرين لكم تعاونكم.",
483
- ].join("\n");
484
- }
485
-
486
  function formMessage(row = null) {
487
  const lines = [
488
  "السلام عليكم ورحمة الله وبركاته،",
@@ -532,6 +525,50 @@ function closeShareDialog() {
532
  shareRow = null;
533
  }
534
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  function createCard(row, index) {
536
  const card = document.createElement("article");
537
  card.className = "sample-card";
@@ -578,20 +615,9 @@ function createCard(row, index) {
578
  const footer = document.createElement("div");
579
  footer.className = "card-actions";
580
  footer.append(
581
- actionButton("نسخ بيانات العينة", ICONS.copy, () => copyText(sampleMessage(row), "تم نسخ بيانات العينة")),
582
  actionButton("مشاركة الاستمارة", ICONS.whatsapp, () => openShareDialog(row)),
583
  );
584
- const representative = findRepresentative(row.representativeCity || row.city);
585
- if (representative && !row.coordinates) {
586
- const message = representativeMessage(representative, row);
587
- footer.append(
588
- actionLink(
589
- `مراسلة ${representative.name.split(" ")[0]}`,
590
- `https://wa.me/${internationalPhone(representative.phone)}?text=${encodeURIComponent(message)}`,
591
- ICONS.whatsapp,
592
- ),
593
- );
594
- }
595
  card.append(header, details, footer);
596
  return card;
597
  }
@@ -721,6 +747,26 @@ elements.shareForm.addEventListener("submit", (event) => {
721
  window.open(`https://wa.me/${phone}?text=${encodeURIComponent(message)}`, "_blank", "noopener");
722
  closeShareDialog();
723
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
724
  document.addEventListener("keydown", (event) => {
725
- if (event.key === "Escape" && !elements.shareDialog.hidden) closeShareDialog();
 
 
726
  });
 
27
  "noResults", "loadMoreWrap", "loadMoreButton", "loadMoreMeta", "toast",
28
  "shareDialog", "closeShareDialog", "shareForm", "recipientPhone", "recipientPhoneError",
29
  "messagePreview", "copyShareMessage", "sendShareButton",
30
+ "madonDialog", "closeMadonDialog", "madonForm", "madonRepresentativeOptions",
31
+ "madonMessagePreview", "copyMadonMessage", "sendMadonMessage",
32
  ].map((id) => [id, document.getElementById(id)]),
33
  );
34
 
 
39
  let renderedCount = 0;
40
  let toastTimer;
41
  let shareRow = null;
42
+ let madonRow = null;
43
+ let selectedRepresentative = null;
44
  const mobileQuery = window.matchMedia("(max-width: 560px)");
45
 
46
  function base64ToBytes(value) {
 
304
 
305
  function representativeMessage(representative, row = null) {
306
  const lines = [
307
+ "السلام عليكم ورحمة الله وبركاته،",
308
+ "",
309
+ `الأستاذ/ ${representative.name} المحترم،`,
310
  "",
311
+ "نأمل التكرم بإفادتنا عن المنشأة الموضحة أدناه، وتزويدنا بموقعها عند توفره، وذلك لاستكمال بيانات الزيارة الميدانية.",
 
312
  ];
313
  if (row) {
314
  lines.push(
315
  "",
316
+ "*بيانات المنشأة:*",
317
+ ` المنشأة: ${row.establishmentName || "-"}`,
318
+ `السجل التجاري: ${row.commercialRecord || "-"}`,
319
+ `• المدينة الصناعية: ${row.city || "-"}`,
320
+ `• حالة العينة: ${row.status || "-"}`,
321
  );
322
+ if (row.madonStatement) lines.push(`• إفادة مدن المسجلة: ${row.madonStatement}`);
323
  }
324
+ lines.push("", "شاكرين لكم تعاونكم وتجاوبكم.");
325
  return lines.join("\n");
326
  }
327
 
 
476
  return section;
477
  }
478
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
  function formMessage(row = null) {
480
  const lines = [
481
  "السلام عليكم ورحمة الله وبركاته،",
 
525
  shareRow = null;
526
  }
527
 
528
+ function renderMadonOptions(recommended) {
529
+ const fragment = document.createDocumentFragment();
530
+ REPRESENTATIVES.forEach((representative, index) => {
531
+ const label = document.createElement("label");
532
+ label.className = "representative-option";
533
+ const input = document.createElement("input");
534
+ input.type = "radio";
535
+ input.name = "madonRepresentative";
536
+ input.value = String(index);
537
+ input.checked = representative === recommended;
538
+ const content = document.createElement("span");
539
+ const name = document.createElement("strong");
540
+ name.textContent = representative.name;
541
+ const meta = document.createElement("small");
542
+ meta.textContent = `${representative.city} • ${representative.phone}`;
543
+ content.append(name, meta);
544
+ const check = createIcon("m5 12 4 4L19 6");
545
+ label.append(input, content, check);
546
+ input.addEventListener("change", () => {
547
+ selectedRepresentative = representative;
548
+ elements.madonMessagePreview.value = representativeMessage(representative, madonRow);
549
+ });
550
+ fragment.append(label);
551
+ });
552
+ elements.madonRepresentativeOptions.replaceChildren(fragment);
553
+ }
554
+
555
+ function openMadonDialog(row) {
556
+ madonRow = row;
557
+ selectedRepresentative =
558
+ findRepresentative(row.representativeCity || row.city) || REPRESENTATIVES[0];
559
+ renderMadonOptions(selectedRepresentative);
560
+ elements.madonMessagePreview.value = representativeMessage(selectedRepresentative, row);
561
+ elements.madonDialog.hidden = false;
562
+ document.body.classList.add("dialog-open");
563
+ }
564
+
565
+ function closeMadonDialog() {
566
+ elements.madonDialog.hidden = true;
567
+ document.body.classList.remove("dialog-open");
568
+ madonRow = null;
569
+ selectedRepresentative = null;
570
+ }
571
+
572
  function createCard(row, index) {
573
  const card = document.createElement("article");
574
  card.className = "sample-card";
 
615
  const footer = document.createElement("div");
616
  footer.className = "card-actions";
617
  footer.append(
618
+ actionButton("رسالة لمسؤول مدن", ICONS.whatsapp, () => openMadonDialog(row)),
619
  actionButton("مشاركة الاستمارة", ICONS.whatsapp, () => openShareDialog(row)),
620
  );
 
 
 
 
 
 
 
 
 
 
 
621
  card.append(header, details, footer);
622
  return card;
623
  }
 
747
  window.open(`https://wa.me/${phone}?text=${encodeURIComponent(message)}`, "_blank", "noopener");
748
  closeShareDialog();
749
  });
750
+ elements.closeMadonDialog.addEventListener("click", closeMadonDialog);
751
+ elements.madonDialog.addEventListener("click", (event) => {
752
+ if (event.target === elements.madonDialog) closeMadonDialog();
753
+ });
754
+ elements.copyMadonMessage.addEventListener("click", () =>
755
+ copyText(elements.madonMessagePreview.value, "تم نسخ رسالة مسؤول مدن"),
756
+ );
757
+ elements.madonForm.addEventListener("submit", (event) => {
758
+ event.preventDefault();
759
+ if (!selectedRepresentative) return;
760
+ const message = representativeMessage(selectedRepresentative, madonRow);
761
+ window.open(
762
+ `https://wa.me/${internationalPhone(selectedRepresentative.phone)}?text=${encodeURIComponent(message)}`,
763
+ "_blank",
764
+ "noopener",
765
+ );
766
+ closeMadonDialog();
767
+ });
768
  document.addEventListener("keydown", (event) => {
769
+ if (event.key !== "Escape") return;
770
+ if (!elements.shareDialog.hidden) closeShareDialog();
771
+ if (!elements.madonDialog.hidden) closeMadonDialog();
772
  });
index.html CHANGED
@@ -228,6 +228,43 @@
228
  </section>
229
  </div>
230
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  <script src="data.js"></script>
232
  <script src="app.js"></script>
233
  </body>
 
228
  </section>
229
  </div>
230
 
231
+ <div id="madonDialog" class="dialog-backdrop" hidden>
232
+ <section class="share-dialog madon-dialog" role="dialog" aria-modal="true" aria-labelledby="madonDialogTitle">
233
+ <div class="dialog-header">
234
+ <div>
235
+ <p class="eyebrow">التواصل مع مدن</p>
236
+ <h2 id="madonDialogTitle">اختيار مسؤول مدن</h2>
237
+ </div>
238
+ <button id="closeMadonDialog" class="icon-button" type="button" aria-label="إغلاق" title="إغلاق">
239
+ <svg viewBox="0 0 24 24"><path d="M18 6 6 18M6 6l12 12" /></svg>
240
+ </button>
241
+ </div>
242
+
243
+ <form id="madonForm" class="share-form">
244
+ <fieldset class="representative-picker">
245
+ <legend>اختر المسؤول الذي ستُرسل إليه الرسالة</legend>
246
+ <div id="madonRepresentativeOptions" class="representative-options"></div>
247
+ </fieldset>
248
+
249
+ <label class="dialog-field" for="madonMessagePreview">
250
+ <span>معاينة الرسالة</span>
251
+ <textarea id="madonMessagePreview" rows="11" readonly></textarea>
252
+ </label>
253
+
254
+ <div class="dialog-actions">
255
+ <button id="copyMadonMessage" class="secondary-button" type="button">
256
+ <svg viewBox="0 0 24 24"><path d="M8 8h11v11H8z M5 16H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" /></svg>
257
+ نسخ الرسالة
258
+ </button>
259
+ <button id="sendMadonMessage" class="whatsapp-button" type="submit">
260
+ <svg viewBox="0 0 24 24"><path d="M20.5 11.5a8.5 8.5 0 0 1-12.6 7.4L3 20l1.2-4.7A8.5 8.5 0 1 1 20.5 11.5Z M8.6 8.7c.2 3.3 2.7 5.8 6 6.2" /></svg>
261
+ إرسال للمسؤول
262
+ </button>
263
+ </div>
264
+ </form>
265
+ </section>
266
+ </div>
267
+
268
  <script src="data.js"></script>
269
  <script src="app.js"></script>
270
  </body>
style.css CHANGED
@@ -1221,6 +1221,90 @@ footer {
1221
  margin-top: 14px;
1222
  }
1223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1224
  .whatsapp-button {
1225
  min-height: 44px;
1226
  display: inline-flex;
@@ -1570,6 +1654,18 @@ select:focus-visible {
1570
  min-height: 220px;
1571
  }
1572
 
 
 
 
 
 
 
 
 
 
 
 
 
1573
  .dialog-actions {
1574
  display: grid;
1575
  grid-template-columns: 1fr;
 
1221
  margin-top: 14px;
1222
  }
1223
 
1224
+ .representative-picker {
1225
+ min-width: 0;
1226
+ margin: 0 0 16px;
1227
+ padding: 0;
1228
+ border: 0;
1229
+ }
1230
+
1231
+ .representative-picker legend {
1232
+ margin-bottom: 8px;
1233
+ color: #344d60;
1234
+ font-weight: 900;
1235
+ }
1236
+
1237
+ .representative-options {
1238
+ display: grid;
1239
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1240
+ gap: 8px;
1241
+ }
1242
+
1243
+ .representative-option {
1244
+ position: relative;
1245
+ min-width: 0;
1246
+ min-height: 70px;
1247
+ display: flex;
1248
+ align-items: center;
1249
+ gap: 10px;
1250
+ padding: 11px;
1251
+ background: #fbfdfe;
1252
+ border: 1px solid var(--line);
1253
+ border-radius: 8px;
1254
+ cursor: pointer;
1255
+ }
1256
+
1257
+ .representative-option input {
1258
+ position: absolute;
1259
+ opacity: 0;
1260
+ pointer-events: none;
1261
+ }
1262
+
1263
+ .representative-option > span {
1264
+ min-width: 0;
1265
+ flex: 1;
1266
+ }
1267
+
1268
+ .representative-option strong,
1269
+ .representative-option small {
1270
+ display: block;
1271
+ }
1272
+
1273
+ .representative-option small {
1274
+ margin-top: 3px;
1275
+ color: var(--muted);
1276
+ font-size: 11px;
1277
+ line-height: 1.55;
1278
+ }
1279
+
1280
+ .representative-option > svg {
1281
+ flex: 0 0 auto;
1282
+ color: transparent;
1283
+ }
1284
+
1285
+ .representative-option:has(input:checked) {
1286
+ background: rgba(0, 176, 80, 0.06);
1287
+ border-color: var(--green);
1288
+ box-shadow: 0 0 0 2px rgba(0, 176, 80, 0.08);
1289
+ }
1290
+
1291
+ .representative-option:has(input:checked) > svg {
1292
+ color: var(--green);
1293
+ }
1294
+
1295
+ #madonMessagePreview {
1296
+ width: 100%;
1297
+ min-height: 225px;
1298
+ resize: vertical;
1299
+ padding: 12px;
1300
+ color: var(--ink);
1301
+ background: #f8fafb;
1302
+ border: 1px solid var(--line);
1303
+ border-radius: 8px;
1304
+ outline: 0;
1305
+ line-height: 1.75;
1306
+ }
1307
+
1308
  .whatsapp-button {
1309
  min-height: 44px;
1310
  display: inline-flex;
 
1654
  min-height: 220px;
1655
  }
1656
 
1657
+ .representative-options {
1658
+ grid-template-columns: 1fr;
1659
+ }
1660
+
1661
+ .representative-option {
1662
+ min-height: 64px;
1663
+ }
1664
+
1665
+ #madonMessagePreview {
1666
+ min-height: 215px;
1667
+ }
1668
+
1669
  .dialog-actions {
1670
  display: grid;
1671
  grid-template-columns: 1fr;