Hoghoghi / frontend /upload.html
Really-amin's picture
Upload 149 files
8d3cb40 verified
raw
history blame
47.2 kB
<!DOCTYPE html>
<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>