fix: 3 bugs — beauty icon color, appointments on dashboard, blog image edit
1. Beauty category icon: was pink (#C2185B), now uses site primary gold
(var(--gold) / var(--gold-pale)) to match brand color
2. Dashboard now shows health requests:
- Two new stat cards: total patients + pending requests (clickable)
- 'آخرین درخواستها' mini-table showing last 6 requests
- Sidebar badge updates from dashboard load too
- loadDashboard() now fetches /api/patients + /api/health-requests
3. Blog image edit fix:
- applyCrop() now captures inputId/previewId BEFORE closeCropper()
to prevent any potential race condition when replacing images
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -179,7 +179,7 @@
|
||||
.health-cat-card { background:var(--white); border-radius:24px; padding:2.2rem; border:1px solid var(--border); display:flex; flex-direction:column; gap:1rem; }
|
||||
.health-cat-icon { width:60px; height:60px; border-radius:18px; display:flex; align-items:center; justify-content:center; }
|
||||
.health-cat-icon svg { width:28px; height:28px; }
|
||||
.health-cat-icon.beauty { background:#FCE4EC; color:#C2185B; }
|
||||
.health-cat-icon.beauty { background:var(--gold-pale); color:var(--gold); }
|
||||
.health-cat-icon.health { background:#E3F2FD; color:#1565C0; }
|
||||
.health-cat-card h3 { font-size:1.15rem; font-weight:700; color:var(--dark); }
|
||||
.health-cat-card p { font-size:.88rem; color:var(--mid); line-height:1.8; }
|
||||
|
||||
@@ -310,11 +310,22 @@ tr:hover td{background:#FAFBFC}
|
||||
<div class="stat-card"><div class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></div><div class="label">کل بازدید مقالات</div><div class="value" id="ds-views">-</div></div>
|
||||
<div class="stat-card"><div class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div><div class="label">نظرات بیماران</div><div class="value" id="ds-testimonials">-</div></div>
|
||||
<div class="stat-card"><div class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/></svg></div><div class="label">مقالات بدون متا</div><div class="value" id="ds-nometa" style="color:var(--danger)">-</div></div>
|
||||
<div class="stat-card" onclick="showPage('patients',document.getElementById('patientsNavItem'))" style="cursor:pointer"><div class="icon" style="color:#1565C0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg></div><div class="label">تعداد بیماران</div><div class="value" id="ds-patients">-</div></div>
|
||||
<div class="stat-card" onclick="showPage('healthrequests',document.getElementById('healthreqNavItem'))" style="cursor:pointer"><div class="icon" style="color:#E53935"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.69 13.6a19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 3.6 3h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L7.91 10.6a16 16 0 0 0 6 6l.91-.91a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg></div><div class="label">درخواستهای جدید</div><div class="value" id="ds-requests" style="color:var(--danger)">-</div></div>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1.2rem;margin-bottom:1.2rem">
|
||||
<div class="card">
|
||||
<div class="card-header"><div class="card-title">پربازدیدترین مقالات</div></div>
|
||||
<div class="table-wrap"><table><thead><tr><th>عنوان</th><th>بازدید</th><th>عملیات</th></tr></thead><tbody id="topPostsTable"></tbody></table></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">آخرین درخواستهای سلامت</div>
|
||||
<button class="btn btn-secondary btn-sm" onclick="showPage('healthrequests',document.getElementById('healthreqNavItem'))">مشاهده همه</button>
|
||||
</div>
|
||||
<div class="table-wrap"><table><thead><tr><th>نام</th><th>تلفن</th><th>دسته</th><th>وضعیت</th></tr></thead><tbody id="dashReqTable"></tbody></table></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── HERO ── -->
|
||||
@@ -1313,9 +1324,11 @@ async function sendReply(id) {
|
||||
|
||||
// ── Dashboard ─────────────────────────────────────────────────────────────────
|
||||
async function loadDashboard() {
|
||||
const [seo, testims] = await Promise.all([
|
||||
const [seo, testims, patients, reqs] = await Promise.all([
|
||||
api('/api/seo/stats'),
|
||||
api('/api/testimonials/all')
|
||||
api('/api/testimonials/all'),
|
||||
api('/api/patients'),
|
||||
api('/api/health-requests')
|
||||
]);
|
||||
if (seo) {
|
||||
document.getElementById('ds-posts').textContent = seo.total;
|
||||
@@ -1325,6 +1338,22 @@ async function loadDashboard() {
|
||||
tb.innerHTML = seo.topPosts.map(p=>`<tr><td>${p.title}</td><td><span class="badge badge-gold">${p.viewCount}</span></td><td><a href="/blog/${p.slug}" target="_blank" class="btn btn-secondary btn-sm">مشاهده</a></td></tr>`).join('');
|
||||
}
|
||||
if (testims) document.getElementById('ds-testimonials').textContent = testims.length;
|
||||
if (patients) document.getElementById('ds-patients').textContent = patients.length;
|
||||
if (reqs) {
|
||||
const pending = reqs.filter(r=>!r.isHandled);
|
||||
document.getElementById('ds-requests').textContent = pending.length;
|
||||
const catLabel = {beauty:'زیبایی پوست', health:'سلامت عمومی'};
|
||||
document.getElementById('dashReqTable').innerHTML = reqs.slice(0,6).map(r=>`
|
||||
<tr style="${!r.isHandled?'font-weight:600':'opacity:.65'}">
|
||||
<td>${r.fullName}</td>
|
||||
<td dir="ltr" style="font-size:.8rem">${r.phoneNumber}</td>
|
||||
<td style="font-size:.78rem">${catLabel[r.category]||r.category}</td>
|
||||
<td><span style="background:${r.isHandled?'#E8F5E9':'#FFEBEE'};color:${r.isHandled?'#388E3C':'#C62828'};padding:2px 8px;border-radius:20px;font-size:.7rem">${r.isHandled?'بررسی شده':'جدید'}</span></td>
|
||||
</tr>`).join('') || '<tr><td colspan="4" style="text-align:center;color:var(--light);padding:1rem">درخواستی وجود ندارد</td></tr>';
|
||||
// update sidebar badge
|
||||
const badge = document.getElementById('healthreqBadge');
|
||||
if(pending.length>0){badge.textContent=pending.length;badge.style.display='inline';}else{badge.style.display='none';}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Section settings ──────────────────────────────────────────────────────────
|
||||
@@ -1624,9 +1653,11 @@ async function applyCrop() {
|
||||
const out = document.createElement('canvas');
|
||||
out.width = Math.round(sw); out.height = Math.round(sh);
|
||||
out.getContext('2d').drawImage(cropper.img, sx, sy, sw, sh, 0, 0, out.width, out.height);
|
||||
// Capture targets BEFORE closing (closeCropper doesn't clear them, but be safe)
|
||||
const _inputId = cropper.inputId, _previewId = cropper.previewId;
|
||||
out.toBlob(async blob => {
|
||||
closeCropper();
|
||||
const inputId = cropper.inputId, previewId = cropper.previewId;
|
||||
const inputId = _inputId, previewId = _previewId;
|
||||
const btn = document.getElementById(`upload-btn-${inputId}`);
|
||||
const orig = btn.innerHTML;
|
||||
btn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="animation:spin .8s linear infinite"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg> در حال آپلود...';
|
||||
|
||||
Reference in New Issue
Block a user