feat(gallery+editor): dedicated /gallery page, homepage teaser, in-content images
Homepage gallery: - Show only 3 before/after samples as a teaser (was: all items) - Add "مشاهده گالری کامل (N نمونه)" CTA when more than 3 exist - Remove the now-pointless category tabs from the teaser New /gallery page: - Full before/after grid with category filter tabs (deduped from data) - Responsive cards with قبل/بعد labels + captions, empty state - Added to sitemap.xml (priority 0.8) Blog content editor: - New 🖼 تصویر toolbar button inserts an uploaded image at the cursor (direct upload, no forced crop) — for richer post bodies - Responsive img styling on the public post page Note: the filler-lab-soorat cover not showing is a data issue — that post has an empty featuredImage in the DB (verified); re-upload + save fixes it. The upload/save path itself is correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1027,6 +1027,7 @@ tr:hover td{background:#FAFBFC}
|
||||
<button onclick="fmt('insertUnorderedList')">• لیست</button>
|
||||
<button onclick="fmt('insertOrderedList')">۱. لیست</button>
|
||||
<button onclick="insLink()">🔗 لینک</button>
|
||||
<button onclick="insImage()" title="درج تصویر در متن">🖼 تصویر</button>
|
||||
</div>
|
||||
<div class="editor-content" id="post-content" contenteditable="true" dir="rtl"></div>
|
||||
</div>
|
||||
@@ -2244,6 +2245,31 @@ function checkSeo(){
|
||||
function fmt(cmd){document.getElementById('post-content').focus();document.execCommand(cmd);}
|
||||
function fmtBlock(tag){document.getElementById('post-content').focus();document.execCommand('formatBlock',false,tag);}
|
||||
function insLink(){const url=prompt('آدرس لینک:');if(url){document.getElementById('post-content').focus();document.execCommand('createLink',false,url);}}
|
||||
// Insert an image into the post content at the cursor (uploads directly, no forced crop)
|
||||
function insImage(){
|
||||
const editor=document.getElementById('post-content');
|
||||
editor.focus();
|
||||
const sel=window.getSelection();
|
||||
const savedRange=sel.rangeCount?sel.getRangeAt(0):null;
|
||||
const fileInput=document.createElement('input');
|
||||
fileInput.type='file';
|
||||
fileInput.accept='image/jpeg,image/png,image/webp,image/gif';
|
||||
fileInput.onchange=async()=>{
|
||||
const file=fileInput.files[0];if(!file)return;
|
||||
toast('در حال آپلود تصویر...');
|
||||
try{
|
||||
const fd=new FormData();fd.append('file',file);
|
||||
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();
|
||||
editor.focus();
|
||||
if(savedRange){sel.removeAllRanges();sel.addRange(savedRange);}
|
||||
document.execCommand('insertHTML',false,`<img src="${url}" alt="" style="max-width:100%;height:auto;border-radius:12px;margin:1rem 0;display:block"/><p><br></p>`);
|
||||
toast('تصویر در متن درج شد ✓');
|
||||
}catch(e){toast('خطا در آپلود: '+e.message,'error');}
|
||||
};
|
||||
fileInput.click();
|
||||
}
|
||||
|
||||
// ── Modal helpers ─────────────────────────────────────────────────────────────
|
||||
function closeModal(id){document.getElementById(id).classList.add('hidden');}
|
||||
|
||||
Reference in New Issue
Block a user