stat2025 commited on
Commit
e457b9b
·
verified ·
1 Parent(s): 6b7c137

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.js +44 -2
  2. index.html +1 -0
  3. style.css +68 -0
app.js CHANGED
@@ -38,7 +38,7 @@ const elements = Object.fromEntries(
38
  "documentationUnavailable", "documentationForm", "fieldStatus", "otherStatusField", "otherStatus", "fieldStatement",
39
  "statementCount", "documentationPhotos", "photoPreviews", "documentationError",
40
  "uploadProgress", "uploadProgressBar", "cancelDocumentationButton", "saveDocumentationButton",
41
- "adminView", "adminLogoutButton", "adminUpdatedAt", "refreshAdminButton", "adminStats",
42
  "adminProgressOverview", "adminTrendTotal", "adminTrendChart", "adminCityProgress",
43
  "adminControls", "adminSearch", "adminResearcherFilter", "adminCityFilter", "adminDateFilter",
44
  "adminStatusFilter", "clearAdminFilters", "activeResearchersCount",
@@ -299,6 +299,33 @@ function renderAdminStats() {
299
  adminStat(snapshot.yesterdayRecords.length, "إنجاز أمس"),
300
  adminStat(snapshot.activeToday.size, "باحثون أنجزوا اليوم", `${inactiveToday} دون إنجاز اليوم`),
301
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  }
303
 
304
  function renderAdminOverview() {
@@ -383,6 +410,10 @@ function filteredAdminRecords() {
383
  return normalize(
384
  `${record.establishmentName} ${record.commercialRecord} ${record.researcher} ${record.city}`,
385
  ).includes(query);
 
 
 
 
386
  });
387
  }
388
 
@@ -474,11 +505,22 @@ function renderAdminRecords() {
474
  heading.append(title, time);
475
  const badges = document.createElement("div");
476
  badges.className = "admin-record-badges";
 
 
 
477
  const statusBadge = document.createElement("span");
478
  statusBadge.textContent = record.fieldStatus || "غير محدد";
479
  const photoBadge = document.createElement("span");
480
  photoBadge.textContent = record.photoCount ? `${record.photoCount} صور` : "بدون صور";
481
- badges.append(statusBadge, photoBadge);
 
 
 
 
 
 
 
 
482
  const details = document.createElement("div");
483
  details.className = "admin-record-details";
484
  details.append(
 
38
  "documentationUnavailable", "documentationForm", "fieldStatus", "otherStatusField", "otherStatus", "fieldStatement",
39
  "statementCount", "documentationPhotos", "photoPreviews", "documentationError",
40
  "uploadProgress", "uploadProgressBar", "cancelDocumentationButton", "saveDocumentationButton",
41
+ "adminView", "adminLogoutButton", "adminUpdatedAt", "refreshAdminButton", "adminStats", "adminInsight",
42
  "adminProgressOverview", "adminTrendTotal", "adminTrendChart", "adminCityProgress",
43
  "adminControls", "adminSearch", "adminResearcherFilter", "adminCityFilter", "adminDateFilter",
44
  "adminStatusFilter", "clearAdminFilters", "activeResearchersCount",
 
299
  adminStat(snapshot.yesterdayRecords.length, "إنجاز أمس"),
300
  adminStat(snapshot.activeToday.size, "باحثون أنجزوا اليوم", `${inactiveToday} دون إنجاز اليوم`),
301
  );
302
+ renderAdminInsight(snapshot, inactiveToday);
303
+ }
304
+
305
+ function renderAdminInsight(snapshot, inactiveToday) {
306
+ const todayByResearcher = new Map();
307
+ snapshot.todayRecords.forEach((record) => {
308
+ if (!record.researcher) return;
309
+ todayByResearcher.set(record.researcher, (todayByResearcher.get(record.researcher) || 0) + 1);
310
+ });
311
+ const leader = [...todayByResearcher.entries()].sort((a, b) => b[1] - a[1])[0];
312
+ const difference = snapshot.todayRecords.length - snapshot.yesterdayRecords.length;
313
+ const trendText = difference > 0
314
+ ? `إنجاز اليوم أعلى من أمس بـ ${difference}`
315
+ : difference < 0
316
+ ? `إنجاز اليوم أقل من أمس بـ ${Math.abs(difference)}`
317
+ : "إنجاز اليوم مماثل لأمس";
318
+ const leaderText = leader
319
+ ? `الأعلى اليوم: ${leader[0]} بعدد ${leader[1]}`
320
+ : "لم يسجل أي إنجاز اليوم حتى الآن";
321
+
322
+ elements.adminInsight.className = `admin-insight ${difference < 0 ? "needs-attention" : "positive"}`;
323
+ elements.adminInsight.innerHTML = `
324
+ <span class="admin-insight-icon">${difference < 0 ? "!" : "✓"}</span>
325
+ <div>
326
+ <strong>${trendText}</strong>
327
+ <p>${leaderText} · ${inactiveToday} باحث دون إنجاز اليوم.</p>
328
+ </div>`;
329
  }
330
 
331
  function renderAdminOverview() {
 
410
  return normalize(
411
  `${record.establishmentName} ${record.commercialRecord} ${record.researcher} ${record.city}`,
412
  ).includes(query);
413
+ }).sort((a, b) => {
414
+ const aTime = Date.parse(a.updatedAt || a.documentedAt || "") || 0;
415
+ const bTime = Date.parse(b.updatedAt || b.documentedAt || "") || 0;
416
+ return bTime - aTime;
417
  });
