Spaces:
Paused
Paused
<html lang="fa" dir="rtl"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>آپلود فایل | سامانه حقوقی</title> | |
<link rel="preconnect" href="https://fonts.googleapis.com"> | |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@200;300;400;500;600;700;800;900&display=swap" rel="stylesheet"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> | |
<!-- Load API Client --> | |
<script src="/static/js/api-client.js"></script> | |
<script src="/static/js/core.js"></script> | |
<style> | |
/* استفاده از همان CSS از upload.html قبلی */ | |
:root { | |
--text-primary: #0f172a; | |
--text-secondary: #475569; | |
--text-muted: #64748b; | |
--text-light: #ffffff; | |
--body-bg: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 50%, #cbd5e1 100%); | |
--card-bg: rgba(255, 255, 255, 0.95); | |
--glass-bg: rgba(255, 255, 255, 0.9); | |
--glass-border: rgba(148, 163, 184, 0.2); | |
--primary-gradient: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); | |
--secondary-gradient: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%); | |
--success-gradient: linear-gradient(135deg, #10b981 0%, #047857 100%); | |
--warning-gradient: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); | |
--danger-gradient: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); | |
--shadow-xs: 0 1px 3px rgba(0, 0, 0, 0.05); | |
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08); | |
--shadow-md: 0 4px 15px rgba(0, 0, 0, 0.1); | |
--shadow-lg: 0 8px 25px rgba(0, 0, 0, 0.12); | |
--sidebar-width: 260px; | |
--border-radius: 12px; | |
--border-radius-sm: 8px; | |
--transition-smooth: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); | |
--transition-fast: all 0.15s ease-in-out; | |
--font-size-xs: 0.7rem; | |
--font-size-sm: 0.8rem; | |
--font-size-base: 0.9rem; | |
--font-size-lg: 1.1rem; | |
--font-size-xl: 1.25rem; | |
--font-size-2xl: 1.5rem; | |
} | |
/* Simplified CSS - using key styles only */ | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: 'Vazirmatn', -apple-system, BlinkMacSystemFont, sans-serif; | |
background: var(--body-bg); | |
color: var(--text-primary); | |
line-height: 1.6; | |
overflow-x: hidden; | |
font-size: var(--font-size-base); | |
} | |
.dashboard-container { | |
display: flex; | |
min-block-size: 100vh; | |
inline-size: 100%; | |
} | |
/* سایدبار کامپکت */ | |
.sidebar { | |
inline-size: var(--sidebar-width); | |
background: linear-gradient(135deg, | |
rgba(248, 250, 252, 0.98) 0%, | |
rgba(241, 245, 249, 0.95) 25%, | |
rgba(226, 232, 240, 0.98) 50%, | |
rgba(203, 213, 225, 0.95) 75%, | |
rgba(148, 163, 184, 0.1) 100%); | |
backdrop-filter: blur(25px); | |
padding: 1rem 0; | |
position: fixed; | |
block-size: 100vh; | |
inset-inline-end: 0; | |
inset-block-start: 0; | |
z-index: 1000; | |
overflow-y: auto; | |
box-shadow: -8px 0 32px rgba(59, 130, 246, 0.12); | |
border-inline-start: 1px solid rgba(59, 130, 246, 0.15); | |
} | |
.sidebar-header { | |
padding: 0 1rem 1rem; | |
border-block-end: 1px solid rgba(59, 130, 246, 0.12); | |
margin-block-end: 1rem; | |
display: flex; | |
align-items: center; | |
background: linear-gradient(135deg, | |
rgba(255, 255, 255, 0.4) 0%, | |
rgba(248, 250, 252, 0.2) 100%); | |
margin: 0 0.5rem 1rem; | |
border-radius: var(--border-radius); | |
backdrop-filter: blur(10px); | |
} | |
.logo { | |
display: flex; | |
align-items: center; | |
gap: 0.6rem; | |
color: var(--text-primary); | |
text-decoration: none; | |
} | |
.logo-icon { | |
inline-size: 2rem; | |
block-size: 2rem; | |
background: var(--primary-gradient); | |
border-radius: var(--border-radius-sm); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
font-size: 1rem; | |
color: white; | |
} | |
.logo-text { | |
font-size: var(--font-size-lg); | |
font-weight: 700; | |
background: var(--primary-gradient); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
} | |
.nav-section { | |
margin-block-end: 1rem; | |
} | |
.nav-title { | |
padding: 0 1rem 0.4rem; | |
font-size: var(--font-size-xs); | |
font-weight: 600; | |
text-transform: uppercase; | |
letter-spacing: 0.5px; | |
color: var(--text-secondary); | |
} | |
.nav-menu { | |
list-style: none; | |
} | |
.nav-item { | |
margin: 0.15rem 0.5rem; | |
} | |
.nav-link { | |
display: flex; | |
align-items: center; | |
padding: 0.6rem 0.8rem; | |
color: var(--text-primary); | |
text-decoration: none; | |
border-radius: var(--border-radius-sm); | |
transition: var(--transition-smooth); | |
font-weight: 500; | |
font-size: var(--font-size-sm); | |
cursor: pointer; | |
border: 1px solid transparent; | |
} | |
.nav-link:hover { | |
transform: translateX(-2px); | |
border-color: rgba(59, 130, 246, 0.15); | |
background: rgba(59, 130, 246, 0.05); | |
} | |
.nav-link.active { | |
background: var(--primary-gradient); | |
color: var(--text-light); | |
box-shadow: var(--shadow-md); | |
} | |
.nav-icon { | |
margin-inline-start: 0.6rem; | |
inline-size: 1rem; | |
text-align: center; | |
font-size: 0.9rem; | |
} | |
.nav-badge { | |
background: var(--danger-gradient); | |
color: white; | |
padding: 0.15rem 0.4rem; | |
border-radius: 10px; | |
font-size: var(--font-size-xs); | |
font-weight: 600; | |
margin-inline-end: auto; | |
min-inline-size: 1.2rem; | |
text-align: center; | |
} | |
/* محتوای اصلی */ | |
.main-content { | |
flex: 1; | |
margin-inline-end: var(--sidebar-width); | |
padding: 1rem; | |
min-block-size: 100vh; | |
inline-size: calc(100% - var(--sidebar-width)); | |
} | |
.page-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-block-end: 2rem; | |
padding: 1rem 0; | |
border-block-end: 1px solid rgba(0, 0, 0, 0.1); | |
} | |
.page-title { | |
font-size: var(--font-size-2xl); | |
font-weight: 800; | |
background: var(--primary-gradient); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
display: flex; | |
align-items: center; | |
gap: 0.6rem; | |
} | |
.page-actions { | |
display: flex; | |
gap: 0.8rem; | |
} | |
.btn { | |
padding: 0.6rem 1.2rem; | |
border: none; | |
border-radius: var(--border-radius-sm); | |
font-family: inherit; | |
font-weight: 600; | |
cursor: pointer; | |
transition: var(--transition-smooth); | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
text-decoration: none; | |
font-size: var(--font-size-sm); | |
} | |
.btn-outline { | |
background: transparent; | |
color: var(--text-primary); | |
border: 1px solid rgba(59, 130, 246, 0.2); | |
} | |
.btn-outline:hover { | |
background: rgba(59, 130, 246, 0.05); | |
border-color: rgba(59, 130, 246, 0.4); | |
} | |
/* آپلود فایل */ | |
.upload-section { | |
background: var(--card-bg); | |
backdrop-filter: blur(10px); | |
border-radius: var(--border-radius); | |
padding: 2rem; | |
margin-block-end: 2rem; | |
box-shadow: var(--shadow-md); | |
border: 1px solid rgba(255, 255, 255, 0.3); | |
} | |
.upload-area { | |
border: 3px dashed rgba(59, 130, 246, 0.3); | |
border-radius: var(--border-radius); | |
padding: 3rem; | |
text-align: center; | |
background: linear-gradient(135deg, rgba(59, 130, 246, 0.02), rgba(255, 255, 255, 0.1)); | |
transition: var(--transition-smooth); | |
cursor: pointer; | |
position: relative; | |
} | |
.upload-area:hover, | |
.upload-area.dragover { | |
border-color: rgba(59, 130, 246, 0.6); | |
background: linear-gradient(135deg, rgba(59, 130, 246, 0.05), rgba(255, 255, 255, 0.15)); | |
transform: scale(1.01); | |
box-shadow: var(--shadow-lg); | |
} | |
.upload-icon { | |
font-size: 4rem; | |
color: rgba(59, 130, 246, 0.6); | |
margin-bottom: 1.5rem; | |
transition: var(--transition-smooth); | |
} | |
.upload-area:hover .upload-icon { | |
color: rgba(59, 130, 246, 0.8); | |
transform: scale(1.1); | |
} | |
.upload-title { | |
font-size: var(--font-size-xl); | |
font-weight: 700; | |
color: var(--text-primary); | |
margin-bottom: 0.8rem; | |
} | |
.upload-description { | |
color: var(--text-secondary); | |
margin-bottom: 1.5rem; | |
font-size: var(--font-size-base); | |
line-height: 1.6; | |
} | |
.upload-input { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
opacity: 0; | |
cursor: pointer; | |
} | |
.upload-btn { | |
background: var(--primary-gradient); | |
color: white; | |
border: none; | |
padding: 0.8rem 2rem; | |
border-radius: var(--border-radius-sm); | |
font-weight: 600; | |
cursor: pointer; | |
transition: var(--transition-smooth); | |
font-size: var(--font-size-base); | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
margin: 0 auto; | |
box-shadow: var(--shadow-sm); | |
} | |
.upload-btn:hover { | |
box-shadow: var(--shadow-lg); | |
transform: translateY(-2px); | |
} | |
/* فایلهای آپلود شده */ | |
.upload-files { | |
margin-top: 2rem; | |
display: none; | |
} | |
.upload-files.has-files { | |
display: block; | |
} | |
.files-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 1rem; | |
padding-bottom: 0.8rem; | |
border-bottom: 1px solid rgba(0, 0, 0, 0.1); | |
} | |
.files-title { | |
font-size: var(--font-size-lg); | |
font-weight: 700; | |
color: var(--text-primary); | |
} | |
.files-actions { | |
display: flex; | |
gap: 0.6rem; | |
} | |
.btn-sm { | |
padding: 0.4rem 0.8rem; | |
font-size: var(--font-size-xs); | |
} | |
.btn-success { | |
background: var(--success-gradient); | |
color: white; | |
} | |
.btn-danger { | |
background: var(--danger-gradient); | |
color: white; | |
} | |
.file-item { | |
display: flex; | |
align-items: center; | |
gap: 1rem; | |
padding: 1rem; | |
background: rgba(255, 255, 255, 0.7); | |
border-radius: var(--border-radius-sm); | |
margin-bottom: 0.8rem; | |
border: 1px solid rgba(0, 0, 0, 0.05); | |
transition: var(--transition-smooth); | |
position: relative; | |
overflow: hidden; | |
} | |
.file-item::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
bottom: 0; | |
width: 4px; | |
background: var(--primary-gradient); | |
} | |
.file-item:hover { | |
background: rgba(255, 255, 255, 0.9); | |
transform: translateX(-3px); | |
box-shadow: var(--shadow-sm); | |
} | |
.file-icon { | |
width: 3rem; | |
height: 3rem; | |
background: var(--danger-gradient); | |
border-radius: var(--border-radius-sm); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: white; | |
font-size: 1.2rem; | |
flex-shrink: 0; | |
box-shadow: var(--shadow-sm); | |
} | |
.file-info { | |
flex: 1; | |
} | |
.file-name { | |
font-weight: 600; | |
color: var(--text-primary); | |
font-size: var(--font-size-base); | |
margin-bottom: 0.3rem; | |
} | |
.file-details { | |
display: flex; | |
gap: 1rem; | |
font-size: var(--font-size-sm); | |
color: var(--text-secondary); | |
} | |
.file-progress { | |
width: 100%; | |
height: 6px; | |
background: rgba(0, 0, 0, 0.08); | |
border-radius: 3px; | |
margin-top: 0.8rem; | |
overflow: hidden; | |
} | |
.file-progress-bar { | |
height: 100%; | |
background: var(--success-gradient); | |
border-radius: 3px; | |
transition: width 0.3s ease; | |
position: relative; | |
} | |
.file-progress-bar::after { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); | |
animation: shimmer 2s infinite; | |
} | |
@keyframes shimmer { | |
0% { transform: translateX(-100%); } | |
100% { transform: translateX(100%); } | |
} | |
.file-status { | |
font-size: var(--font-size-xs); | |
font-weight: 600; | |
padding: 0.3rem 0.8rem; | |
border-radius: 15px; | |
display: flex; | |
align-items: center; | |
gap: 0.3rem; | |
white-space: nowrap; | |
} | |
.file-status.uploading { | |
background: rgba(245, 158, 11, 0.1); | |
color: #b45309; | |
} | |
.file-status.processing { | |
background: rgba(59, 130, 246, 0.1); | |
color: #1d4ed8; | |
} | |
.file-status.success { | |
background: rgba(16, 185, 129, 0.1); | |
color: #047857; | |
} | |
.file-status.error { | |
background: rgba(239, 68, 68, 0.1); | |
color: #b91c1c; | |
} | |
.file-actions { | |
display: flex; | |
gap: 0.4rem; | |
flex-shrink: 0; | |
} | |
.action-btn { | |
width: 2rem; | |
height: 2rem; | |
border: none; | |
border-radius: var(--border-radius-sm); | |
cursor: pointer; | |
transition: var(--transition-fast); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
font-size: var(--font-size-xs); | |
} | |
.action-btn.retry { | |
background: rgba(245, 158, 11, 0.1); | |
color: #f59e0b; | |
} | |
.action-btn.remove { | |
background: rgba(239, 68, 68, 0.1); | |
color: #ef4444; | |
} | |
.action-btn:hover { | |
transform: scale(1.1); | |
} | |
/* Toast Notifications */ | |
.toast-container { | |
position: fixed; | |
top: 1rem; | |
left: 1rem; | |
z-index: 10001; | |
display: flex; | |
flex-direction: column; | |
gap: 0.5rem; | |
} | |
.toast { | |
background: var(--card-bg); | |
border-radius: var(--border-radius-sm); | |
padding: 1rem 1.5rem; | |
box-shadow: var(--shadow-lg); | |
border-left: 4px solid; | |
display: flex; | |
align-items: center; | |
gap: 0.8rem; | |
min-width: 300px; | |
transform: translateX(-100%); | |
transition: all 0.3s ease; | |
} | |
.toast.show { | |
transform: translateX(0); | |
} | |
.toast.success { border-left-color: #10b981; } | |
.toast.error { border-left-color: #ef4444; } | |
.toast.warning { border-left-color: #f59e0b; } | |
.toast.info { border-left-color: #3b82f6; } | |
.toast-icon { | |
font-size: 1.2rem; | |
} | |
.toast.success .toast-icon { color: #10b981; } | |
.toast.error .toast-icon { color: #ef4444; } | |
.toast.warning .toast-icon { color: #f59e0b; } | |
.toast.info .toast-icon { color: #3b82f6; } | |
.toast-content { | |
flex: 1; | |
} | |
.toast-title { | |
font-weight: 600; | |
font-size: var(--font-size-sm); | |
margin-bottom: 0.2rem; | |
} | |
.toast-message { | |
font-size: var(--font-size-xs); | |
color: var(--text-secondary); | |
} | |
.toast-close { | |
background: none; | |
border: none; | |
color: var(--text-secondary); | |
cursor: pointer; | |
font-size: 1rem; | |
transition: var(--transition-fast); | |
} | |
.toast-close:hover { | |
color: var(--text-primary); | |
} | |
/* واکنشگرایی */ | |
@media (max-width: 992px) { | |
.sidebar { | |
transform: translateX(100%); | |
transition: transform 0.3s ease; | |
} | |
.sidebar.open { | |
transform: translateX(0); | |
} | |
.main-content { | |
margin-right: 0; | |
width: 100%; | |
padding: 1rem; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="dashboard-container"> | |
<!-- سایدبار --> | |
<aside class="sidebar" id="sidebar"> | |
<div class="sidebar-header"> | |
<a href="/" class="logo"> | |
<div class="logo-icon"> | |
<i class="fas fa-scale-balanced"></i> | |
</div> | |
<div class="logo-text">سامانه حقوقی</div> | |
</a> | |
</div> | |
<nav> | |
<div class="nav-section"> | |
<h6 class="nav-title">داشبورد</h6> | |
<ul class="nav-menu"> | |
<li class="nav-item"> | |
<a href="/" class="nav-link"> | |
<i class="fas fa-chart-pie nav-icon"></i> | |
<span>نمای کلی</span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
<div class="nav-section"> | |
<h6 class="nav-title">مدیریت اسناد</h6> | |
<ul class="nav-menu"> | |
<li class="nav-item"> | |
<a href="/static/documents.html" class="nav-link"> | |
<i class="fas fa-file-alt nav-icon"></i> | |
<span>مدیریت اسناد</span> | |
<span class="nav-badge" id="totalDocumentsBadge">6</span> | |
</a> | |
</li> | |
<li class="nav-item"> | |
<a href="/static/upload.html" class="nav-link active"> | |
<i class="fas fa-cloud-upload-alt nav-icon"></i> | |
<span>آپلود فایل</span> | |
</a> | |
</li> | |
<li class="nav-item"> | |
<a href="/static/search.html" class="nav-link"> | |
<i class="fas fa-search nav-icon"></i> | |
<span>جستجو</span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
<div class="nav-section"> | |
<h6 class="nav-title">ابزارها</h6> | |
<ul class="nav-menu"> | |
<li class="nav-item"> | |
<a href="/static/scraping.html" class="nav-link"> | |
<i class="fas fa-globe nav-icon"></i> | |
<span>استخراج محتوا</span> | |
</a> | |
</li> | |
<li class="nav-item"> | |
<a href="/static/analytics.html" class="nav-link"> | |
<i class="fas fa-chart-line nav-icon"></i> | |
<span>آمار و تحلیل</span> | |
</a> | |
</li> | |
<li class="nav-item"> | |
<a href="/static/reports.html" class="nav-link"> | |
<i class="fas fa-file-export nav-icon"></i> | |
<span>گزارشها</span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
<div class="nav-section"> | |
<h6 class="nav-title">تنظیمات</h6> | |
<ul class="nav-menu"> | |
<li class="nav-item"> | |
<a href="/static/settings.html" class="nav-link"> | |
<i class="fas fa-cog nav-icon"></i> | |
<span>تنظیمات</span> | |
</a> | |
</li> | |
<li class="nav-item"> | |
<a href="#" class="nav-link"> | |
<i class="fas fa-sign-out-alt nav-icon"></i> | |
<span>خروج</span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
</nav> | |
</aside> | |
<!-- محتوای اصلی --> | |
<main class="main-content"> | |
<!-- هدر صفحه --> | |
<header class="page-header"> | |
<h1 class="page-title"> | |
<i class="fas fa-cloud-upload-alt"></i> | |
آپلود فایلهای حقوقی | |
</h1> | |
<div class="page-actions"> | |
<a href="/static/documents.html" class="btn btn-outline"> | |
<i class="fas fa-list"></i> | |
مشاهده اسناد | |
</a> | |
</div> | |
</header> | |
<!-- بخش آپلود اصلی --> | |
<section class="upload-section"> | |
<div class="upload-area" id="uploadArea"> | |
<div class="upload-icon"> | |
<i class="fas fa-cloud-upload-alt"></i> | |
</div> | |
<h2 class="upload-title">آپلود اسناد PDF</h2> | |
<p class="upload-description"> | |
فایلهای PDF خود را بکشید و اینجا رها کنید یا کلیک کنید تا انتخاب کنید.<br> | |
چندین فایل را میتوانید همزمان آپلود کنید. | |
</p> | |
<input type="file" class="upload-input" id="fileInput" multiple accept=".pdf"> | |
<button type="button" class="upload-btn" onclick="document.getElementById('fileInput').click()"> | |
<i class="fas fa-folder-open"></i> | |
انتخاب فایلها | |
</button> | |
</div> | |
</section> | |
<!-- فایلهای آپلود شده --> | |
<section class="upload-files" id="uploadFiles"> | |
<div class="files-header"> | |
<h3 class="files-title">فایلهای آپلود شده</h3> | |
<div class="files-actions"> | |
<button type="button" class="btn btn-success btn-sm" onclick="processAllFiles()"> | |
<i class="fas fa-play"></i> | |
پردازش همه | |
</button> | |
<button type="button" class="btn btn-danger btn-sm" onclick="clearAllFiles()"> | |
<i class="fas fa-trash"></i> | |
پاک کردن همه | |
</button> | |
</div> | |
</div> | |
<div id="filesContainer"> | |
<!-- فایلها اینجا نمایش داده میشوند --> | |
</div> | |
</section> | |
</main> | |
</div> | |
<!-- Toast Container --> | |
<div class="toast-container" id="toastContainer"></div> | |
<script> | |
// Global variables | |
let uploadedFiles = new Map(); | |
let fileCounter = 0; | |
let isOnline = false; | |
// Initialize page | |
document.addEventListener('DOMContentLoaded', function() { | |
console.log('📤 Upload page loading...'); | |
initializeUploadPage(); | |
}); | |
async function initializeUploadPage() { | |
try { | |
// Test backend connection | |
isOnline = await testConnection(); | |
// Setup event listeners | |
setupEventListeners(); | |
showToast('صفحه آپلود آماده است', 'success', 'آماده'); | |
} catch (error) { | |
console.error('Failed to initialize upload page:', error); | |
// Fallback mode | |
isOnline = false; | |
setupEventListeners(); | |
showToast('حالت آفلاین فعال است', 'warning', 'اتصال ناموفق'); | |
} | |
} | |
async function testConnection() { | |
try { | |
await window.legalAPI.healthCheck(); | |
return true; | |
} catch (error) { | |
return false; | |
} | |
} | |
// Setup event listeners | |
function setupEventListeners() { | |
const uploadArea = document.getElementById('uploadArea'); | |
const fileInput = document.getElementById('fileInput'); | |
// Drag and drop events | |
uploadArea.addEventListener('dragover', handleDragOver); | |
uploadArea.addEventListener('dragleave', handleDragLeave); | |
uploadArea.addEventListener('drop', handleDrop); | |
// File input change | |
fileInput.addEventListener('change', handleFileSelect); | |
// Click to upload | |
uploadArea.addEventListener('click', (e) => { | |
if (!e.target.closest('.upload-btn') && !e.target.classList.contains('upload-input')) { | |
fileInput.click(); | |
} | |
}); | |
} | |
// Drag and drop handlers | |
function handleDragOver(e) { | |
e.preventDefault(); | |
e.currentTarget.classList.add('dragover'); | |
} | |
function handleDragLeave(e) { | |
e.currentTarget.classList.remove('dragover'); | |
} | |
function handleDrop(e) { | |
e.preventDefault(); | |
e.currentTarget.classList.remove('dragover'); | |
const files = Array.from(e.dataTransfer.files).filter(file => | |
file.type === 'application/pdf' | |
); | |
if (files.length > 0) { | |
handleFileUpload(files); | |
} else { | |
showToast('فقط فایلهای PDF پذیرفته میشوند', 'warning', 'هشدار'); | |
} | |
} | |
// File selection handler | |
function handleFileSelect(e) { | |
const files = Array.from(e.target.files); | |
if (files.length > 0) { | |
handleFileUpload(files); | |
} | |
} | |
// File upload processing | |
async function handleFileUpload(files) { | |
const uploadFilesSection = document.getElementById('uploadFiles'); | |
const filesContainer = document.getElementById('filesContainer'); | |
uploadFilesSection.classList.add('has-files'); | |
let validFiles = []; | |
let invalidFiles = 0; | |
for (const file of files) { | |
// Validate file | |
const validation = validateFile(file); | |
if (!validation.valid) { | |
showToast(`${file.name}: ${validation.error}`, 'error', 'خطای اعتبارسنجی'); | |
invalidFiles++; | |
continue; | |
} | |
validFiles.push(file); | |
const fileId = generateFileId(); | |
const fileItem = createFileItem(file, fileId); | |
filesContainer.appendChild(fileItem); | |
// Store file info | |
uploadedFiles.set(fileId, { | |
file: file, | |
status: 'ready', | |
progress: 0 | |
}); | |
} | |
if (validFiles.length > 0) { | |
showToast(`${validFiles.length} فایل آماده پردازش شد`, 'success', 'آپلود موفق'); | |
// Start upload to backend if online | |
if (isOnline) { | |
await uploadFilesToBackend(validFiles); | |
} else { | |
// Simulate upload in offline mode | |
validFiles.forEach((file, index) => { | |
const fileId = Array.from(uploadedFiles.keys())[uploadedFiles.size - validFiles.length + index]; | |
setTimeout(() => { | |
simulateFileUpload(fileId); | |
}, Math.random() * 1000); | |
}); | |
} | |
} | |
if (invalidFiles > 0) { | |
showToast(`${invalidFiles} فایل نامعتبر حذف شد`, 'warning', 'هشدار'); | |
} | |
// Clear file input | |
document.getElementById('fileInput').value = ''; | |
} | |
// Upload files to backend | |
async function uploadFilesToBackend(files) { | |
try { | |
const response = await window.legalAPI.uploadFiles(files); | |
if (response.results) { | |
response.results.forEach((result, index) => { | |
const fileId = Array.from(uploadedFiles.keys())[uploadedFiles.size - files.length + index]; | |
const fileData = uploadedFiles.get(fileId); | |
if (result.success) { | |
// Real upload successful - monitor progress | |
monitorUploadProgress(fileId, result.document_id); | |
} else { | |
// Upload failed | |
updateFileStatus(fileId, 'error', result.error || 'خطا در آپلود'); | |
showFileRetryOption(fileId); | |
} | |
}); | |
} | |
showToast(`${response.successful_uploads} فایل با موفقیت آپلود شد`, 'success', 'آپلود موفق'); | |
} catch (error) { | |
console.error('Upload to backend failed:', error); | |
showToast('خطا در ارسال فایلها به سرور', 'error', 'خطای آپلود'); | |
// Fallback to simulation | |
files.forEach((file, index) => { | |
const fileId = Array.from(uploadedFiles.keys())[uploadedFiles.size - files.length + index]; | |
setTimeout(() => { | |
simulateFileUpload(fileId); | |
}, Math.random() * 1000); | |
}); | |
} | |
} | |
// Monitor upload progress for real backend uploads | |
async function monitorUploadProgress(fileId, documentId) { | |
const fileData = uploadedFiles.get(fileId); | |
if (!fileData) return; | |
updateFileStatus(fileId, 'uploading', 'در حال آپلود...'); | |
// Simulate upload progress | |
let progress = 0; | |
const uploadInterval = setInterval(() => { | |
progress += Math.random() * 15; | |
if (progress >= 100) { | |
progress = 100; | |
clearInterval(uploadInterval); | |
// Start monitoring OCR processing | |
setTimeout(() => { | |
monitorProcessingStatus(fileId, documentId); | |
}, 500); | |
} | |
const progressBar = document.querySelector(`#file-${fileId} .file-progress-bar`); | |
if (progressBar) { | |
progressBar.style.width = `${progress}%`; | |
} | |
}, 200); | |
} | |
// Monitor processing status from backend | |
async function monitorProcessingStatus(fileId, documentId) { | |
const fileData = uploadedFiles.get(fileId); | |
if (!fileData) return; | |
updateFileStatus(fileId, 'processing', 'در حال پردازش OCR...'); | |
// Poll for processing status | |
const pollInterval = setInterval(async () => { | |
try { | |
const document = await window.legalAPI.getDocument(documentId); | |
if (document.status === 'processed') { | |
clearInterval(pollInterval); | |
updateFileStatus(fileId, 'success', 'پردازش تکمیل شد'); | |
showToast(`${fileData.file.name} با موفقیت پردازش شد`, 'success', 'پردازش موفق'); | |
} else if (document.status === 'error') { | |
clearInterval(pollInterval); | |
updateFileStatus(fileId, 'error', 'خطا در پردازش'); | |
showFileRetryOption(fileId); | |
showToast(`خطا در پردازش ${fileData.file.name}`, 'error', 'خطای پردازش'); | |
} | |
// Continue polling for other statuses (processing, pending) | |
} catch (error) { | |
console.error('Failed to check processing status:', error); | |
// Don't clear interval yet, might be temporary network issue | |
} | |
}, 3000); // Poll every 3 seconds | |
// Stop polling after 5 minutes | |
setTimeout(() => { | |
clearInterval(pollInterval); | |
if (fileData.status !== 'success' && fileData.status !== 'error') { | |
updateFileStatus(fileId, 'processing', 'پردازش ادامه دارد...'); | |
} | |
}, 300000); | |
} | |
// Validate file | |
function validateFile(file) { | |
if (file.type !== 'application/pdf') { | |
return { valid: false, error: 'فقط فایلهای PDF پذیرفته میشوند' }; | |
} | |
if (file.size > 50 * 1024 * 1024) { // 50MB | |
return { valid: false, error: 'حجم فایل نباید بیشتر از 50 مگابایت باشد' }; | |
} | |
if (file.size < 1024) { // 1KB | |
return { valid: false, error: 'فایل خیلی کوچک است' }; | |
} | |
return { valid: true }; | |
} | |
// Create file item UI | |
function createFileItem(file, fileId) { | |
const fileItem = document.createElement('div'); | |
fileItem.className = 'file-item'; | |
fileItem.id = `file-${fileId}`; | |
fileItem.innerHTML = ` | |
<div class="file-icon"> | |
<i class="fas fa-file-pdf"></i> | |
</div> | |
<div class="file-info"> | |
<div class="file-name">${file.name}</div> | |
<div class="file-details"> | |
<div class="file-size"> | |
<i class="fas fa-weight-hanging"></i> | |
${formatFileSize(file.size)} | |
</div> | |
<div class="file-type"> | |
<i class="fas fa-file-pdf"></i> | |
PDF Document | |
</div> | |
</div> | |
<div class="file-progress"> | |
<div class="file-progress-bar" style="width: 0%"></div> | |
</div> | |
</div> | |
<div class="file-status uploading"> | |
<i class="fas fa-clock"></i> | |
آماده | |
</div> | |
<div class="file-actions"> | |
<button type="button" class="action-btn retry" onclick="retryUpload('${fileId}')" title="تلاش مجدد" style="display: none;"> | |
<i class="fas fa-redo"></i> | |
</button> | |
<button type="button" class="action-btn remove" onclick="removeFile('${fileId}')" title="حذف"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
`; | |
return fileItem; | |
} | |
// Simulate file upload and processing (offline mode) | |
function simulateFileUpload(fileId) { | |
const fileData = uploadedFiles.get(fileId); | |
if (!fileData) return; | |
const fileItem = document.getElementById(`file-${fileId}`); | |
const statusElement = fileItem.querySelector('.file-status'); | |
const progressBar = fileItem.querySelector('.file-progress-bar'); | |
// Phase 1: Uploading | |
updateFileStatus(fileId, 'uploading', 'در حال آپلود...'); | |
let progress = 0; | |
const uploadInterval = setInterval(() => { | |
progress += Math.random() * 15; | |
if (progress >= 100) { | |
progress = 100; | |
clearInterval(uploadInterval); | |
// Phase 2: Processing | |
setTimeout(() => { | |
simulateProcessing(fileId); | |
}, 500); | |
} | |
progressBar.style.width = `${progress}%`; | |
}, 200); | |
} | |
// Simulate OCR processing (offline mode) | |
function simulateProcessing(fileId) { | |
const fileData = uploadedFiles.get(fileId); | |
if (!fileData) return; | |
updateFileStatus(fileId, 'processing', 'در حال پردازش OCR...'); | |
// Simulate processing time based on file size | |
const fileSize = fileData.file.size; | |
const processingTime = Math.min(fileSize / (1024 * 1024) * 1000 + 2000, 8000); // 2-8 seconds | |
setTimeout(() => { | |
// Random success/failure (90% success rate) | |
const success = Math.random() > 0.1; | |
if (success) { | |
updateFileStatus(fileId, 'success', 'پردازش تکمیل شد'); | |
showToast(`${fileData.file.name} با موفقیت پردازش شد`, 'success', 'پردازش موفق'); | |
} else { | |
updateFileStatus(fileId, 'error', 'خطا در پردازش'); | |
showFileRetryOption(fileId); | |
showToast(`خطا در پردازش ${fileData.file.name}`, 'error', 'خطای پردازش'); | |
} | |
}, processingTime); | |
} | |
// Update file status | |
function updateFileStatus(fileId, status, message) { | |
const fileData = uploadedFiles.get(fileId); | |
if (!fileData) return; | |
fileData.status = status; | |
const fileItem = document.getElementById(`file-${fileId}`); | |
if (!fileItem) return; | |
const statusElement = fileItem.querySelector('.file-status'); | |
const progressBar = fileItem.querySelector('.file-progress-bar'); | |
statusElement.className = `file-status ${status}`; | |
const icons = { | |
ready: 'clock', | |
uploading: 'spinner fa-spin', | |
processing: 'cog fa-spin', | |
success: 'check-circle', | |
error: 'exclamation-triangle' | |
}; | |
statusElement.innerHTML = ` | |
<i class="fas fa-${icons[status]}"></i> | |
${message} | |
`; | |
switch (status) { | |
case 'success': | |
progressBar.style.width = '100%'; | |
progressBar.style.background = 'var(--success-gradient)'; | |
break; | |
case 'error': | |
progressBar.style.width = '100%'; | |
progressBar.style.background = 'var(--danger-gradient)'; | |
break; | |
} | |
} | |
// Show retry option for failed files | |
function showFileRetryOption(fileId) { | |
const fileItem = document.getElementById(`file-${fileId}`); | |
if (!fileItem) return; | |
const retryBtn = fileItem.querySelector('.action-btn.retry'); | |
retryBtn.style.display = 'flex'; | |
} | |
// Retry file upload | |
async function retryUpload(fileId) { | |
const fileData = uploadedFiles.get(fileId); | |
if (!fileData) return; | |
const fileItem = document.getElementById(`file-${fileId}`); | |
const retryBtn = fileItem.querySelector('.action-btn.retry'); | |
retryBtn.style.display = 'none'; | |
fileData.status = 'ready'; | |
// Reset progress bar | |
const progressBar = fileItem.querySelector('.file-progress-bar'); | |
progressBar.style.width = '0%'; | |
progressBar.style.background = 'var(--success-gradient)'; | |
if (isOnline) { | |
// Retry with real backend | |
await uploadFilesToBackend([fileData.file]); | |
} else { | |
// Retry with simulation | |
setTimeout(() => { | |
simulateFileUpload(fileId); | |
}, 500); | |
} | |
} | |
// Remove file | |
function removeFile(fileId) { | |
const fileData = uploadedFiles.get(fileId); | |
if (!fileData) return; | |
if (!confirm(`آیا از حذف "${fileData.file.name}" اطمینان دارید؟`)) { | |
return; | |
} | |
const fileItem = document.getElementById(`file-${fileId}`); | |
if (fileItem) { | |
fileItem.style.transform = 'translateX(100%)'; | |
fileItem.style.opacity = '0'; | |
setTimeout(() => { | |
fileItem.remove(); | |
uploadedFiles.delete(fileId); | |
// Hide section if no files | |
if (uploadedFiles.size === 0) { | |
document.getElementById('uploadFiles').classList.remove('has-files'); | |
} | |
}, 300); | |
} | |
showToast(`${fileData.file.name} حذف شد`, 'info', 'حذف فایل'); | |
} | |
// Process all files | |
async function processAllFiles() { | |
let readyFiles = []; | |
uploadedFiles.forEach((fileData, fileId) => { | |
if (fileData.status === 'ready') { | |
readyFiles.push({fileId, file: fileData.file}); | |
} | |
}); | |
if (readyFiles.length === 0) { | |
showToast('هیچ فایل آمادهای برای پردازش وجود ندارد', 'warning', 'هشدار'); | |
return; | |
} | |
if (isOnline) { | |
// Process with real backend | |
const files = readyFiles.map(item => item.file); | |
await uploadFilesToBackend(files); | |
} else { | |
// Process with simulation | |
readyFiles.forEach(({fileId}) => { | |
setTimeout(() => { | |
simulateFileUpload(fileId); | |
}, Math.random() * 1000); | |
}); | |
} | |
showToast(`پردازش ${readyFiles.length} فایل شروع شد`, 'info', 'شروع پردازش'); | |
} | |
// Clear all files | |
function clearAllFiles() { | |
if (uploadedFiles.size === 0) { | |
showToast('هیچ فایلی برای حذف وجود ندارد', 'warning', 'هشدار'); | |
return; | |
} | |
if (!confirm('آیا از حذف همه فایلها اطمینان دارید؟')) { | |
return; | |
} | |
const filesContainer = document.getElementById('filesContainer'); | |
filesContainer.innerHTML = ''; | |
uploadedFiles.clear(); | |
document.getElementById('uploadFiles').classList.remove('has-files'); | |
showToast('همه فایلها حذف شدند', 'info', 'پاک کردن'); | |
} | |
// Utility functions | |
function generateFileId() { | |
return `file_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; | |
} | |
function formatFileSize(bytes) { | |
if (bytes === 0) return '0 بایت'; | |
const k = 1024; | |
const sizes = ['بایت', 'کیلوبایت', 'مگابایت', 'گیگابایت']; | |
const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
} | |
console.log('📤 Upload Page Ready!'); | |
</script> | |
</body> | |
</html> |