Upload 3 files
Browse files- app.js +44 -2
- index.html +1 -0
- 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);
|