418
  }
419
 
 
505
  heading.append(title, time);
506
  const badges = document.createElement("div");
507
  badges.className = "admin-record-badges";
508
+ const documentedBadge = document.createElement("span");
509
+ documentedBadge.className = "admin-badge-documented";
510
+ documentedBadge.textContent = record.documentedDate === riyadhToday() ? "موثق اليوم" : "موثق";
511
  const statusBadge = document.createElement("span");
512
  statusBadge.textContent = record.fieldStatus || "غير محدد";
513
  const photoBadge = document.createElement("span");
514
  photoBadge.textContent = record.photoCount ? `${record.photoCount} صور` : "بدون صور";
515
+ badges.append(documentedBadge, statusBadge, photoBadge);
516
+ const documentedTime = Date.parse(record.documentedAt || "") || 0;
517
+ const updatedTime = Date.parse(record.updatedAt || "") || 0;
518
+ if (updatedTime && documentedTime && updatedTime - documentedTime > 60000) {
519
+ const editedBadge = document.createElement("span");
520
+ editedBadge.className = "admin-badge-edited";
521
+ editedBadge.textContent = "تم التعديل";
522
+ badges.append(editedBadge);
523
+ }
524
  const details = document.createElement("div");
525
  details.className = "admin-record-details";
526
  details.append(
index.html CHANGED
@@ -212,6 +212,7 @@
212
  </section>
213
 
214
  <section id="adminStats" class="admin-stats"></section>
 
215
 
216
  <section class="admin-overview-grid">
217
  <article class="admin-panel progress-panel">
 
212
  </section>
213
 
214
  <section id="adminStats" class="admin-stats"></section>
215
+ <section id="adminInsight" class="admin-insight" aria-live="polite"></section>
216
 
217
  <section class="admin-overview-grid">
218
  <article class="admin-panel progress-panel">
style.css CHANGED
@@ -2447,6 +2447,62 @@ body {
2447
  color: #7a8797;
2448
  }
2449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2450
  .admin-overview-grid {
2451
  display: grid;
2452
  grid-template-columns: minmax(250px, 0.75fr) minmax(360px, 1.25fr) minmax(320px, 1fr);
@@ -2931,6 +2987,18 @@ body {
2931
  font-weight: 900;
2932
  }
2933
 
 
 
 
 
 
 
 
 
 
 
 
 
2934
  @media (max-width: 980px) {
2935
  .admin-stats {
2936
  grid-template-columns: repeat(3, 1fr);
 
2447
  color: #7a8797;
2448
  }
2449
 
2450
+ .admin-insight {
2451
+ min-height: 68px;
2452
+ display: flex;
2453
+ align-items: center;
2454
+ gap: 12px;
2455
+ margin: -8px 0 18px;
2456
+ padding: 12px 15px;
2457
+ border: 1px solid #bfe7cf;
2458
+ border-right: 4px solid #00b050;
2459
+ border-radius: 6px;
2460
+ background: #f4fff8;
2461
+ }
2462
+
2463
+ .admin-insight.needs-attention {
2464
+ border-color: #f4d38a;
2465
+ border-right-color: #ffbc00;
2466
+ background: #fffaf0;
2467
+ }
2468
+
2469
+ .admin-insight-icon {
2470
+ width: 34px;
2471
+ height: 34px;
2472
+ flex: 0 0 auto;
2473
+ display: grid;
2474
+ place-items: center;
2475
+ color: #ffffff;
2476
+ background: #00b050;
2477
+ border-radius: 50%;
2478
+ font-weight: 900;
2479
+ }
2480
+
2481
+ .admin-insight.needs-attention .admin-insight-icon {
2482
+ color: #533d00;
2483
+ background: #ffbc00;
2484
+ }
2485
+
2486
+ .admin-insight strong,
2487
+ .admin-insight p {
2488
+ display: block;
2489
+ margin: 0;
2490
+ }
2491
+
2492
+ .admin-insight strong {
2493
+ color: #176b3a;
2494
+ }
2495
+
2496
+ .admin-insight.needs-attention strong {
2497
+ color: #775900;
2498
+ }
2499
+
2500
+ .admin-insight p {
2501
+ margin-top: 3px;
2502
+ color: #526174;
2503
+ font-size: 12px;
2504
+ }
2505
+
2506
  .admin-overview-grid {
2507
  display: grid;
2508
  grid-template-columns: minmax(250px, 0.75fr) minmax(360px, 1.25fr) minmax(320px, 1fr);
 
2987
  font-weight: 900;
2988
  }
2989
 
2990
+ .admin-record-badges .admin-badge-documented {
2991
+ color: #08783b;
2992
+ background: #dcfce7;
2993
+ border-color: #86efac;
2994
+ }
2995
+
2996
+ .admin-record-badges .admin-badge-edited {
2997
+ color: #4f2683;
2998
+ background: #f3e8ff;
2999
+ border-color: #d8b4fe;
3000
+ }
3001
+
3002
  @media (max-width: 980px) {
3003
  .admin-stats {
3004
  grid-template-columns: repeat(3, 1fr);