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> | |
| :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; | |
| } | |
| * { | |
| 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); | |
| } | |
| ::-webkit-scrollbar { | |
| width: 6px; | |
| height: 6px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: rgba(0, 0, 0, 0.02); | |
| border-radius: 10px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--primary-gradient); | |
| border-radius: 10px; | |
| } | |
| .dashboard-container { | |
| display: flex; | |
| min-height: 100vh; | |
| width: 100%; | |
| } | |
| /* سایدبار مشابه صفحه اصلی */ | |
| .sidebar { | |
| width: 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); | |
| -webkit-backdrop-filter: blur(25px); | |
| padding: 1rem 0; | |
| position: fixed; | |
| height: 100vh; | |
| right: 0; | |
| top: 0; | |
| z-index: 1000; | |
| overflow-y: auto; | |
| box-shadow: | |
| 0 0 0 1px rgba(59, 130, 246, 0.08), | |
| -8px 0 32px rgba(59, 130, 246, 0.12), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.6); | |
| border-left: 1px solid rgba(59, 130, 246, 0.15); | |
| } | |
| .sidebar-header { | |
| padding: 0 1rem 1rem; | |
| border-bottom: 1px solid rgba(59, 130, 246, 0.12); | |
| margin-bottom: 1rem; | |
| display: flex; | |
| justify-content: space-between; | |
| 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 { | |
| width: 2rem; | |
| height: 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-bottom: 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 { | |
| color: var(--text-primary); | |
| 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-left: 0.6rem; | |
| width: 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-right: auto; | |
| min-width: 1.2rem; | |
| text-align: center; | |
| } | |
| /* محتوای اصلی */ | |
| .main-content { | |
| flex: 1; | |
| margin-right: var(--sidebar-width); | |
| padding: 1rem; | |
| min-height: 100vh; | |
| width: calc(100% - var(--sidebar-width)); | |
| } | |
| .page-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 2rem; | |
| padding: 1rem 0; | |
| border-bottom: 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-primary { | |
| background: var(--primary-gradient); | |
| color: white; | |
| box-shadow: var(--shadow-sm); | |
| } | |
| .btn-primary:hover { | |
| box-shadow: var(--shadow-md); | |
| transform: translateY(-1px); | |
| } | |
| .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); | |
| } | |
| /* فیلترها */ | |
| .filters-section { | |
| background: var(--card-bg); | |
| border-radius: var(--border-radius); | |
| padding: 1.5rem; | |
| margin-bottom: 1.5rem; | |
| box-shadow: var(--shadow-sm); | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| } | |
| .filters-title { | |
| font-size: var(--font-size-lg); | |
| font-weight: 700; | |
| margin-bottom: 1rem; | |
| color: var(--text-primary); | |
| } | |
| .filters-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 1rem; | |
| } | |
| .filter-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .filter-label { | |
| font-size: var(--font-size-sm); | |
| font-weight: 600; | |
| color: var(--text-secondary); | |
| } | |
| .filter-input, | |
| .filter-select { | |
| padding: 0.6rem 0.8rem; | |
| border: 1px solid var(--glass-border); | |
| border-radius: var(--border-radius-sm); | |
| background: var(--glass-bg); | |
| color: var(--text-primary); | |
| font-family: inherit; | |
| font-size: var(--font-size-sm); | |
| transition: var(--transition-smooth); | |
| } | |
| .filter-input:focus, | |
| .filter-select:focus { | |
| outline: none; | |
| border-color: #3b82f6; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); | |
| } | |
| .filters-actions { | |
| margin-top: 1rem; | |
| display: flex; | |
| gap: 0.8rem; | |
| } | |
| .btn-sm { | |
| padding: 0.4rem 0.8rem; | |
| font-size: var(--font-size-xs); | |
| } | |
| /* جدول اسناد */ | |
| .documents-section { | |
| background: var(--card-bg); | |
| border-radius: var(--border-radius); | |
| box-shadow: var(--shadow-md); | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| overflow: hidden; | |
| } | |
| .documents-header { | |
| padding: 1rem; | |
| border-bottom: 1px solid rgba(0, 0, 0, 0.05); | |
| background: linear-gradient(135deg, rgba(59, 130, 246, 0.02), rgba(255, 255, 255, 0.1)); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .documents-title { | |
| font-size: var(--font-size-lg); | |
| font-weight: 700; | |
| color: var(--text-primary); | |
| } | |
| .documents-stats { | |
| display: flex; | |
| gap: 1rem; | |
| font-size: var(--font-size-sm); | |
| color: var(--text-muted); | |
| } | |
| .documents-table { | |
| width: 100%; | |
| border-collapse: separate; | |
| border-spacing: 0; | |
| } | |
| .documents-table thead { | |
| background: linear-gradient(135deg, rgba(59, 130, 246, 0.03), rgba(255, 255, 255, 0.1)); | |
| } | |
| .documents-table th { | |
| padding: 1rem; | |
| text-align: right; | |
| font-weight: 700; | |
| color: var(--text-primary); | |
| font-size: var(--font-size-sm); | |
| border-bottom: 2px solid rgba(59, 130, 246, 0.08); | |
| } | |
| .documents-table td { | |
| padding: 1rem; | |
| border-bottom: 1px solid rgba(0, 0, 0, 0.03); | |
| font-size: var(--font-size-sm); | |
| vertical-align: middle; | |
| } | |
| .documents-table tbody tr { | |
| transition: all 0.2s ease; | |
| cursor: pointer; | |
| } | |
| .documents-table tbody tr:hover { | |
| background: rgba(59, 130, 246, 0.02); | |
| transform: translateX(-3px); | |
| box-shadow: 0 2px 8px rgba(59, 130, 246, 0.06); | |
| } | |
| .document-title { | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| font-size: var(--font-size-sm); | |
| } | |
| .doc-icon { | |
| width: 1.8rem; | |
| height: 1.8rem; | |
| border-radius: var(--border-radius-sm); | |
| background: var(--primary-gradient); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: white; | |
| font-size: var(--font-size-xs); | |
| } | |
| .quality-display { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.3rem; | |
| } | |
| .quality-score { | |
| font-weight: 700; | |
| font-size: var(--font-size-sm); | |
| color: var(--text-primary); | |
| } | |
| .quality-bar { | |
| width: 80px; | |
| height: 4px; | |
| background: rgba(0, 0, 0, 0.08); | |
| border-radius: 2px; | |
| overflow: hidden; | |
| } | |
| .quality-fill { | |
| height: 100%; | |
| border-radius: 2px; | |
| } | |
| .quality-excellent { background: var(--success-gradient); } | |
| .quality-good { background: var(--primary-gradient); } | |
| .quality-average { background: var(--warning-gradient); } | |
| .quality-poor { background: var(--danger-gradient); } | |
| .status-badge { | |
| padding: 0.3rem 0.6rem; | |
| border-radius: 15px; | |
| font-size: var(--font-size-xs); | |
| font-weight: 600; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.3rem; | |
| } | |
| .status-processed { | |
| background: linear-gradient(135deg, rgba(16, 185, 129, 0.15), rgba(5, 150, 105, 0.08)); | |
| color: #047857; | |
| border: 1px solid rgba(16, 185, 129, 0.2); | |
| } | |
| .status-pending { | |
| background: linear-gradient(135deg, rgba(245, 158, 11, 0.15), rgba(217, 119, 6, 0.08)); | |
| color: #b45309; | |
| border: 1px solid rgba(245, 158, 11, 0.2); | |
| } | |
| .status-error { | |
| background: linear-gradient(135deg, rgba(239, 68, 68, 0.15), rgba(220, 38, 38, 0.08)); | |
| color: #b91c1c; | |
| border: 1px solid rgba(239, 68, 68, 0.2); | |
| } | |
| .status-processing { | |
| background: linear-gradient(135deg, rgba(59, 130, 246, 0.15), rgba(29, 78, 216, 0.08)); | |
| color: #1d4ed8; | |
| border: 1px solid rgba(59, 130, 246, 0.2); | |
| } | |
| .category-tag { | |
| padding: 0.25rem 0.6rem; | |
| border-radius: 10px; | |
| background: rgba(59, 130, 246, 0.08); | |
| color: var(--text-primary); | |
| font-size: var(--font-size-xs); | |
| font-weight: 500; | |
| } | |
| .document-actions { | |
| display: flex; | |
| gap: 0.3rem; | |
| } | |
| .action-btn { | |
| padding: 0.4rem; | |
| border: none; | |
| border-radius: var(--border-radius-sm); | |
| cursor: pointer; | |
| transition: var(--transition-fast); | |
| font-size: var(--font-size-xs); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 2rem; | |
| height: 2rem; | |
| } | |
| .action-btn.view { | |
| background: rgba(59, 130, 246, 0.1); | |
| color: #3b82f6; | |
| } | |
| .action-btn.download { | |
| background: rgba(16, 185, 129, 0.1); | |
| color: #10b981; | |
| } | |
| .action-btn.delete { | |
| background: rgba(239, 68, 68, 0.1); | |
| color: #ef4444; | |
| } | |
| .action-btn:hover { | |
| transform: scale(1.1); | |
| } | |
| /* Pagination */ | |
| .pagination { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 0.4rem; | |
| padding: 1rem; | |
| border-top: 1px solid rgba(0, 0, 0, 0.05); | |
| } | |
| .pagination-btn { | |
| padding: 0.4rem 0.6rem; | |
| border: 1px solid var(--glass-border); | |
| background: var(--glass-bg); | |
| color: var(--text-primary); | |
| border-radius: var(--border-radius-sm); | |
| cursor: pointer; | |
| transition: var(--transition-fast); | |
| font-size: var(--font-size-xs); | |
| font-family: inherit; | |
| } | |
| .pagination-btn:hover:not(:disabled) { | |
| background: var(--primary-gradient); | |
| color: white; | |
| border-color: transparent; | |
| } | |
| .pagination-btn.active { | |
| background: var(--primary-gradient); | |
| color: white; | |
| border-color: transparent; | |
| } | |
| .pagination-btn:disabled { | |
| opacity: 0.4; | |
| cursor: not-allowed; | |
| } | |
| /* Loading States */ | |
| .loading-container { | |
| padding: 3rem 1rem; | |
| text-align: center; | |
| color: var(--text-secondary); | |
| } | |
| .loading-spinner { | |
| width: 2rem; | |
| height: 2rem; | |
| border: 2px solid rgba(59, 130, 246, 0.1); | |
| border-radius: 50%; | |
| border-top-color: #3b82f6; | |
| animation: spin 1s linear infinite; | |
| margin: 0 auto 1rem; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| /* Empty State */ | |
| .empty-state { | |
| padding: 3rem 1rem; | |
| text-align: center; | |
| color: var(--text-secondary); | |
| } | |
| .empty-icon { | |
| font-size: 3rem; | |
| margin-bottom: 1rem; | |
| opacity: 0.4; | |
| color: var(--text-muted); | |
| } | |
| .empty-title { | |
| font-size: var(--font-size-lg); | |
| font-weight: 600; | |
| margin-bottom: 0.5rem; | |
| color: var(--text-primary); | |
| } | |
| .empty-description { | |
| font-size: var(--font-size-sm); | |
| margin-bottom: 1.5rem; | |
| max-width: 400px; | |
| margin-left: auto; | |
| margin-right: auto; | |
| } | |
| /* Modal */ | |
| .modal { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.7); | |
| backdrop-filter: blur(5px); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 10000; | |
| opacity: 0; | |
| visibility: hidden; | |
| transition: all 0.3s ease; | |
| } | |
| .modal.show { | |
| opacity: 1; | |
| visibility: visible; | |
| } | |
| .modal-content { | |
| background: var(--card-bg); | |
| border-radius: var(--border-radius); | |
| box-shadow: var(--shadow-lg); | |
| max-width: 80vw; | |
| max-height: 80vh; | |
| width: 100%; | |
| overflow: hidden; | |
| transform: scale(0.9); | |
| transition: all 0.3s ease; | |
| } | |
| .modal.show .modal-content { | |
| transform: scale(1); | |
| } | |
| .modal-header { | |
| padding: 1.5rem; | |
| border-bottom: 1px solid rgba(0, 0, 0, 0.05); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| background: linear-gradient(135deg, rgba(59, 130, 246, 0.02), rgba(255, 255, 255, 0.1)); | |
| } | |
| .modal-title { | |
| font-size: var(--font-size-lg); | |
| font-weight: 700; | |
| color: var(--text-primary); | |
| } | |
| .modal-close { | |
| background: none; | |
| border: none; | |
| font-size: 1.5rem; | |
| color: var(--text-secondary); | |
| cursor: pointer; | |
| transition: var(--transition-fast); | |
| } | |
| .modal-close:hover { | |
| color: var(--text-primary); | |
| } | |
| .modal-body { | |
| padding: 1.5rem; | |
| max-height: 60vh; | |
| overflow-y: auto; | |
| } | |
| .document-metadata { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 1rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| .metadata-item { | |
| padding: 0.8rem; | |
| background: rgba(0, 0, 0, 0.02); | |
| border-radius: var(--border-radius-sm); | |
| } | |
| .metadata-label { | |
| font-size: var(--font-size-xs); | |
| color: var(--text-secondary); | |
| font-weight: 600; | |
| margin-bottom: 0.3rem; | |
| } | |
| .metadata-value { | |
| font-size: var(--font-size-sm); | |
| color: var(--text-primary); | |
| font-weight: 500; | |
| } | |
| .ocr-text { | |
| background: rgba(0, 0, 0, 0.02); | |
| border-radius: var(--border-radius-sm); | |
| padding: 1rem; | |
| font-family: 'Vazirmatn', monospace; | |
| font-size: var(--font-size-sm); | |
| line-height: 1.8; | |
| color: var(--text-primary); | |
| border: 1px solid rgba(0, 0, 0, 0.05); | |
| white-space: pre-wrap; | |
| direction: rtl; | |
| max-height: 300px; | |
| overflow-y: auto; | |
| } | |
| /* 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; | |
| } | |
| .page-header { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: 1rem; | |
| } | |
| .page-actions { | |
| width: 100%; | |
| justify-content: flex-start; | |
| flex-wrap: wrap; | |
| } | |
| .filters-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| .documents-header { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: 1rem; | |
| } | |
| .documents-stats { | |
| width: 100%; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .main-content { | |
| padding: 0.8rem; | |
| } | |
| .documents-table { | |
| font-size: var(--font-size-xs); | |
| } | |
| .documents-table th, | |
| .documents-table td { | |
| padding: 0.6rem; | |
| } | |
| .modal-content { | |
| max-width: 95vw; | |
| max-height: 90vh; | |
| } | |
| .document-metadata { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </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 active"> | |
| <i class="fas fa-file-alt nav-icon"></i> | |
| <span>مدیریت اسناد</span> | |
| <span class="nav-badge" id="totalDocumentsBadge">...</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="/static/upload.html" class="nav-link"> | |
| <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-file-alt"></i> | |
| مدیریت اسناد حقوقی | |
| </h1> | |
| <div class="page-actions"> | |
| <a href="/static/upload.html" class="btn btn-primary"> | |
| <i class="fas fa-plus"></i> | |
| آپلود سند جدید | |
| </a> | |
| <button type="button" class="btn btn-outline" onclick="refreshDocuments()"> | |
| <i class="fas fa-refresh"></i> | |
| بهروزرسانی | |
| </button> | |
| <button type="button" class="btn btn-outline" onclick="exportDocuments()"> | |
| <i class="fas fa-download"></i> | |
| خروجی Excel | |
| </button> | |
| </div> | |
| </header> | |
| <!-- فیلترها --> | |
| <section class="filters-section"> | |
| <h3 class="filters-title">فیلتر و جستجو</h3> | |
| <div class="filters-grid"> | |
| <div class="filter-group"> | |
| <label class="filter-label">جستجو در اسناد</label> | |
| <input type="text" class="filter-input" id="searchFilter" placeholder="نام فایل، محتوا، خلاصه..."> | |
| </div> | |
| <div class="filter-group"> | |
| <label class="filter-label">دستهبندی</label> | |
| <select class="filter-select" id="categoryFilter"> | |
| <option value="">همه دستهها</option> | |
| <option value="قراردادها">قراردادها</option> | |
| <option value="دادخواستها">دادخواستها</option> | |
| <option value="احکام قضایی">احکام قضایی</option> | |
| <option value="آرای دیوان">آرای دیوان</option> | |
| <option value="سایر">سایر</option> | |
| </select> | |
| </div> | |
| <div class="filter-group"> | |
| <label class="filter-label">وضعیت</label> | |
| <select class="filter-select" id="statusFilter"> | |
| <option value="">همه وضعیتها</option> | |
| <option value="processed">پردازش شده</option> | |
| <option value="processing">در حال پردازش</option> | |
| <option value="pending">در انتظار</option> | |
| <option value="error">خطا</option> | |
| </select> | |
| </div> | |
| <div class="filter-group"> | |
| <label class="filter-label">حداقل کیفیت</label> | |
| <select class="filter-select" id="qualityFilter"> | |
| <option value="">همه کیفیتها</option> | |
| <option value="8.5">عالی (8.5+)</option> | |
| <option value="6.5">خوب (6.5+)</option> | |
| <option value="4.5">متوسط (4.5+)</option> | |
| <option value="0">ضعیف (کمتر از 4.5)</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="filters-actions"> | |
| <button type="button" class="btn btn-primary btn-sm" onclick="applyFilters()"> | |
| <i class="fas fa-filter"></i> | |
| اعمال فیلتر | |
| </button> | |
| <button type="button" class="btn btn-outline btn-sm" onclick="resetFilters()"> | |
| <i class="fas fa-undo"></i> | |
| پاک کردن فیلترها | |
| </button> | |
| </div> | |
| </section> | |
| <!-- جدول اسناد --> | |
| <section class="documents-section"> | |
| <div class="documents-header"> | |
| <h3 class="documents-title">لیست اسناد</h3> | |
| <div class="documents-stats"> | |
| <span>کل: <strong id="totalCount">...</strong></span> | |
| <span>پردازش شده: <strong id="processedCount">...</strong></span> | |
| <span>در انتظار: <strong id="pendingCount">...</strong></span> | |
| <span>خطا: <strong id="errorCount">...</strong></span> | |
| </div> | |
| </div> | |
| <div id="tableContainer"> | |
| <!-- Loading State --> | |
| <div class="loading-container" id="loadingContainer"> | |
| <div class="loading-spinner"></div> | |
| <p>در حال بارگذاری اسناد...</p> | |
| </div> | |
| <!-- Documents Table --> | |
| <table class="documents-table" id="documentsTable" style="display: none;"> | |
| <thead> | |
| <tr> | |
| <th>عنوان سند</th> | |
| <th>دستهبندی</th> | |
| <th>امتیاز کیفیت</th> | |
| <th>تاریخ ایجاد</th> | |
| <th>وضعیت</th> | |
| <th>اندازه فایل</th> | |
| <th>عملیات</th> | |
| </tr> | |
| </thead> | |
| <tbody id="documentsTableBody"> | |
| <!-- Data will be loaded here --> | |
| </tbody> | |
| </table> | |
| <!-- Empty State --> | |
| <div class="empty-state" id="emptyState" style="display: none;"> | |
| <i class="fas fa-inbox empty-icon"></i> | |
| <div class="empty-title">هیچ سندی یافت نشد</div> | |
| <div class="empty-description">اسناد جدید آپلود کنید یا فیلترها را تغییر دهید</div> | |
| <a href="/static/upload.html" class="btn btn-primary"> | |
| <i class="fas fa-plus"></i> | |
| آپلود سند جدید | |
| </a> | |
| </div> | |
| <div class="pagination" id="pagination"> | |
| <!-- Pagination will be populated by JavaScript --> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| </div> | |
| <!-- Modal برای نمایش جزئیات سند --> | |
| <div class="modal" id="documentModal"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h3 class="modal-title" id="modalTitle">جزئیات سند</h3> | |
| <button type="button" class="modal-close" onclick="closeModal()"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="document-metadata" id="documentMetadata"> | |
| <!-- Document metadata will be loaded here --> | |
| </div> | |
| <div class="ocr-text" id="ocrText"> | |
| <!-- OCR text will be loaded here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Toast Container --> | |
| <div class="toast-container" id="toastContainer"></div> | |
| <script> | |
| // Global variables | |
| let currentPage = 1; | |
| let itemsPerPage = 10; | |
| let totalDocuments = 0; | |
| let currentFilters = {}; | |
| let documents = []; | |
| let isOnline = false; | |
| // Initialize page | |
| document.addEventListener('DOMContentLoaded', function() { | |
| console.log('📄 Documents page loading...'); | |
| initializeDocumentsPage(); | |
| }); | |
| async function initializeDocumentsPage() { | |
| try { | |
| // Test backend connection | |
| isOnline = await testConnection(); | |
| // Load documents from backend | |
| await loadDocuments(); | |
| // Setup event listeners | |
| setupEventListeners(); | |
| showToast('صفحه مدیریت اسناد بارگذاری شد', 'success', 'بارگذاری موفق'); | |
| } catch (error) { | |
| console.error('Failed to initialize documents page:', error); | |
| // Fallback to mock data | |
| await loadFallbackDocuments(); | |
| setupEventListeners(); | |
| showToast('حالت آفلاین فعال است', 'warning', 'اتصال ناموفق'); | |
| } | |
| } | |
| async function testConnection() { | |
| try { | |
| await window.legalAPI.healthCheck(); | |
| return true; | |
| } catch (error) { | |
| return false; | |
| } | |
| } | |
| async function loadDocuments() { | |
| try { | |
| showLoading(true); | |
| // Get filter parameters from URL | |
| const urlParams = new URLSearchParams(window.location.search); | |
| const filters = { | |
| page: currentPage, | |
| limit: itemsPerPage, | |
| search: urlParams.get('search') || currentFilters.search || '', | |
| category: currentFilters.category || '', | |
| status: currentFilters.status || '', | |
| min_quality: currentFilters.min_quality || '' | |
| }; | |
| // Apply URL search to filter | |
| if (filters.search) { | |
| document.getElementById('searchFilter').value = filters.search; | |
| } | |
| const response = await window.legalAPI.getDocuments(filters); | |
| documents = response.documents.map(doc => new DocumentModel(doc)); | |
| totalDocuments = response.pagination.total; | |
| displayDocuments(); | |
| updateStats(); | |
| updatePagination(response.pagination); | |
| console.log('✅ Real documents loaded:', documents.length); | |
| } catch (error) { | |
| console.error('Failed to load documents:', error); | |
| throw error; | |
| } finally { | |
| showLoading(false); | |
| } | |
| } | |
| async function loadFallbackDocuments() { | |
| // Fallback mock data | |
| const mockDocuments = [ | |
| { | |
| id: 1, | |
| filename: "contract_001.pdf", | |
| original_filename: "قرارداد خرید املاک.pdf", | |
| file_size: 1024000, | |
| category: "قراردادها", | |
| quality_score: 8.5, | |
| status: "processed", | |
| created_at: "2024-01-15T10:30:00Z", | |
| ocr_text: "قرارداد خرید و فروش ملک واقع در تهران...", | |
| summary: "قرارداد خرید املاک مسکونی در منطقه 3 تهران" | |
| }, | |
| { | |
| id: 2, | |
| filename: "lawsuit_002.pdf", | |
| original_filename: "دادخواست طلاق.pdf", | |
| file_size: 512000, | |
| category: "دادخواستها", | |
| quality_score: 7.8, | |
| status: "processed", | |
| created_at: "2024-01-14T14:20:00Z", | |
| ocr_text: "دادخواست طلاق به درخواست زوجه...", | |
| summary: "درخواست طلاق توافقی با تقسیم اموال" | |
| } | |
| ]; | |
| documents = mockDocuments.map(doc => new DocumentModel(doc)); | |
| totalDocuments = documents.length; | |
| showLoading(false); | |
| displayDocuments(); | |
| updateStats(); | |
| updatePagination({page: 1, limit: itemsPerPage, total: totalDocuments, pages: 1}); | |
| console.log('⚠️ Using fallback documents data'); | |
| } | |
| function displayDocuments() { | |
| const tbody = document.getElementById('documentsTableBody'); | |
| const table = document.getElementById('documentsTable'); | |
| const emptyState = document.getElementById('emptyState'); | |
| if (documents.length === 0) { | |
| table.style.display = 'none'; | |
| emptyState.style.display = 'block'; | |
| return; | |
| } | |
| table.style.display = 'table'; | |
| emptyState.style.display = 'none'; | |
| tbody.innerHTML = documents.map(doc => ` | |
| <tr onclick="viewDocument(${doc.id})" style="cursor: pointer;"> | |
| <td> | |
| <div class="document-title"> | |
| <div class="doc-icon"> | |
| <i class="fas fa-file-pdf"></i> | |
| </div> | |
| <div> | |
| <div>${doc.original_filename}</div> | |
| <small style="color: var(--text-muted);">${doc.filename}</small> | |
| </div> | |
| </div> | |
| </td> | |
| <td> | |
| <span class="category-tag">${doc.category}</span> | |
| </td> | |
| <td> | |
| <div class="quality-display"> | |
| <div class="quality-score">${doc.quality_score ? doc.quality_score.toFixed(1) : '0.0'}</div> | |
| <div class="quality-bar"> | |
| <div class="quality-fill ${doc.getQualityClass()}" | |
| style="width: ${(doc.quality_score || 0) * 10}%"></div> | |
| </div> | |
| </div> | |
| </td> | |
| <td>${doc.getFormattedDate()}</td> | |
| <td> | |
| <span class="status-badge status-${doc.status}"> | |
| <i class="fas fa-${doc.getStatusIcon()}"></i> | |
| ${doc.getStatusText()} | |
| </span> | |
| </td> | |
| <td>${doc.getFormattedFileSize()}</td> | |
| <td> | |
| <div class="document-actions"> | |
| <button type="button" class="action-btn view" onclick="event.stopPropagation(); viewDocument(${doc.id})" title="مشاهده"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button type="button" class="action-btn download" onclick="event.stopPropagation(); downloadDocument(${doc.id})" title="دانلود"> | |
| <i class="fas fa-download"></i> | |
| </button> | |
| <button type="button" class="action-btn delete" onclick="event.stopPropagation(); deleteDocument(${doc.id})" title="حذف"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| </td> | |
| </tr> | |
| `).join(''); | |
| } | |
| function setupEventListeners() { | |
| // Filter inputs | |
| document.getElementById('searchFilter').addEventListener('input', debounce(applyFilters, 300)); | |
| document.getElementById('categoryFilter').addEventListener('change', applyFilters); | |
| document.getElementById('statusFilter').addEventListener('change', applyFilters); | |
| document.getElementById('qualityFilter').addEventListener('change', applyFilters); | |
| // Modal events | |
| document.addEventListener('click', (e) => { | |
| const modal = document.getElementById('documentModal'); | |
| if (e.target === modal) { | |
| closeModal(); | |
| } | |
| }); | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'Escape') { | |
| closeModal(); | |
| } | |
| }); | |
| } | |
| async function applyFilters() { | |
| currentFilters = { | |
| search: document.getElementById('searchFilter').value.trim(), | |
| category: document.getElementById('categoryFilter').value, | |
| status: document.getElementById('statusFilter').value, | |
| min_quality: document.getElementById('qualityFilter').value | |
| }; | |
| currentPage = 1; // Reset to first page | |
| await loadDocuments(); | |
| } | |
| function resetFilters() { | |
| document.getElementById('searchFilter').value = ''; | |
| document.getElementById('categoryFilter').value = ''; | |
| document.getElementById('statusFilter').value = ''; | |
| document.getElementById('qualityFilter').value = ''; | |
| currentFilters = {}; | |
| currentPage = 1; | |
| loadDocuments(); | |
| showToast('فیلترها پاک شدند', 'info', 'بازنشانی'); | |
| } | |
| async function refreshDocuments() { | |
| await loadDocuments(); | |
| showToast('لیست اسناد بهروزرسانی شد', 'success', 'بهروزرسانی'); | |
| } | |
| async function viewDocument(documentId) { | |
| try { | |
| let document; | |
| if (isOnline) { | |
| document = await window.legalAPI.getDocument(documentId); | |
| } else { | |
| // Find in local documents | |
| document = documents.find(d => d.id === documentId); | |
| } | |
| if (!document) { | |
| showToast('سند یافت نشد', 'error', 'خطا'); | |
| return; | |
| } | |
| showDocumentModal(document); | |
| } catch (error) { | |
| console.error('Failed to load document:', error); | |
| showToast('خطا در بارگذاری جزئیات سند', 'error', 'خطا'); | |
| } | |
| } | |
| function showDocumentModal(document) { | |
| const modal = document.getElementById('documentModal'); | |
| const modalTitle = document.getElementById('modalTitle'); | |
| const documentMetadata = document.getElementById('documentMetadata'); | |
| const ocrText = document.getElementById('ocrText'); | |
| modalTitle.textContent = document.original_filename || document.filename; | |
| // Update metadata | |
| documentMetadata.innerHTML = ` | |
| <div class="metadata-item"> | |
| <div class="metadata-label">نام فایل اصلی</div> | |
| <div class="metadata-value">${document.original_filename || document.filename}</div> | |
| </div> | |
| <div class="metadata-item"> | |
| <div class="metadata-label">نام فایل سیستم</div> | |
| <div class="metadata-value">${document.filename}</div> | |
| </div> | |
| <div class="metadata-item"> | |
| <div class="metadata-label">اندازه فایل</div> | |
| <div class="metadata-value">${formatFileSize(document.file_size)}</div> | |
| </div> | |
| <div class="metadata-item"> | |
| <div class="metadata-label">دستهبندی</div> | |
| <div class="metadata-value">${document.category}</div> | |
| </div> | |
| <div class="metadata-item"> | |
| <div class="metadata-label">امتیاز کیفیت</div> | |
| <div class="metadata-value">${document.quality_score ? document.quality_score.toFixed(1) : '0.0'} / 10</div> | |
| </div> | |
| <div class="metadata-item"> | |
| <div class="metadata-label">وضعیت</div> | |
| <div class="metadata-value">${document.getStatusText ? document.getStatusText() : document.status}</div> | |
| </div> | |
| <div class="metadata-item"> | |
| <div class="metadata-label">تاریخ ایجاد</div> | |
| <div class="metadata-value">${formatDate(document.created_at)}</div> | |
| </div> | |
| <div class="metadata-item"> | |
| <div class="metadata-label">خلاصه محتوا</div> | |
| <div class="metadata-value">${document.summary || 'در دسترس نیست'}</div> | |
| </div> | |
| `; | |
| // Update OCR text | |
| ocrText.textContent = document.ocr_text || 'متن استخراج شده در دسترس نیست.'; | |
| modal.classList.add('show'); | |
| } | |
| function closeModal() { | |
| const modal = document.getElementById('documentModal'); | |
| modal.classList.remove('show'); | |
| } | |
| async function deleteDocument(documentId) { | |
| if (!confirm('آیا از حذف این سند اطمینان دارید؟')) { | |
| return; | |
| } | |
| try { | |
| if (isOnline) { | |
| await window.legalAPI.deleteDocument(documentId); | |
| } | |
| // Remove from local array | |
| documents = documents.filter(doc => doc.id !== documentId); | |
| totalDocuments = Math.max(0, totalDocuments - 1); | |
| displayDocuments(); | |
| updateStats(); | |
| showToast('سند با موفقیت حذف شد', 'success', 'حذف موفق'); | |
| } catch (error) { | |
| console.error('Failed to delete document:', error); | |
| showToast('خطا در حذف سند', 'error', 'خطا'); | |
| } | |
| } | |
| function downloadDocument(documentId) { | |
| const doc = documents.find(d => d.id === documentId); | |
| if (!doc) return; | |
| showToast(`دانلود ${doc.original_filename} شروع شد`, 'info', 'دانلود'); | |
| } | |
| function exportDocuments() { | |
| showToast('فایل Excel در حال آمادهسازی...', 'info', 'خروجی Excel'); | |
| setTimeout(() => { | |
| showToast('فایل Excel آماده شد و دانلود شروع شد', 'success', 'خروجی موفق'); | |
| }, 2000); | |
| } | |
| function updateStats() { | |
| const processedCount = documents.filter(doc => doc.status === 'processed').length; | |
| const pendingCount = documents.filter(doc => doc.status === 'processing' || doc.status === 'pending').length; | |
| const errorCount = documents.filter(doc => doc.status === 'error').length; | |
| document.getElementById('totalCount').textContent = totalDocuments; | |
| document.getElementById('processedCount').textContent = processedCount; | |
| document.getElementById('pendingCount').textContent = pendingCount; | |
| document.getElementById('errorCount').textContent = errorCount; | |
| // Update badge | |
| const badge = document.getElementById('totalDocumentsBadge'); | |
| if (badge) { | |
| badge.textContent = totalDocuments; | |
| } | |
| } | |
| function updatePagination(pagination) { | |
| const paginationContainer = document.getElementById('pagination'); | |
| const totalPages = pagination.pages; | |
| if (totalPages <= 1) { | |
| paginationContainer.style.display = 'none'; | |
| return; | |
| } | |
| paginationContainer.style.display = 'flex'; | |
| let paginationHTML = ''; | |
| // Previous button | |
| paginationHTML += ` | |
| <button class="pagination-btn" ${currentPage <= 1 ? 'disabled' : ''} | |
| onclick="changePage(${currentPage - 1})"> | |
| <i class="fas fa-chevron-right"></i> | |
| </button> | |
| `; | |
| // Page numbers | |
| const startPage = Math.max(1, currentPage - 2); | |
| const endPage = Math.min(totalPages, currentPage + 2); | |
| for (let i = startPage; i <= endPage; i++) { | |
| paginationHTML += ` | |
| <button class="pagination-btn ${i === currentPage ? 'active' : ''}" | |
| onclick="changePage(${i})"> | |
| ${i} | |
| </button> | |
| `; | |
| } | |
| // Next button | |
| paginationHTML += ` | |
| <button class="pagination-btn" ${currentPage >= totalPages ? 'disabled' : ''} | |
| onclick="changePage(${currentPage + 1})"> | |
| <i class="fas fa-chevron-left"></i> | |
| </button> | |
| `; | |
| paginationContainer.innerHTML = paginationHTML; | |
| } | |
| async function changePage(page) { | |
| currentPage = page; | |
| await loadDocuments(); | |
| } | |
| function showLoading(show) { | |
| const loading = document.getElementById('loadingContainer'); | |
| const table = document.getElementById('documentsTable'); | |
| const emptyState = document.getElementById('emptyState'); | |
| if (show) { | |
| loading.style.display = 'block'; | |
| table.style.display = 'none'; | |
| emptyState.style.display = 'none'; | |
| } else { | |
| loading.style.display = 'none'; | |
| } | |
| } | |
| console.log('📄 Documents Management Page Ready!'); | |
| </script> | |
| </body> | |
| </html> |