fix: cropper mime bug + loadSiteIdentity crash + logo|name header
CI/CD / CI · dotnet build (push) Successful in 37s
CI/CD / Deploy · drsousan (push) Successful in 29s

1. applyCrop() — mime variable was declared INSIDE toBlob callback
   but used as an argument to toBlob() (outer scope) → ReferenceError.
   Fix: declare _mime, _quality, _ext BEFORE out.toBlob() call.

2. loadSiteIdentity() — crashed when identity section had no rows
   (data was null/non-array). Fix: safe Array.isArray guard + catch.

3. Header logo: show logo image + | + site name side by side
   when logo is configured (was showing one OR the other).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-02 18:15:51 +03:30
parent 5d6a4a630d
commit 1e51df406b
2 changed files with 15 additions and 16 deletions
+11 -10
View File
@@ -1375,9 +1375,9 @@ async function deleteReq(id){if(!confirm('حذف؟'))return;await api(`/api/heal
// ── Comments ──────────────────────────────────────────────────────────────────
// ── Site Identity (logo / favicon) ────────────────────────────────────────────
async function loadSiteIdentity() {
const data = await api('/api/settings/identity') || [];
const data = await api('/api/settings/identity').catch(()=>[]) || [];
const vals = {};
data.forEach(s => vals[s.key] = s.value);
(Array.isArray(data) ? data : []).forEach(s => vals[s.key] = s.value);
// Set hidden inputs
document.getElementById('si-logo').value = vals.logo || '';
document.getElementById('si-favicon').value = vals.favicon || '';
@@ -1823,8 +1823,13 @@ 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;
// Capture targets and mime type BEFORE the async callback
const _inputId = cropper.inputId;
const _previewId = cropper.previewId;
const _mime = cropper.mimeType || 'image/jpeg';
const _quality = (_mime === 'image/jpeg' || _mime === 'image/webp') ? 0.92 : undefined;
const _ext = _mime.split('/')[1].replace('jpeg','jpg').replace('vnd.microsoft.icon','ico').replace('x-icon','ico').replace('svg+xml','svg');
out.toBlob(async blob => {
closeCropper();
const inputId = _inputId, previewId = _previewId;
@@ -1834,8 +1839,7 @@ async function applyCrop() {
btn.disabled = true;
try {
const fd = new FormData();
const ext = _inputId && cropper.mimeType ? cropper.mimeType.split('/')[1].replace('jpeg','jpg') : 'jpg';
fd.append('file', new File([blob], `crop.${ext}`, {type: cropper.mimeType || 'image/jpeg'}));
fd.append('file', new File([blob], `crop.${_ext}`, {type: _mime}));
const r = await fetch('/api/upload', {method:'POST', headers:{'Authorization':`Bearer ${token}`}, body:fd});
if (!r.ok) throw new Error(await r.text());
const { url } = await r.json();
@@ -1844,10 +1848,7 @@ async function applyCrop() {
toast('تصویر با موفقیت آپلود شد ✓');
} catch(e) { toast('خطا در آپلود: '+e.message,'error'); }
finally { btn.innerHTML=orig; btn.disabled=false; }
// Use original mime type; only pass quality for lossy formats
const mime = cropper.mimeType || 'image/jpeg';
const quality = (mime === 'image/jpeg' || mime === 'image/webp') ? 0.92 : undefined;
}, mime, quality);
}, _mime, _quality);
}
async function uploadImage(inputId, previewId) {