v1.8.23: remove upload from homepage, restore single+bulk in library for admin/librarian only
This commit is contained in:
Binary file not shown.
@@ -117,7 +117,6 @@ header { animation:slideDown 350ms var(--ease-out) both; }
|
|||||||
|
|
||||||
/* Panels enter slightly later */
|
/* Panels enter slightly later */
|
||||||
.query-card { animation:slideLeft 450ms var(--ease-out) 300ms both; }
|
.query-card { animation:slideLeft 450ms var(--ease-out) 300ms both; }
|
||||||
.upload-card-wrap { animation:slideRight 450ms var(--ease-out) 340ms both; }
|
|
||||||
.books-card { animation:fadeUp 400ms var(--ease-out) 440ms both; }
|
.books-card { animation:fadeUp 400ms var(--ease-out) 440ms both; }
|
||||||
|
|
||||||
/* ── Stat card — hover only on pointer devices ───────────────────────────── */
|
/* ── Stat card — hover only on pointer devices ───────────────────────────── */
|
||||||
@@ -293,32 +292,6 @@ header { animation:slideDown 350ms var(--ease-out) both; }
|
|||||||
<div id="answer-meta" class="flex flex-wrap gap-2 mt-3"></div>
|
<div id="answer-meta" class="flex flex-wrap gap-2 mt-3"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Upload -->
|
|
||||||
<div class="upload-card-wrap">
|
|
||||||
<div class="glass rounded-3xl p-6 h-full" id="upload-card">
|
|
||||||
<h2 class="text-primary text-xs font-bold uppercase tracking-widest mb-4 flex items-center gap-2">
|
|
||||||
<span class="msym text-base">upload_file</span> رفع كتاب PDF
|
|
||||||
</h2>
|
|
||||||
<div class="upload-zone rounded-2xl p-8 text-center cursor-pointer relative mb-4" id="drop-zone">
|
|
||||||
<input type="file" accept=".pdf" multiple class="absolute inset-0 opacity-0 cursor-pointer w-full h-full" onchange="handleFiles(this.files)"/>
|
|
||||||
<div class="w-14 h-14 bg-primary/10 rounded-full flex items-center justify-center mx-auto mb-3 glow" style="transition:transform .3s">
|
|
||||||
<span class="msym text-primary text-3xl">cloud_upload</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-on-surface-variant text-sm">اسحب ملفات PDF وأفلتها هنا</p>
|
|
||||||
<p class="text-primary text-xs font-bold mt-1">أو انقر للاختيار</p>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<input type="text" id="url-input" dir="ltr" placeholder="https://example.com/book.pdf"
|
|
||||||
class="flex-1 bg-surface-high border border-outline-variant rounded-xl px-3 py-2 text-xs text-on-surface outline-none focus:border-primary transition-colors"/>
|
|
||||||
<button onclick="ingestURL()" class="bg-primary/10 border border-primary/30 text-primary px-3 py-2 rounded-xl text-xs font-bold hover:bg-primary/20 transition-colors flex items-center gap-1">
|
|
||||||
<span class="msym text-sm">add</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="upload-progress" class="hidden mt-3">
|
|
||||||
<div class="bg-surface-high rounded-full h-1.5 overflow-hidden">
|
|
||||||
<div id="progress-fill" class="h-1.5 bg-primary rounded-full transition-all duration-500" style="width:0%"></div>
|
|
||||||
</div>
|
|
||||||
<p id="progress-text" class="text-xs text-on-surface-variant mt-2"></p>
|
<p id="progress-text" class="text-xs text-on-surface-variant mt-2"></p>
|
||||||
</div>
|
</div>
|
||||||
<pre id="log-box" class="hidden mt-2 text-xs text-on-surface-variant bg-surface-high rounded-xl p-3 max-h-24 overflow-y-auto scrollbar font-mono"></pre>
|
<pre id="log-box" class="hidden mt-2 text-xs text-on-surface-variant bg-surface-high rounded-xl p-3 max-h-24 overflow-y-auto scrollbar font-mono"></pre>
|
||||||
@@ -485,51 +458,11 @@ async function doQuery() {
|
|||||||
btn.innerHTML = '<span class="msym text-lg">search</span> بحث';
|
btn.innerHTML = '<span class="msym text-lg">search</span> بحث';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Upload ────────────────────────────────────────────────────────────────────
|
|
||||||
async function handleFiles(files){for(const f of files)await uploadFile(f);loadStats();loadBooks();}
|
|
||||||
|
|
||||||
async function uploadFile(file){
|
|
||||||
const prog=document.getElementById('upload-progress'),fill=document.getElementById('progress-fill');
|
|
||||||
const txt=document.getElementById('progress-text'),log=document.getElementById('log-box');
|
|
||||||
prog.classList.remove('hidden');log.classList.remove('hidden');
|
|
||||||
txt.textContent='رفع: '+file.name;fill.style.width='30%';
|
|
||||||
const fd=new FormData();fd.append('file',file);
|
|
||||||
try{
|
|
||||||
fill.style.width='60%';
|
|
||||||
const headers={'X-API-Key':API_KEY};
|
|
||||||
if(userToken) headers['Authorization']='Bearer '+userToken;
|
|
||||||
const r=await fetch('/ingest',{method:'POST',headers,body:fd});
|
|
||||||
fill.style.width='100%';
|
|
||||||
const d=await r.json();
|
|
||||||
txt.textContent='✅ تم: '+file.name;log.textContent=d.log||'Done';
|
|
||||||
}catch(e){txt.textContent='❌ خطأ: '+e.message;}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ingestURL(){
|
|
||||||
const url=document.getElementById('url-input').value.trim();if(!url)return;
|
|
||||||
const prog=document.getElementById('upload-progress'),fill=document.getElementById('progress-fill');
|
|
||||||
const txt=document.getElementById('progress-text'),log=document.getElementById('log-box');
|
|
||||||
prog.classList.remove('hidden');log.classList.remove('hidden');
|
|
||||||
txt.textContent='جلب الملف...';fill.style.width='40%';
|
|
||||||
try{
|
|
||||||
fill.style.width='70%';
|
|
||||||
const headers={'Content-Type':'application/json','X-API-Key':API_KEY};
|
|
||||||
if(userToken) headers['Authorization']='Bearer '+userToken;
|
|
||||||
const r=await fetch('/ingest-url',{method:'POST',headers,body:JSON.stringify({url})});
|
|
||||||
fill.style.width='100%';
|
|
||||||
const d=await r.json();
|
|
||||||
txt.textContent='✅ تم: '+d.file;log.textContent=d.log||'Done';
|
|
||||||
document.getElementById('url-input').value='';
|
|
||||||
loadStats();loadBooks();
|
|
||||||
}catch(e){txt.textContent='❌ خطأ: '+e.message;}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Upload zone drag ─────────────────────────────────────────────────────────
|
|
||||||
const zone = document.getElementById('drop-zone');
|
|
||||||
const cloudIcon = zone.querySelector('.w-14');
|
|
||||||
zone.addEventListener('dragover',e=>{e.preventDefault();zone.classList.add('dragover');cloudIcon.style.transform='scale(1.15)';});
|
|
||||||
zone.addEventListener('dragleave',()=>{zone.classList.remove('dragover');cloudIcon.style.transform='';});
|
|
||||||
zone.addEventListener('drop',e=>{e.preventDefault();zone.classList.remove('dragover');cloudIcon.style.transform='';handleFiles(e.dataTransfer.files);});
|
|
||||||
|
|
||||||
// ── Keyboard shortcut ────────────────────────────────────────────────────────
|
// ── Keyboard shortcut ────────────────────────────────────────────────────────
|
||||||
document.getElementById('query-input').addEventListener('keydown',e=>{if(e.ctrlKey&&e.key==='Enter')doQuery();});
|
document.getElementById('query-input').addEventListener('keydown',e=>{if(e.ctrlKey&&e.key==='Enter')doQuery();});
|
||||||
|
|||||||
@@ -598,6 +598,25 @@ let uploadFile = null;
|
|||||||
let searchTimer = null;
|
let searchTimer = null;
|
||||||
let favIds = new Set();
|
let favIds = new Set();
|
||||||
|
|
||||||
|
// ── Auth init — populate currentUser and show upload for admin/librarian ──
|
||||||
|
(async function() {
|
||||||
|
const tok = localStorage.getItem('rag_token');
|
||||||
|
if (!tok) return;
|
||||||
|
try {
|
||||||
|
const r = await fetch('/auth/me', { headers: { 'Authorization': 'Bearer ' + tok } });
|
||||||
|
if (!r.ok) return;
|
||||||
|
currentUser = await r.json();
|
||||||
|
const role = currentUser.role || '';
|
||||||
|
const perms = currentUser.permissions || [];
|
||||||
|
const canUpload = perms.includes('upload') || role === 'admin' || role === 'librarian';
|
||||||
|
if (canUpload) {
|
||||||
|
const wrap = document.getElementById('upload-btn-wrap');
|
||||||
|
if (wrap) wrap.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
} catch(e) {}
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
async function loadFavIds() {
|
async function loadFavIds() {
|
||||||
if (!userToken) return;
|
if (!userToken) return;
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user