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"> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.min.js"></script> | |
| <script src="js/api-client.js"></script> | |
| <script src="js/core.js"></script> | |
| <script src="js/api-connection-test.js"></script> | |
| <script src="js/file-upload-handler.js"></script> | |
| <script src="js/document-crud.js"></script> | |
| <script src="js/scraping-control.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); | |
| --sidebar-bg: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); | |
| /* گرادیانهای مدرن */ | |
| --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); | |
| --shadow-glow-primary: 0 0 20px rgba(59, 130, 246, 0.15); | |
| --shadow-glow-success: 0 0 20px rgba(16, 185, 129, 0.15); | |
| --shadow-glow-warning: 0 0 20px rgba(245, 158, 11, 0.15); | |
| --shadow-glow-danger: 0 0 20px rgba(239, 68, 68, 0.15); | |
| /* متغیرهای کامپکت */ | |
| --sidebar-width: 260px; | |
| --border-radius: 12px; | |
| --border-radius-sm: 8px; | |
| --border-radius-lg: 16px; | |
| --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; | |
| } | |
| html { | |
| 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); | |
| margin: 0; | |
| padding: 0; | |
| } | |
| /* اسکرولبار مدرن */ | |
| ::-webkit-scrollbar { | |
| inline-size: 6px; | |
| block-size: 6px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: rgba(0, 0, 0, 0.02); | |
| border-radius: 10px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--primary-gradient); | |
| border-radius: 10px; | |
| box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3); | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: var(--secondary-gradient); | |
| } | |
| /* کلاسهای کمکی */ | |
| .sr-only { | |
| position: absolute; | |
| inline-size: 1px; | |
| block-size: 1px; | |
| padding: 0; | |
| margin: -1px; | |
| overflow: hidden; | |
| clip: rect(0, 0, 0, 0); | |
| white-space: nowrap; | |
| border: 0; | |
| } | |
| /* کانتینر اصلی */ | |
| .dashboard-container { | |
| display: flex; | |
| min-block-size: 100vh; | |
| inline-size: 100%; | |
| position: relative; | |
| overflow-x: hidden; | |
| } | |
| /* سایدبار کامپکت و بهبود یافته */ | |
| .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); | |
| -webkit-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; | |
| overflow-x: hidden; | |
| 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-inline-start: 1px solid rgba(59, 130, 246, 0.15); | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .sidebar::before { | |
| content: ''; | |
| position: absolute; | |
| inset-block-start: 0; | |
| inset-inline-start: 0; | |
| inset-inline-end: 0; | |
| inset-block-end: 0; | |
| background: | |
| radial-gradient(circle at 20% 20%, rgba(59, 130, 246, 0.03) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 80%, rgba(16, 185, 129, 0.02) 0%, transparent 50%), | |
| linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%); | |
| pointer-events: none; | |
| border-radius: inherit; | |
| } | |
| .sidebar.collapsed { | |
| inline-size: 70px; | |
| } | |
| .sidebar.collapsed .logo-text, | |
| .sidebar.collapsed .nav-title, | |
| .sidebar.collapsed .nav-link span:not(.nav-badge), | |
| .sidebar.collapsed .nav-badge, | |
| .sidebar.collapsed .submenu-toggle { | |
| opacity: 0; | |
| visibility: hidden; | |
| pointer-events: none; | |
| transition: opacity 0.2s ease, visibility 0.2s ease; | |
| } | |
| .sidebar.collapsed .nav-link { | |
| justify-content: center; | |
| padding: 0.6rem; | |
| position: relative; | |
| } | |
| .sidebar.collapsed .nav-link:hover::after { | |
| content: attr(data-tooltip); | |
| position: absolute; | |
| inset-inline-start: 100%; | |
| inset-block-start: 50%; | |
| transform: translateY(-50%); | |
| background: rgba(0, 0, 0, 0.8); | |
| color: white; | |
| padding: 0.5rem 0.8rem; | |
| border-radius: 4px; | |
| font-size: 0.8rem; | |
| white-space: nowrap; | |
| z-index: 10001; | |
| margin-inline-start: 0.5rem; | |
| opacity: 0; | |
| animation: fadeIn 0.2s ease forwards; | |
| } | |
| @keyframes fadeIn { | |
| to { opacity: 1; } | |
| } | |
| .sidebar.collapsed .nav-icon { | |
| margin-inline-start: 0; | |
| } | |
| .sidebar.collapsed .submenu { | |
| display: none; | |
| } | |
| .sidebar.collapsed .sidebar-header { | |
| justify-content: center; | |
| } | |
| .sidebar.collapsed .logo { | |
| gap: 0; | |
| } | |
| .sidebar.collapsed + .main-content { | |
| margin-inline-end: 70px; | |
| inline-size: calc(100% - 70px); | |
| max-inline-size: calc(100% - 70px); | |
| } | |
| .sidebar-header { | |
| padding: 0 1rem 1rem; | |
| border-block-end: 1px solid rgba(59, 130, 246, 0.12); | |
| margin-block-end: 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); | |
| -webkit-backdrop-filter: blur(10px); | |
| position: relative; | |
| } | |
| .sidebar-header::before { | |
| content: ''; | |
| position: absolute; | |
| inset-block-start: 0; | |
| inset-inline-start: 0; | |
| inset-inline-end: 0; | |
| inset-block-end: 0; | |
| background: linear-gradient(135deg, | |
| rgba(59, 130, 246, 0.05) 0%, | |
| rgba(16, 185, 129, 0.03) 100%); | |
| border-radius: inherit; | |
| pointer-events: none; | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.6rem; | |
| color: var(--text-primary); | |
| transition: var(--transition-smooth); | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .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; | |
| box-shadow: | |
| var(--shadow-glow-primary), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.3); | |
| position: relative; | |
| } | |
| .logo-icon::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 1px; | |
| background: linear-gradient(135deg, | |
| rgba(255, 255, 255, 0.2) 0%, | |
| transparent 50%, | |
| rgba(0, 0, 0, 0.1) 100%); | |
| border-radius: inherit; | |
| pointer-events: none; | |
| } | |
| .sidebar-toggle { | |
| background: var(--primary-gradient); | |
| border: none; | |
| inline-size: 2rem; | |
| block-size: 2rem; | |
| border-radius: var(--border-radius-sm); | |
| color: white; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: var(--transition-smooth); | |
| box-shadow: | |
| var(--shadow-sm), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.3); | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .sidebar-toggle::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 1px; | |
| background: linear-gradient(135deg, | |
| rgba(255, 255, 255, 0.2) 0%, | |
| transparent 50%); | |
| border-radius: inherit; | |
| pointer-events: none; | |
| } | |
| .sidebar-toggle:hover { | |
| transform: scale(1.05); | |
| box-shadow: | |
| var(--shadow-glow-primary), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.4); | |
| } | |
| .logo-text { | |
| font-size: var(--font-size-lg); | |
| font-weight: 700; | |
| background: var(--primary-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| transition: var(--transition-smooth); | |
| } | |
| /* منوی کامپکت */ | |
| .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); | |
| transition: var(--transition-smooth); | |
| background: linear-gradient(90deg, | |
| var(--text-secondary) 0%, | |
| var(--primary-gradient) 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| position: relative; | |
| } | |
| .nav-title::after { | |
| content: ''; | |
| position: absolute; | |
| inset-block-end: 0; | |
| inset-inline-end: 1rem; | |
| inset-inline-start: 1rem; | |
| block-size: 1px; | |
| background: linear-gradient(90deg, | |
| transparent 0%, | |
| rgba(59, 130, 246, 0.3) 50%, | |
| transparent 100%); | |
| } | |
| .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); | |
| position: relative; | |
| font-weight: 500; | |
| font-size: var(--font-size-sm); | |
| cursor: pointer; | |
| border: 1px solid transparent; | |
| background: transparent; | |
| } | |
| .nav-link::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| background: linear-gradient(135deg, | |
| rgba(59, 130, 246, 0.0) 0%, | |
| rgba(59, 130, 246, 0.0) 100%); | |
| border-radius: inherit; | |
| transition: var(--transition-smooth); | |
| opacity: 0; | |
| } | |
| .nav-link:hover { | |
| color: var(--text-primary); | |
| transform: translateX(-2px); | |
| border-color: rgba(59, 130, 246, 0.15); | |
| box-shadow: | |
| 0 2px 8px rgba(59, 130, 246, 0.08), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.1); | |
| } | |
| .nav-link:hover::before { | |
| background: linear-gradient(135deg, | |
| rgba(59, 130, 246, 0.08) 0%, | |
| rgba(16, 185, 129, 0.04) 100%); | |
| opacity: 1; | |
| } | |
| .nav-link.active { | |
| background: var(--primary-gradient); | |
| color: var(--text-light); | |
| box-shadow: | |
| var(--shadow-glow-primary), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.3), | |
| 0 4px 12px rgba(59, 130, 246, 0.4); | |
| border-color: rgba(59, 130, 246, 0.5); | |
| position: relative; | |
| } | |
| .nav-link.active::before { | |
| background: linear-gradient(135deg, | |
| rgba(255, 255, 255, 0.1) 0%, | |
| transparent 50%, | |
| rgba(0, 0, 0, 0.05) 100%); | |
| opacity: 1; | |
| } | |
| .nav-link.active::after { | |
| content: ''; | |
| position: absolute; | |
| inset-inline-end: -0.5rem; | |
| inset-block-start: 50%; | |
| transform: translateY(-50%); | |
| inline-size: 3px; | |
| block-size: 1.5rem; | |
| background: var(--primary-gradient); | |
| border-radius: 2px; | |
| box-shadow: | |
| 0 0 8px rgba(59, 130, 246, 0.6), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.3); | |
| } | |
| .nav-icon { | |
| margin-inline-start: 0.6rem; | |
| inline-size: 1rem; | |
| text-align: center; | |
| font-size: 0.9rem; | |
| opacity: 0.9; | |
| transition: var(--transition-smooth); | |
| } | |
| .submenu-toggle { | |
| background: none; | |
| border: none; | |
| color: var(--text-secondary); | |
| padding: 0.2rem; | |
| cursor: pointer; | |
| margin-inline-end: auto; | |
| transition: var(--transition-fast); | |
| font-size: 0.8rem; | |
| } | |
| .submenu-toggle:hover { | |
| color: var(--text-primary); | |
| } | |
| .submenu-toggle.open { | |
| transform: rotate(90deg); | |
| } | |
| .submenu { | |
| max-block-size: 0; | |
| overflow: hidden; | |
| transition: max-height 0.25s ease-out; | |
| margin-block-start: 0.15rem; | |
| } | |
| .submenu.open { | |
| max-block-size: 800px; | |
| } | |
| .submenu .nav-link { | |
| padding: 0.45rem 0.6rem 0.45rem 2rem; | |
| font-size: var(--font-size-xs); | |
| color: var(--text-secondary); | |
| margin: 0.1rem 0; | |
| background: rgba(255, 255, 255, 0.3); | |
| backdrop-filter: blur(5px); | |
| -webkit-backdrop-filter: blur(5px); | |
| border: 1px solid rgba(59, 130, 246, 0.08); | |
| position: relative; | |
| } | |
| .submenu .nav-link::before { | |
| content: ''; | |
| position: absolute; | |
| inset-inline-end: 1.2rem; | |
| inset-block-start: 50%; | |
| transform: translateY(-50%); | |
| inline-size: 3px; | |
| block-size: 3px; | |
| background: var(--primary-gradient); | |
| border-radius: 50%; | |
| opacity: 0.6; | |
| } | |
| .submenu .nav-link:hover { | |
| color: var(--text-primary); | |
| background: rgba(59, 130, 246, 0.08); | |
| border-color: rgba(59, 130, 246, 0.15); | |
| box-shadow: | |
| 0 2px 8px rgba(59, 130, 246, 0.1), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.2); | |
| } | |
| .submenu .nav-link:hover::before { | |
| opacity: 1; | |
| transform: translateY(-50%) scale(1.2); | |
| } | |
| .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; | |
| box-shadow: | |
| 0 2px 4px rgba(239, 68, 68, 0.3), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.3); | |
| position: relative; | |
| } | |
| .nav-badge::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 1px; | |
| background: linear-gradient(135deg, | |
| rgba(255, 255, 255, 0.2) 0%, | |
| transparent 70%); | |
| border-radius: inherit; | |
| pointer-events: none; | |
| } | |
| /* محتوای اصلی */ | |
| .main-content { | |
| flex: 1; | |
| margin-inline-end: var(--sidebar-width); | |
| padding: 1rem; | |
| min-block-size: 100vh; | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| inline-size: calc(100% - var(--sidebar-width)); | |
| max-inline-size: calc(100% - var(--sidebar-width)); | |
| box-sizing: border-box; | |
| } | |
| .main-content.sidebar-collapsed { | |
| margin-inline-end: 70px; | |
| inline-size: calc(100% - 70px); | |
| max-inline-size: calc(100% - 70px); | |
| } | |
| /* هدر کامپکت */ | |
| .dashboard-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-block-end: 1.2rem; | |
| padding: 0.8rem 0; | |
| } | |
| .dashboard-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; | |
| position: relative; | |
| } | |
| .dashboard-title::after { | |
| content: ''; | |
| position: absolute; | |
| inset-block-end: -0.3rem; | |
| inset-inline-end: 0; | |
| inline-size: 2.5rem; | |
| block-size: 2px; | |
| background: var(--primary-gradient); | |
| border-radius: 2px; | |
| } | |
| .title-icon { | |
| background: var(--primary-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .header-actions { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.8rem; | |
| } | |
| .search-container { | |
| position: relative; | |
| } | |
| .search-input { | |
| inline-size: 280px; | |
| padding: 0.6rem 1rem 0.6rem 2.2rem; | |
| border: none; | |
| border-radius: 20px; | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| box-shadow: var(--shadow-sm); | |
| font-family: inherit; | |
| font-size: var(--font-size-sm); | |
| color: var(--text-primary); | |
| transition: var(--transition-smooth); | |
| border: 1px solid var(--glass-border); | |
| } | |
| .search-input::placeholder { | |
| color: var(--text-secondary); | |
| } | |
| .search-input:focus { | |
| outline: none; | |
| box-shadow: var(--shadow-glow-primary); | |
| background: var(--card-bg); | |
| border-color: rgba(59, 130, 246, 0.3); | |
| } | |
| .search-icon { | |
| position: absolute; | |
| inset-inline-end: 0.8rem; | |
| inset-block-start: 50%; | |
| transform: translateY(-50%); | |
| color: var(--text-secondary); | |
| font-size: 0.9rem; | |
| } | |
| .user-profile { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.6rem; | |
| padding: 0.4rem 0.8rem; | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border-radius: 18px; | |
| box-shadow: var(--shadow-sm); | |
| cursor: pointer; | |
| transition: var(--transition-smooth); | |
| border: 1px solid var(--glass-border); | |
| } | |
| .user-profile:hover { | |
| box-shadow: var(--shadow-md); | |
| transform: translateY(-1px); | |
| } | |
| .user-avatar { | |
| inline-size: 1.8rem; | |
| block-size: 1.8rem; | |
| border-radius: 50%; | |
| background: var(--primary-gradient); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: white; | |
| font-weight: 600; | |
| font-size: var(--font-size-sm); | |
| box-shadow: | |
| 0 4px 12px rgba(59, 130, 246, 0.3), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.3), | |
| inset 0 -1px 0 rgba(0, 0, 0, 0.1); | |
| position: relative; | |
| } | |
| .user-avatar::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 1px; | |
| background: linear-gradient(135deg, | |
| rgba(255, 255, 255, 0.2) 0%, | |
| transparent 50%, | |
| rgba(0, 0, 0, 0.05) 100%); | |
| border-radius: inherit; | |
| pointer-events: none; | |
| } | |
| .user-info { | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .user-name { | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| font-size: var(--font-size-sm); | |
| } | |
| .user-role { | |
| font-size: var(--font-size-xs); | |
| color: var(--text-secondary); | |
| } | |
| /* کارتهای آمار کامپکت */ | |
| .stats-grid { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 1rem; | |
| margin-block-end: 1.2rem; | |
| } | |
| .stat-card { | |
| background: var(--card-bg); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border-radius: var(--border-radius); | |
| padding: 1.2rem; | |
| box-shadow: | |
| 0 4px 20px rgba(0, 0, 0, 0.08), | |
| 0 2px 8px rgba(0, 0, 0, 0.06), | |
| 0 1px 3px rgba(0, 0, 0, 0.12), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.6); | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| position: relative; | |
| overflow: hidden; | |
| transition: var(--transition-smooth); | |
| min-block-size: 130px; | |
| } | |
| .stat-card::before { | |
| content: ''; | |
| position: absolute; | |
| inset-block-start: 0; | |
| inset-inline-start: 0; | |
| inset-inline-end: 0; | |
| block-size: 4px; | |
| background: var(--primary-gradient); | |
| } | |
| .stat-card::after { | |
| content: ''; | |
| position: absolute; | |
| inset-block-start: 0; | |
| inset-inline-start: 0; | |
| inset-inline-end: 0; | |
| inset-block-end: 0; | |
| background: linear-gradient(135deg, | |
| rgba(255, 255, 255, 0.1) 0%, | |
| rgba(255, 255, 255, 0.05) 50%, | |
| transparent 100%); | |
| pointer-events: none; | |
| } | |
| .stat-card.primary::before { background: var(--primary-gradient); } | |
| .stat-card.success::before { background: var(--success-gradient); } | |
| .stat-card.danger::before { background: var(--danger-gradient); } | |
| .stat-card.warning::before { background: var(--warning-gradient); } | |
| .stat-card:hover { | |
| transform: translateY(-6px) scale(1.02); | |
| box-shadow: | |
| 0 12px 35px rgba(0, 0, 0, 0.15), | |
| 0 6px 20px rgba(0, 0, 0, 0.1), | |
| 0 2px 8px rgba(0, 0, 0, 0.12), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.8); | |
| } | |
| .stat-card.primary:hover { | |
| box-shadow: | |
| 0 12px 35px rgba(59, 130, 246, 0.25), | |
| 0 6px 20px rgba(59, 130, 246, 0.15), | |
| 0 2px 8px rgba(59, 130, 246, 0.1), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.8); | |
| } | |
| .stat-card.success:hover { | |
| box-shadow: | |
| 0 12px 35px rgba(16, 185, 129, 0.25), | |
| 0 6px 20px rgba(16, 185, 129, 0.15), | |
| 0 2px 8px rgba(16, 185, 129, 0.1), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.8); | |
| } | |
| .stat-card.danger:hover { | |
| box-shadow: | |
| 0 12px 35px rgba(239, 68, 68, 0.25), | |
| 0 6px 20px rgba(239, 68, 68, 0.15), | |
| 0 2px 8px rgba(239, 68, 68, 0.1), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.8); | |
| } | |
| .stat-card.warning:hover { | |
| box-shadow: | |
| 0 12px 35px rgba(245, 158, 11, 0.25), | |
| 0 6px 20px rgba(245, 158, 11, 0.15), | |
| 0 2px 8px rgba(245, 158, 11, 0.1), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.8); | |
| } | |
| .stat-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: flex-start; | |
| margin-block-end: 0.8rem; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .stat-icon { | |
| inline-size: 2.2rem; | |
| block-size: 2.2rem; | |
| border-radius: var(--border-radius-sm); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1.1rem; | |
| box-shadow: | |
| 0 4px 12px rgba(0, 0, 0, 0.15), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.3), | |
| inset 0 -1px 0 rgba(0, 0, 0, 0.1); | |
| opacity: 0.95; | |
| transition: var(--transition-smooth); | |
| position: relative; | |
| } | |
| .stat-icon::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 1px; | |
| background: linear-gradient(135deg, | |
| rgba(255, 255, 255, 0.2) 0%, | |
| transparent 50%, | |
| rgba(0, 0, 0, 0.05) 100%); | |
| border-radius: inherit; | |
| pointer-events: none; | |
| } | |
| .stat-card:hover .stat-icon { | |
| transform: scale(1.08); | |
| box-shadow: | |
| 0 6px 16px rgba(0, 0, 0, 0.2), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.4), | |
| inset 0 -1px 0 rgba(0, 0, 0, 0.15); | |
| } | |
| .stat-icon.primary { background: var(--primary-gradient); color: white; } | |
| .stat-icon.success { background: var(--success-gradient); color: white; } | |
| .stat-icon.danger { background: var(--danger-gradient); color: white; } | |
| .stat-icon.warning { background: var(--warning-gradient); color: white; } | |
| .stat-content { | |
| flex: 1; | |
| } | |
| .stat-title { | |
| font-size: var(--font-size-xs); | |
| color: var(--text-secondary); | |
| font-weight: 600; | |
| margin-block-end: 0.3rem; | |
| line-height: 1.3; | |
| } | |
| .stat-value { | |
| font-size: var(--font-size-xl); | |
| font-weight: 800; | |
| color: var(--text-primary); | |
| line-height: 1; | |
| margin-block-end: 0.3rem; | |
| } | |
| .stat-extra { | |
| font-size: var(--font-size-xs); | |
| color: var(--text-muted); | |
| margin-block-end: 0.3rem; | |
| } | |
| .stat-change { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.25rem; | |
| font-size: var(--font-size-xs); | |
| font-weight: 700; | |
| } | |
| .stat-change.positive { color: #059669; } | |
| .stat-change.negative { color: #dc2626; } | |
| /* نمودارها */ | |
| .charts-section { | |
| display: grid; | |
| grid-template-columns: 2fr 1fr; | |
| gap: 1.5rem; | |
| margin-block-end: 1.5rem; | |
| } | |
| .chart-card { | |
| background: var(--card-bg); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border-radius: var(--border-radius); | |
| padding: 1.5rem; | |
| box-shadow: | |
| 0 8px 25px rgba(0, 0, 0, 0.1), | |
| 0 4px 12px rgba(0, 0, 0, 0.08), | |
| 0 2px 6px rgba(0, 0, 0, 0.12), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.6); | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| transition: var(--transition-smooth); | |
| position: relative; | |
| } | |
| .chart-card::after { | |
| content: ''; | |
| position: absolute; | |
| inset-block-start: 0; | |
| inset-inline-start: 0; | |
| inset-inline-end: 0; | |
| inset-block-end: 0; | |
| background: linear-gradient(135deg, | |
| rgba(255, 255, 255, 0.08) 0%, | |
| rgba(255, 255, 255, 0.03) 50%, | |
| transparent 100%); | |
| pointer-events: none; | |
| border-radius: inherit; | |
| } | |
| .chart-card:hover { | |
| transform: translateY(-4px); | |
| box-shadow: | |
| 0 12px 35px rgba(0, 0, 0, 0.15), | |
| 0 6px 20px rgba(0, 0, 0, 0.1), | |
| 0 3px 10px rgba(0, 0, 0, 0.12), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.8); | |
| } | |
| .chart-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-block-end: 1.2rem; | |
| } | |
| .chart-title { | |
| font-size: var(--font-size-lg); | |
| font-weight: 700; | |
| color: var(--text-primary); | |
| } | |
| .chart-filters { | |
| display: flex; | |
| gap: 0.3rem; | |
| } | |
| .chart-filter { | |
| padding: 0.3rem 0.8rem; | |
| border: none; | |
| border-radius: 12px; | |
| background: rgba(59, 130, 246, 0.08); | |
| color: var(--text-secondary); | |
| font-family: inherit; | |
| font-size: var(--font-size-xs); | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: var(--transition-fast); | |
| } | |
| .chart-filter:hover { | |
| background: rgba(59, 130, 246, 0.12); | |
| } | |
| .chart-filter.active { | |
| background: var(--primary-gradient); | |
| color: white; | |
| box-shadow: var(--shadow-glow-primary); | |
| } | |
| .chart-container { | |
| block-size: 280px; | |
| position: relative; | |
| } | |
| .chart-placeholder { | |
| block-size: 100%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-direction: column; | |
| color: var(--text-muted); | |
| background: rgba(0, 0, 0, 0.02); | |
| border-radius: var(--border-radius-sm); | |
| border: 2px dashed rgba(0, 0, 0, 0.1); | |
| } | |
| .chart-placeholder i { | |
| font-size: 3rem; | |
| margin-block-end: 1rem; | |
| opacity: 0.3; | |
| } | |
| /* Toast Notifications */ | |
| .toast-container { | |
| position: fixed; | |
| inset-block-start: 1rem; | |
| inset-inline-start: 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-inline-start: 4px solid; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.8rem; | |
| min-inline-size: 300px; | |
| transform: translateX(-100%); | |
| transition: all 0.3s ease; | |
| } | |
| .toast.show { | |
| transform: translateX(0); | |
| } | |
| .toast.success { | |
| border-inline-start-color: #10b981; | |
| } | |
| .toast.error { | |
| border-inline-start-color: #ef4444; | |
| } | |
| .toast.warning { | |
| border-inline-start-color: #f59e0b; | |
| } | |
| .toast.info { | |
| border-inline-start-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-block-end: 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); | |
| } | |
| /* Connection Status */ | |
| .connection-status { | |
| position: fixed; | |
| inset-block-end: 1rem; | |
| inset-inline-start: 1rem; | |
| background: var(--card-bg); | |
| border-radius: var(--border-radius-sm); | |
| padding: 0.5rem 1rem; | |
| box-shadow: var(--shadow-sm); | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| font-size: var(--font-size-xs); | |
| border-inline-start: 3px solid; | |
| z-index: 1000; | |
| } | |
| .connection-status.online { | |
| border-inline-start-color: #10b981; | |
| color: #047857; | |
| } | |
| .connection-status.offline { | |
| border-inline-start-color: #ef4444; | |
| color: #b91c1c; | |
| } | |
| .status-indicator { | |
| inline-size: 8px; | |
| block-size: 8px; | |
| border-radius: 50%; | |
| } | |
| .connection-status.online .status-indicator { | |
| background: #10b981; | |
| animation: pulse 2s infinite; | |
| } | |
| .connection-status.offline .status-indicator { | |
| background: #ef4444; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| /* دکمه منوی موبایل */ | |
| .mobile-menu-toggle { | |
| display: none; | |
| background: var(--glass-bg); | |
| border: 1px solid var(--glass-border); | |
| padding: 0.5rem; | |
| border-radius: var(--border-radius-sm); | |
| color: var(--text-primary); | |
| font-size: 1rem; | |
| cursor: pointer; | |
| transition: var(--transition-fast); | |
| } | |
| .mobile-menu-toggle:hover { | |
| background: var(--primary-gradient); | |
| color: white; | |
| } | |
| /* انیمیشنها */ | |
| @keyframes fadeInUp { | |
| from { | |
| opacity: 0; | |
| transform: translateY(15px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .animate-fade-in { | |
| animation: fadeInUp 0.4s ease-out; | |
| } | |
| /* واکنشگرایی */ | |
| @media (max-inline-size: 1200px) { | |
| .charts-section { | |
| grid-template-columns: 1fr; | |
| } | |
| .search-input { | |
| inline-size: 220px; | |
| } | |
| .stats-grid { | |
| grid-template-columns: repeat(2, 1fr); | |
| } | |
| } | |
| @media (max-inline-size: 992px) { | |
| .mobile-menu-toggle { | |
| display: block; | |
| } | |
| .sidebar { | |
| transform: translateX(100%); | |
| position: fixed; | |
| z-index: 10000; | |
| } | |
| .sidebar.open { | |
| transform: translateX(0); | |
| } | |
| .main-content { | |
| margin-inline-end: 0; | |
| inline-size: 100%; | |
| max-inline-size: 100%; | |
| padding: 1rem; | |
| } | |
| .main-content.sidebar-collapsed { | |
| margin-inline-end: 0; | |
| inline-size: 100%; | |
| max-inline-size: 100%; | |
| } | |
| .dashboard-header { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: 0.8rem; | |
| } | |
| .header-actions { | |
| inline-size: 100%; | |
| justify-content: space-between; | |
| flex-direction: column; | |
| gap: 0.8rem; | |
| } | |
| .search-container { | |
| inline-size: 100%; | |
| } | |
| .search-input { | |
| inline-size: 100%; | |
| } | |
| .sidebar-toggle { | |
| display: none; | |
| } | |
| } | |
| @media (max-inline-size: 768px) { | |
| .main-content { | |
| padding: 0.8rem; | |
| inline-size: 100%; | |
| margin-inline-end: 0; | |
| max-inline-size: 100%; | |
| } | |
| .stats-grid { | |
| grid-template-columns: 1fr; | |
| gap: 0.6rem; | |
| } | |
| .stat-card { | |
| min-block-size: 100px; | |
| padding: 0.8rem; | |
| } | |
| .stat-value { | |
| font-size: var(--font-size-lg); | |
| } | |
| .chart-container { | |
| block-size: 220px; | |
| } | |
| } | |
| @media (max-inline-size: 480px) { | |
| .dashboard-title { | |
| font-size: var(--font-size-xl); | |
| } | |
| .stat-value { | |
| font-size: var(--font-size-base); | |
| } | |
| .chart-card { | |
| padding: 0.8rem; | |
| } | |
| .main-content { | |
| padding: 0.5rem; | |
| inline-size: 100%; | |
| } | |
| } | |
| /* File Upload Section Styles */ | |
| .upload-section { | |
| background: var(--card-bg); | |
| border-radius: var(--border-radius); | |
| padding: 1.5rem; | |
| margin-block-end: 2rem; | |
| box-shadow: var(--shadow-sm); | |
| } | |
| .upload-drop-zone { | |
| border: 2px dashed #cbd5e1; | |
| border-radius: var(--border-radius); | |
| padding: 2rem; | |
| text-align: center; | |
| transition: var(--transition-smooth); | |
| cursor: pointer; | |
| } | |
| .upload-drop-zone:hover, | |
| .upload-drop-zone.drag-over { | |
| border-color: #3b82f6; | |
| background: rgba(59, 130, 246, 0.05); | |
| } | |
| .upload-content { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 1rem; | |
| } | |
| .upload-icon { | |
| font-size: 3rem; | |
| color: #64748b; | |
| } | |
| .upload-btn { | |
| background: var(--primary-gradient); | |
| color: white; | |
| border: none; | |
| padding: 0.8rem 1.5rem; | |
| border-radius: var(--border-radius); | |
| cursor: pointer; | |
| font-weight: 500; | |
| transition: var(--transition-smooth); | |
| } | |
| .upload-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: var(--shadow-glow-primary); | |
| } | |
| .upload-queue { | |
| margin-block-start: 1.5rem; | |
| padding: 1rem; | |
| background: #f8fafc; | |
| border-radius: var(--border-radius); | |
| } | |
| .queue-item { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 0.8rem; | |
| background: white; | |
| border-radius: var(--border-radius-sm); | |
| margin-block-end: 0.5rem; | |
| box-shadow: var(--shadow-xs); | |
| } | |
| .file-info { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.3rem; | |
| } | |
| .file-name { | |
| font-weight: 500; | |
| color: var(--text-primary); | |
| } | |
| .file-size { | |
| font-size: var(--font-size-sm); | |
| color: var(--text-muted); | |
| } | |
| .remove-file { | |
| background: #ef4444; | |
| color: white; | |
| border: none; | |
| padding: 0.3rem 0.6rem; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| font-size: 0.8rem; | |
| } | |
| .upload-progress { | |
| margin-block-start: 1rem; | |
| padding: 1rem; | |
| background: #f8fafc; | |
| border-radius: var(--border-radius); | |
| } | |
| .progress-bar { | |
| inline-size: 100%; | |
| block-size: 8px; | |
| background: #e2e8f0; | |
| border-radius: 4px; | |
| overflow: hidden; | |
| } | |
| .progress-fill { | |
| block-size: 100%; | |
| background: var(--primary-gradient); | |
| transition: width 0.3s ease; | |
| } | |
| .progress-text { | |
| margin-block-start: 0.5rem; | |
| text-align: center; | |
| color: var(--text-secondary); | |
| } | |
| .upload-actions { | |
| margin-block-start: 1rem; | |
| text-align: center; | |
| } | |
| .ocr-results { | |
| margin-block-start: 2rem; | |
| padding: 1rem; | |
| background: #f8fafc; | |
| border-radius: var(--border-radius); | |
| } | |
| .ocr-result { | |
| background: white; | |
| padding: 1rem; | |
| border-radius: var(--border-radius-sm); | |
| margin-block-end: 1rem; | |
| box-shadow: var(--shadow-xs); | |
| } | |
| .extracted-text { | |
| background: #f1f5f9; | |
| padding: 0.8rem; | |
| border-radius: var(--border-radius-sm); | |
| margin-block-start: 0.5rem; | |
| font-family: monospace; | |
| white-space: pre-wrap; | |
| } | |
| /* Document Management Section Styles */ | |
| .documents-section { | |
| background: var(--card-bg); | |
| border-radius: var(--border-radius); | |
| padding: 1.5rem; | |
| margin-block-end: 2rem; | |
| box-shadow: var(--shadow-sm); | |
| } | |
| .section-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-block-end: 1.5rem; | |
| } | |
| .section-title { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.8rem; | |
| font-size: var(--font-size-xl); | |
| color: var(--text-primary); | |
| } | |
| .section-actions { | |
| display: flex; | |
| gap: 0.8rem; | |
| } | |
| .documents-filters { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 1rem; | |
| margin-block-end: 1.5rem; | |
| padding: 1rem; | |
| background: #f8fafc; | |
| border-radius: var(--border-radius); | |
| } | |
| .filter-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .search-input, | |
| .filter-select, | |
| .date-input { | |
| padding: 0.6rem; | |
| border: 1px solid #cbd5e1; | |
| border-radius: var(--border-radius-sm); | |
| font-size: var(--font-size-base); | |
| transition: var(--transition-smooth); | |
| } | |
| .search-input:focus, | |
| .filter-select:focus, | |
| .date-input:focus { | |
| outline: none; | |
| border-color: #3b82f6; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); | |
| } | |
| .documents-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| } | |
| .document-item { | |
| background: white; | |
| border-radius: var(--border-radius); | |
| padding: 1rem; | |
| box-shadow: var(--shadow-xs); | |
| transition: var(--transition-smooth); | |
| } | |
| .document-item:hover { | |
| box-shadow: var(--shadow-md); | |
| transform: translateY(-2px); | |
| } | |
| .document-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-block-end: 0.8rem; | |
| } | |
| .document-title { | |
| font-size: var(--font-size-lg); | |
| color: var(--text-primary); | |
| margin: 0; | |
| } | |
| .document-actions { | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .btn-edit, | |
| .btn-delete { | |
| background: none; | |
| border: none; | |
| padding: 0.4rem; | |
| border-radius: var(--border-radius-sm); | |
| cursor: pointer; | |
| transition: var(--transition-smooth); | |
| } | |
| .btn-edit { | |
| color: #3b82f6; | |
| } | |
| .btn-delete { | |
| color: #ef4444; | |
| } | |
| .btn-edit:hover { | |
| background: rgba(59, 130, 246, 0.1); | |
| } | |
| .btn-delete:hover { | |
| background: rgba(239, 68, 68, 0.1); | |
| } | |
| .document-details { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.8rem; | |
| } | |
| .document-info { | |
| display: flex; | |
| gap: 1rem; | |
| flex-wrap: wrap; | |
| } | |
| .document-status, | |
| .document-quality, | |
| .document-date { | |
| padding: 0.3rem 0.6rem; | |
| border-radius: var(--border-radius-sm); | |
| font-size: var(--font-size-sm); | |
| } | |
| .status-pending { | |
| background: #fef3c7; | |
| color: #92400e; | |
| } | |
| .status-processing { | |
| background: #dbeafe; | |
| color: #1e40af; | |
| } | |
| .status-completed { | |
| background: #d1fae5; | |
| color: #065f46; | |
| } | |
| .status-error { | |
| background: #fee2e2; | |
| color: #991b1b; | |
| } | |
| .document-description { | |
| color: var(--text-secondary); | |
| margin: 0; | |
| } | |
| /* Scraping Control Section Styles */ | |
| .scraping-section { | |
| background: var(--card-bg); | |
| border-radius: var(--border-radius); | |
| padding: 1.5rem; | |
| margin-block-end: 2rem; | |
| box-shadow: var(--shadow-sm); | |
| } | |
| .scraping-controls { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
| gap: 1rem; | |
| margin-block-end: 1.5rem; | |
| padding: 1rem; | |
| background: #f8fafc; | |
| border-radius: var(--border-radius); | |
| } | |
| .control-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .control-group label { | |
| font-weight: 500; | |
| color: var(--text-primary); | |
| } | |
| .form-input, | |
| .form-select { | |
| padding: 0.6rem; | |
| border: 1px solid #cbd5e1; | |
| border-radius: var(--border-radius-sm); | |
| font-size: var(--font-size-base); | |
| transition: var(--transition-smooth); | |
| } | |
| .form-input:focus, | |
| .form-select:focus { | |
| outline: none; | |
| border-color: #3b82f6; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); | |
| } | |
| .scraping-actions { | |
| display: flex; | |
| gap: 1rem; | |
| margin-block-end: 1.5rem; | |
| flex-wrap: wrap; | |
| } | |
| .scraping-status { | |
| padding: 1rem; | |
| background: #f8fafc; | |
| border-radius: var(--border-radius); | |
| margin-block-end: 1.5rem; | |
| } | |
| .status-info { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.8rem; | |
| margin-block-end: 1rem; | |
| } | |
| .status-label { | |
| font-weight: 500; | |
| color: var(--text-primary); | |
| } | |
| .status-value { | |
| padding: 0.3rem 0.8rem; | |
| border-radius: var(--border-radius-sm); | |
| font-size: var(--font-size-sm); | |
| font-weight: 500; | |
| } | |
| .status-idle { | |
| background: #f1f5f9; | |
| color: #64748b; | |
| } | |
| .status-running { | |
| background: #dbeafe; | |
| color: #1e40af; | |
| } | |
| .status-completed { | |
| background: #d1fae5; | |
| color: #065f46; | |
| } | |
| .status-failed { | |
| background: #fee2e2; | |
| color: #991b1b; | |
| } | |
| .status-stopped { | |
| background: #fef3c7; | |
| color: #92400e; | |
| } | |
| .scraping-stats { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); | |
| gap: 1rem; | |
| margin-block-start: 1rem; | |
| } | |
| .stat-item { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 0.8rem; | |
| background: white; | |
| border-radius: var(--border-radius-sm); | |
| box-shadow: var(--shadow-xs); | |
| } | |
| .stat-label { | |
| font-size: var(--font-size-sm); | |
| color: var(--text-secondary); | |
| } | |
| .stat-value { | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| } | |
| .scraping-results { | |
| margin-block-end: 1.5rem; | |
| } | |
| .scraping-item { | |
| background: white; | |
| border-radius: var(--border-radius); | |
| padding: 1rem; | |
| margin-block-end: 1rem; | |
| box-shadow: var(--shadow-xs); | |
| transition: var(--transition-smooth); | |
| } | |
| .scraping-item:hover { | |
| box-shadow: var(--shadow-md); | |
| } | |
| .item-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-block-end: 0.8rem; | |
| } | |
| .item-title { | |
| font-size: var(--font-size-lg); | |
| color: var(--text-primary); | |
| margin: 0; | |
| } | |
| .item-rating { | |
| background: #fef3c7; | |
| color: #92400e; | |
| padding: 0.3rem 0.6rem; | |
| border-radius: var(--border-radius-sm); | |
| font-size: var(--font-size-sm); | |
| font-weight: 500; | |
| } | |
| .item-content { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .item-url a { | |
| color: #3b82f6; | |
| text-decoration: none; | |
| } | |
| .item-url a:hover { | |
| text-decoration: underline; | |
| } | |
| .item-description { | |
| color: var(--text-secondary); | |
| margin: 0; | |
| } | |
| .item-meta { | |
| display: flex; | |
| gap: 1rem; | |
| font-size: var(--font-size-sm); | |
| color: var(--text-muted); | |
| } | |
| .scraping-logs { | |
| background: #f8fafc; | |
| border-radius: var(--border-radius); | |
| padding: 1rem; | |
| } | |
| .logs-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-block-end: 1rem; | |
| } | |
| .logs-container { | |
| max-block-size: 300px; | |
| overflow-y: auto; | |
| background: white; | |
| border-radius: var(--border-radius-sm); | |
| padding: 0.8rem; | |
| } | |
| .log-entry { | |
| display: flex; | |
| gap: 0.8rem; | |
| padding: 0.5rem; | |
| border-radius: var(--border-radius-sm); | |
| margin-block-end: 0.3rem; | |
| font-size: var(--font-size-sm); | |
| } | |
| .log-entry.info { | |
| background: #dbeafe; | |
| color: #1e40af; | |
| } | |
| .log-entry.success { | |
| background: #d1fae5; | |
| color: #065f46; | |
| } | |
| .log-entry.error { | |
| background: #fee2e2; | |
| color: #991b1b; | |
| } | |
| .log-timestamp { | |
| font-weight: 500; | |
| min-inline-size: 80px; | |
| } | |
| .log-message { | |
| flex: 1; | |
| } | |
| /* Button Styles */ | |
| .btn { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| padding: 0.6rem 1.2rem; | |
| border: none; | |
| border-radius: var(--border-radius); | |
| font-size: var(--font-size-base); | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: var(--transition-smooth); | |
| text-decoration: none; | |
| } | |
| .btn-primary { | |
| background: var(--primary-gradient); | |
| color: white; | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: var(--shadow-glow-primary); | |
| } | |
| .btn-secondary { | |
| background: #64748b; | |
| color: white; | |
| } | |
| .btn-secondary:hover { | |
| background: #475569; | |
| transform: translateY(-2px); | |
| } | |
| .btn-danger { | |
| background: var(--danger-gradient); | |
| color: white; | |
| } | |
| .btn-danger:hover { | |
| transform: translateY(-2px); | |
| box-shadow: var(--shadow-glow-danger); | |
| } | |
| .btn-sm { | |
| padding: 0.4rem 0.8rem; | |
| font-size: var(--font-size-sm); | |
| } | |
| /* Utility Classes */ | |
| .no-files, | |
| .no-documents, | |
| .no-results { | |
| text-align: center; | |
| color: var(--text-muted); | |
| padding: 2rem; | |
| font-style: italic; | |
| } | |
| .sr-only { | |
| position: absolute; | |
| inline-size: 1px; | |
| block-size: 1px; | |
| padding: 0; | |
| margin: -1px; | |
| overflow: hidden; | |
| clip: rect(0, 0, 0, 0); | |
| white-space: nowrap; | |
| border: 0; | |
| } | |
| /* Enhanced Analytics Dashboard Styles */ | |
| .analytics-dashboard { | |
| margin-block-end: 2rem; | |
| } | |
| .analytics-overview { | |
| margin-block-end: 1.5rem; | |
| } | |
| .analytics-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); | |
| gap: 1.5rem; | |
| margin-block-end: 1.5rem; | |
| } | |
| .analytics-card { | |
| background: var(--card-bg); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border-radius: var(--border-radius-lg); | |
| padding: 1.5rem; | |
| box-shadow: var(--shadow-md); | |
| border: 1px solid var(--glass-border); | |
| transition: var(--transition-smooth); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .analytics-card::before { | |
| content: ''; | |
| position: absolute; | |
| inset-block-start: 0; | |
| inset-inline-start: 0; | |
| inset-inline-end: 0; | |
| block-size: 4px; | |
| background: var(--primary-gradient); | |
| } | |
| .analytics-card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: var(--shadow-lg); | |
| } | |
| .analytics-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-block-end: 1rem; | |
| } | |
| .analytics-title { | |
| font-size: var(--font-size-lg); | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| margin: 0; | |
| } | |
| .analytics-actions { | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .analytics-content { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| } | |
| /* Overview Stats */ | |
| .overview-stats { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 1rem; | |
| margin-block-end: 1rem; | |
| } | |
| .overview-stat { | |
| text-align: center; | |
| padding: 1rem; | |
| background: rgba(255, 255, 255, 0.5); | |
| border-radius: var(--border-radius); | |
| border: 1px solid rgba(59, 130, 246, 0.1); | |
| } | |
| .stat-number { | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| color: var(--text-primary); | |
| margin-block-end: 0.25rem; | |
| } | |
| .stat-label { | |
| font-size: var(--font-size-sm); | |
| color: var(--text-secondary); | |
| } | |
| /* Trends Insights */ | |
| .trends-insights { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .insight-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| padding: 0.5rem; | |
| background: rgba(255, 255, 255, 0.5); | |
| border-radius: var(--border-radius-sm); | |
| font-size: var(--font-size-sm); | |
| } | |
| .text-success { color: #10b981; } | |
| .text-warning { color: #f59e0b; } | |
| .text-danger { color: #ef4444; } | |
| /* Predictions Forecast */ | |
| .predictions-forecast { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .forecast-item { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 0.5rem; | |
| background: rgba(255, 255, 255, 0.5); | |
| border-radius: var(--border-radius-sm); | |
| } | |
| .forecast-label { | |
| font-size: var(--font-size-sm); | |
| color: var(--text-secondary); | |
| } | |
| .forecast-value { | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| } | |
| /* Quality Metrics */ | |
| .quality-metrics { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 1rem; | |
| margin-block-end: 1rem; | |
| } | |
| .quality-metric { | |
| text-align: center; | |
| padding: 0.75rem; | |
| background: rgba(255, 255, 255, 0.5); | |
| border-radius: var(--border-radius-sm); | |
| } | |
| .metric-label { | |
| font-size: var(--font-size-sm); | |
| color: var(--text-secondary); | |
| margin-block-end: 0.25rem; | |
| } | |
| .metric-value { | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| } | |
| /* Health Status */ | |
| .health-status { | |
| margin-block-end: 1rem; | |
| } | |
| .status-indicator { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| padding: 0.5rem; | |
| background: rgba(255, 255, 255, 0.5); | |
| border-radius: var(--border-radius-sm); | |
| font-size: var(--font-size-sm); | |
| } | |
| .status-indicator i { | |
| color: #10b981; | |
| } | |
| .health-metrics { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 1rem; | |
| margin-block-end: 1rem; | |
| } | |
| .health-metric { | |
| text-align: center; | |
| padding: 0.75rem; | |
| background: rgba(255, 255, 255, 0.5); | |
| border-radius: var(--border-radius-sm); | |
| } | |
| /* Clustering Summary */ | |
| .clustering-summary { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-block-end: 1rem; | |
| } | |
| .cluster-count, .cluster-avg { | |
| text-align: center; | |
| padding: 0.75rem; | |
| background: rgba(255, 255, 255, 0.5); | |
| border-radius: var(--border-radius-sm); | |
| flex: 1; | |
| margin: 0 0.5rem; | |
| } | |
| .count-label, .avg-label { | |
| font-size: var(--font-size-sm); | |
| color: var(--text-secondary); | |
| margin-block-end: 0.25rem; | |
| } | |
| .count-value, .avg-value { | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| } | |
| /* Chart Containers */ | |
| .overview-chart, .trends-chart, .predictions-chart, | |
| .quality-chart, .health-chart, .clustering-chart, | |
| .similarity-chart { | |
| block-size: 200px; | |
| position: relative; | |
| } | |
| /* Responsive Design */ | |
| @media (max-inline-size: 768px) { | |
| .analytics-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| .overview-stats, .quality-metrics, .health-metrics { | |
| grid-template-columns: 1fr; | |
| } | |
| .clustering-summary { | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="dashboard-container"> | |
| <!-- سایدبار --> | |
| <aside class="sidebar" id="sidebar"> | |
| <div class="sidebar-header"> | |
| <div class="logo"> | |
| <div class="logo-icon"> | |
| <i class="fas fa-scale-balanced"></i> | |
| </div> | |
| <div class="logo-text">سامانه حقوقی</div> | |
| </div> | |
| <button type="button" class="sidebar-toggle" onclick="toggleSidebar()" aria-label="تغییر وضعیت منو"> | |
| <i class="fas fa-bars"></i> | |
| </button> | |
| </div> | |
| <nav> | |
| <div class="nav-section"> | |
| <h6 class="nav-title">داشبورد</h6> | |
| <ul class="nav-menu"> | |
| <li class="nav-item"> | |
| <a href="index.html" class="nav-link active" data-tooltip="نمای کلی"> | |
| <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="documents.html" class="nav-link" data-tooltip="مدیریت اسناد"> | |
| <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="upload.html" class="nav-link" data-tooltip="آپلود فایل"> | |
| <i class="fas fa-cloud-upload-alt nav-icon"></i> | |
| <span>آپلود فایل</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="search.html" class="nav-link" data-tooltip="جستجو"> | |
| <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="scraping.html" class="nav-link" data-tooltip="استخراج محتوا"> | |
| <i class="fas fa-globe nav-icon"></i> | |
| <span>استخراج محتوا</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="analytics.html" class="nav-link" data-tooltip="آمار و تحلیل"> | |
| <i class="fas fa-chart-line nav-icon"></i> | |
| <span>آمار و تحلیل</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="reports.html" class="nav-link" data-tooltip="گزارشها"> | |
| <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="settings.html" class="nav-link" data-tooltip="تنظیمات"> | |
| <i class="fas fa-cog nav-icon"></i> | |
| <span>تنظیمات</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="#" class="nav-link" data-tooltip="خروج"> | |
| <i class="fas fa-sign-out-alt nav-icon"></i> | |
| <span>خروج</span> | |
| </a> | |
| </li> | |
| </ul> | |
| </div> | |
| </nav> | |
| </aside> | |
| <!-- محتوای اصلی --> | |
| <main class="main-content" id="mainContent"> | |
| <!-- هدر --> | |
| <header class="dashboard-header"> | |
| <div> | |
| <h1 class="dashboard-title"> | |
| <i class="fas fa-chart-pie title-icon"></i> | |
| <span>داشبورد مدیریتی حقوقی</span> | |
| </h1> | |
| </div> | |
| <div class="header-actions"> | |
| <button type="button" class="mobile-menu-toggle" id="mobileMenuToggle" aria-label="منوی موبایل"> | |
| <i class="fas fa-bars"></i> | |
| </button> | |
| <div class="search-container"> | |
| <input type="text" class="search-input" id="searchInput" placeholder="جستجو در اسناد، قوانین، پروندهها..."> | |
| <i class="fas fa-search search-icon"></i> | |
| </div> | |
| <div class="user-profile"> | |
| <div class="user-avatar">ح</div> | |
| <div class="user-info"> | |
| <span class="user-name">حسین محمدی</span> | |
| <span class="user-role">وکیل پایه یک</span> | |
| </div> | |
| <i class="fas fa-chevron-down"></i> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- کارتهای آمار --> | |
| <section aria-labelledby="stats-section"> | |
| <h2 id="stats-section" class="sr-only">آمار و ارقام کلیدی</h2> | |
| <div class="stats-grid"> | |
| <div class="stat-card primary animate-fade-in"> | |
| <div class="stat-header"> | |
| <div class="stat-content"> | |
| <div class="stat-title">کل اسناد جمعآوری شده</div> | |
| <div class="stat-value" id="totalDocuments">6</div> | |
| <div class="stat-extra">در پایگاه داده سیستم</div> | |
| <div class="stat-change positive"> | |
| <i class="fas fa-arrow-up"></i> | |
| <span>+15.2%</span> | |
| </div> | |
| </div> | |
| <div class="stat-icon primary"> | |
| <i class="fas fa-file-alt"></i> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="stat-card success animate-fade-in"> | |
| <div class="stat-header"> | |
| <div class="stat-content"> | |
| <div class="stat-title">اسناد پردازش شده</div> | |
| <div class="stat-value" id="processedDocuments">4</div> | |
| <div class="stat-extra">با موفقیت پردازش شده</div> | |
| <div class="stat-change positive"> | |
| <i class="fas fa-arrow-up"></i> | |
| <span>+23.1%</span> | |
| </div> | |
| </div> | |
| <div class="stat-icon success"> | |
| <i class="fas fa-check-circle"></i> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="stat-card danger animate-fade-in"> | |
| <div class="stat-header"> | |
| <div class="stat-content"> | |
| <div class="stat-title">اسناد دارای خطا</div> | |
| <div class="stat-value" id="errorDocuments">1</div> | |
| <div class="stat-extra">نیازمند بررسی</div> | |
| <div class="stat-change negative"> | |
| <i class="fas fa-arrow-down"></i> | |
| <span>-8.3%</span> | |
| </div> | |
| </div> | |
| <div class="stat-icon danger"> | |
| <i class="fas fa-triangle-exclamation"></i> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="stat-card warning animate-fade-in"> | |
| <div class="stat-header"> | |
| <div class="stat-content"> | |
| <div class="stat-title">امتیاز کیفی میانگین</div> | |
| <div class="stat-value" id="averageQuality">8.1</div> | |
| <div class="stat-extra">از 10 امتیاز</div> | |
| <div class="stat-change positive"> | |
| <i class="fas fa-arrow-up"></i> | |
| <span>+2.1%</span> | |
| </div> | |
| </div> | |
| <div class="stat-icon warning"> | |
| <i class="fas fa-star"></i> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- نمودارها --> | |
| <section class="charts-section"> | |
| <div class="chart-card animate-fade-in"> | |
| <div class="chart-header"> | |
| <h2 class="chart-title">روند پردازش اسناد</h2> | |
| <div class="chart-filters"> | |
| <button type="button" class="chart-filter" onclick="updateChart('daily')">روزانه</button> | |
| <button type="button" class="chart-filter active" onclick="updateChart('weekly')">هفتگی</button> | |
| <button type="button" class="chart-filter" onclick="updateChart('monthly')">ماهانه</button> | |
| </div> | |
| </div> | |
| <div class="chart-container" id="documentsChart"> | |
| <div class="chart-placeholder" id="chartPlaceholder"> | |
| <i class="fas fa-chart-line"></i> | |
| <p>نمودار روند پردازش</p> | |
| <small>Chart.js در حال بارگذاری...</small> | |
| </div> | |
| <canvas id="documentsChartCanvas" style="display: none;"></canvas> | |
| </div> | |
| </div> | |
| <div class="chart-card animate-fade-in"> | |
| <div class="chart-header"> | |
| <h2 class="chart-title">توزیع وضعیت اسناد</h2> | |
| <div class="chart-filters"> | |
| <button type="button" class="chart-filter active" onclick="updateStatusChart('status')">وضعیت</button> | |
| <button type="button" class="chart-filter" onclick="updateStatusChart('category')">دستهبندی</button> | |
| </div> | |
| </div> | |
| <div class="chart-container" id="statusChart"> | |
| <div class="chart-placeholder" id="statusPlaceholder"> | |
| <i class="fas fa-chart-pie"></i> | |
| <p>نمودار توزیع وضعیت</p> | |
| <small>Chart.js در حال بارگذاری...</small> | |
| </div> | |
| <canvas id="statusChartCanvas" style="display: none;"></canvas> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Enhanced Analytics Dashboard --> | |
| <section class="analytics-dashboard animate-fade-in"> | |
| <div class="section-header"> | |
| <h2 class="section-title"> | |
| <i class="fas fa-chart-line"></i> | |
| داشبورد تحلیلی پیشرفته | |
| </h2> | |
| <div class="section-actions"> | |
| <button type="button" class="btn btn-primary" id="refreshAnalyticsBtn"> | |
| <i class="fas fa-sync-alt"></i> | |
| بروزرسانی تحلیلها | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Analytics Overview --> | |
| <div class="analytics-overview"> | |
| <div class="analytics-card"> | |
| <div class="analytics-header"> | |
| <h3 class="analytics-title"> | |
| <i class="fas fa-eye"></i> | |
| نمای کلی | |
| </h3> | |
| <div class="analytics-actions"> | |
| <button type="button" class="btn btn-sm btn-secondary" onclick="refreshOverview()" aria-label="بروزرسانی نمای کلی"> | |
| <i class="fas fa-refresh"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="analytics-content" id="overviewContent"> | |
| <div class="overview-stats"> | |
| <div class="overview-stat"> | |
| <div class="stat-number" id="totalAnalytics">0</div> | |
| <div class="stat-label">کل تحلیلها</div> | |
| </div> | |
| <div class="overview-stat"> | |
| <div class="stat-number" id="activeAnalytics">0</div> | |
| <div class="stat-label">تحلیلهای فعال</div> | |
| </div> | |
| <div class="overview-stat"> | |
| <div class="stat-number" id="accuracyRate">0%</div> | |
| <div class="stat-label">دقت تحلیل</div> | |
| </div> | |
| </div> | |
| <div class="overview-chart" id="overviewChart"> | |
| <canvas id="overviewChartCanvas"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Analytics Grid --> | |
| <div class="analytics-grid"> | |
| <!-- Trends Analytics --> | |
| <div class="analytics-card"> | |
| <div class="analytics-header"> | |
| <h3 class="analytics-title"> | |
| <i class="fas fa-trending-up"></i> | |
| روندها و الگوها | |
| </h3> | |
| <div class="analytics-actions"> | |
| <button type="button" class="btn btn-sm btn-secondary" onclick="refreshTrends()" aria-label="بروزرسانی روندها"> | |
| <i class="fas fa-refresh"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="analytics-content" id="trendsContent"> | |
| <div class="trends-chart" id="trendsChart"> | |
| <canvas id="trendsChartCanvas"></canvas> | |
| </div> | |
| <div class="trends-insights" id="trendsInsights"> | |
| <div class="insight-item"> | |
| <i class="fas fa-arrow-up text-success"></i> | |
| <span>افزایش 15% در پردازش اسناد</span> | |
| </div> | |
| <div class="insight-item"> | |
| <i class="fas fa-clock text-warning"></i> | |
| <span>کاهش زمان پردازش به 2.3 ثانیه</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Predictions Analytics --> | |
| <div class="analytics-card"> | |
| <div class="analytics-header"> | |
| <h3 class="analytics-title"> | |
| <i class="fas fa-crystal-ball"></i> | |
| پیشبینیها | |
| </h3> | |
| <div class="analytics-actions"> | |
| <button type="button" class="btn btn-sm btn-secondary" onclick="refreshPredictions()" aria-label="بروزرسانی پیشبینیها"> | |
| <i class="fas fa-refresh"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="analytics-content" id="predictionsContent"> | |
| <div class="predictions-chart" id="predictionsChart"> | |
| <canvas id="predictionsChartCanvas"></canvas> | |
| </div> | |
| <div class="predictions-forecast" id="predictionsForecast"> | |
| <div class="forecast-item"> | |
| <div class="forecast-label">پیشبینی هفته آینده:</div> | |
| <div class="forecast-value" id="weeklyForecast">0</div> | |
| </div> | |
| <div class="forecast-item"> | |
| <div class="forecast-label">اعتماد به پیشبینی:</div> | |
| <div class="forecast-value" id="confidenceLevel">0%</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Quality Analytics --> | |
| <div class="analytics-card"> | |
| <div class="analytics-header"> | |
| <h3 class="analytics-title"> | |
| <i class="fas fa-award"></i> | |
| ارزیابی کیفیت | |
| </h3> | |
| <div class="analytics-actions"> | |
| <button type="button" class="btn btn-sm btn-secondary" onclick="refreshQuality()" aria-label="بروزرسانی کیفیت"> | |
| <i class="fas fa-refresh"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="analytics-content" id="qualityContent"> | |
| <div class="quality-metrics"> | |
| <div class="quality-metric"> | |
| <div class="metric-label">امتیاز کلی:</div> | |
| <div class="metric-value" id="overallQuality">0</div> | |
| </div> | |
| <div class="quality-metric"> | |
| <div class="metric-label">خوانایی:</div> | |
| <div class="metric-value" id="readabilityScore">0</div> | |
| </div> | |
| <div class="quality-metric"> | |
| <div class="metric-label">دقت:</div> | |
| <div class="metric-value" id="accuracyScore">0</div> | |
| </div> | |
| </div> | |
| <div class="quality-chart" id="qualityChart"> | |
| <canvas id="qualityChartCanvas"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Health Analytics --> | |
| <div class="analytics-card"> | |
| <div class="analytics-header"> | |
| <h3 class="analytics-title"> | |
| <i class="fas fa-heartbeat"></i> | |
| سلامت سیستم | |
| </h3> | |
| <div class="analytics-actions"> | |
| <button type="button" class="btn btn-sm btn-secondary" onclick="refreshHealth()" aria-label="Refresh system health"> | |
| <i class="fas fa-refresh"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="analytics-content" id="healthContent"> | |
| <div class="health-status"> | |
| <div class="status-indicator" id="systemStatus"> | |
| <i class="fas fa-circle"></i> | |
| <span>وضعیت سیستم</span> | |
| </div> | |
| </div> | |
| <div class="health-metrics"> | |
| <div class="health-metric"> | |
| <div class="metric-label">CPU:</div> | |
| <div class="metric-value" id="cpuUsage">0%</div> | |
| </div> | |
| <div class="health-metric"> | |
| <div class="metric-label">RAM:</div> | |
| <div class="metric-value" id="memoryUsage">0%</div> | |
| </div> | |
| <div class="health-metric"> | |
| <div class="metric-label">دیسک:</div> | |
| <div class="metric-value" id="diskUsage">0%</div> | |
| </div> | |
| </div> | |
| <div class="health-chart" id="healthChart"> | |
| <canvas id="healthChartCanvas"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Clustering Analytics --> | |
| <div class="analytics-card"> | |
| <div class="analytics-header"> | |
| <h3 class="analytics-title"> | |
| <i class="fas fa-sitemap"></i> | |
| خوشهبندی اسناد | |
| </h3> | |
| <div class="analytics-actions"> | |
| <button type="button" class="btn btn-sm btn-secondary" onclick="refreshClustering()" aria-label="Refresh clustering analysis"> | |
| <i class="fas fa-refresh"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="analytics-content" id="clusteringContent"> | |
| <div class="clustering-summary"> | |
| <div class="cluster-count"> | |
| <div class="count-label">تعداد خوشهها:</div> | |
| <div class="count-value" id="clusterCount">0</div> | |
| </div> | |
| <div class="cluster-avg"> | |
| <div class="avg-label">میانگین شباهت:</div> | |
| <div class="avg-value" id="avgSimilarity">0%</div> | |
| </div> | |
| </div> | |
| <div class="clustering-chart" id="clusteringChart"> | |
| <canvas id="clusteringChartCanvas"></canvas> | |
| </div> | |
| <div class="clustering-list" id="clusteringList"> | |
| <!-- Cluster items will be loaded here --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Similarity Analytics --> | |
| <div class="analytics-card"> | |
| <div class="analytics-header"> | |
| <h3 class="analytics-title"> | |
| <i class="fas fa-search-plus"></i> | |
| تحلیل شباهت | |
| </h3> | |
| <div class="analytics-actions"> | |
| <button type="button" class="btn btn-sm btn-secondary" onclick="refreshSimilarity()" aria-label="Refresh similarity analysis"> | |
| <i class="fas fa-refresh"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="analytics-content" id="similarityContent"> | |
| <div class="similarity-results" id="similarityResults"> | |
| <!-- Similarity results will be loaded here --> | |
| </div> | |
| <div class="similarity-chart" id="similarityChart"> | |
| <canvas id="similarityChartCanvas"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- File Upload Section --> | |
| <section class="upload-section animate-fade-in"> | |
| <div class="section-header"> | |
| <h2 class="section-title"> | |
| <i class="fas fa-upload"></i> | |
| آپلود و پردازش اسناد | |
| </h2> | |
| </div> | |
| <div class="upload-container"> | |
| <div class="upload-drop-zone" id="uploadDropZone"> | |
| <div class="upload-content"> | |
| <i class="fas fa-cloud-upload-alt upload-icon"></i> | |
| <h3>فایلهای خود را اینجا رها کنید</h3> | |
| <p>یا کلیک کنید تا فایل انتخاب کنید</p> | |
| <input type="file" id="documentUpload" multiple accept=".pdf,.jpg,.jpeg,.png,.tiff" style="display: none;"> | |
| <button type="button" class="upload-btn" onclick="document.getElementById('documentUpload').click()"> | |
| انتخاب فایل | |
| </button> | |
| </div> | |
| </div> | |
| <div class="upload-queue" id="uploadQueue"> | |
| <h4>فایلهای انتخاب شده</h4> | |
| <div class="queue-content"> | |
| <p class="no-files">هیچ فایلی برای آپلود انتخاب نشده</p> | |
| </div> | |
| </div> | |
| <div class="upload-progress" id="uploadProgress" style="display: none;"> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="uploadProgressBar"></div> | |
| </div> | |
| <p class="progress-text" id="uploadProgressText">آپلود در حال انجام...</p> | |
| </div> | |
| <div class="upload-actions"> | |
| <button type="button" class="btn btn-primary" id="uploadButton"> | |
| <i class="fas fa-upload"></i> | |
| شروع آپلود | |
| </button> | |
| </div> | |
| </div> | |
| <div class="ocr-results" id="ocrResults"> | |
| <h4>نتایج پردازش OCR</h4> | |
| <div class="results-content"> | |
| <!-- OCR results will be displayed here --> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Document Management Section --> | |
| <section class="documents-section animate-fade-in"> | |
| <div class="section-header"> | |
| <h2 class="section-title"> | |
| <i class="fas fa-folder-open"></i> | |
| مدیریت اسناد | |
| </h2> | |
| <div class="section-actions"> | |
| <button type="button" class="btn btn-primary" id="createDocumentBtn"> | |
| <i class="fas fa-plus"></i> | |
| ایجاد سند جدید | |
| </button> | |
| </div> | |
| </div> | |
| <div class="documents-filters"> | |
| <div class="filter-group"> | |
| <input type="text" id="documentSearch" placeholder="جستجو در اسناد..." class="search-input"> | |
| </div> | |
| <div class="filter-group"> | |
| <select id="statusFilter" class="filter-select"> | |
| <option value="all">همه وضعیتها</option> | |
| <option value="pending">در انتظار</option> | |
| <option value="processing">در حال پردازش</option> | |
| <option value="completed">تکمیل شده</option> | |
| <option value="error">خطا</option> | |
| </select> | |
| </div> | |
| <div class="filter-group"> | |
| <select id="categoryFilter" class="filter-select"> | |
| <option value="all">همه دستهها</option> | |
| <option value="contracts">قراردادها</option> | |
| <option value="lawsuits">دادخواستها</option> | |
| <option value="judgments">احکام قضایی</option> | |
| <option value="appeals">آرای دیوان</option> | |
| <option value="other">سایر</option> | |
| </select> | |
| </div> | |
| <div class="filter-group"> | |
| <input type="date" id="dateFromFilter" class="date-input"> | |
| </div> | |
| <div class="filter-group"> | |
| <input type="date" id="dateToFilter" class="date-input"> | |
| </div> | |
| </div> | |
| <div class="documents-list" id="documentsList"> | |
| <!-- Documents will be loaded here --> | |
| </div> | |
| </section> | |
| <!-- Scraping Control Section --> | |
| <section class="scraping-section animate-fade-in"> | |
| <div class="section-header"> | |
| <h2 class="section-title"> | |
| <i class="fas fa-spider"></i> | |
| کنترل اسکرپینگ وب | |
| </h2> | |
| </div> | |
| <div class="scraping-controls"> | |
| <div class="control-group"> | |
| <label for="scrapingUrl">آدرس وبسایت:</label> | |
| <input type="url" id="scrapingUrl" placeholder="https://example.com" class="form-input"> | |
| </div> | |
| <div class="control-group"> | |
| <label for="scrapingDepth">عمق اسکرپینگ:</label> | |
| <select id="scrapingDepth" class="form-select"> | |
| <option value="1">سطح 1</option> | |
| <option value="2">سطح 2</option> | |
| <option value="3">سطح 3</option> | |
| </select> | |
| </div> | |
| <div class="control-group"> | |
| <label for="maxPages">حداکثر صفحات:</label> | |
| <input type="number" id="maxPages" value="100" min="1" max="1000" class="form-input"> | |
| </div> | |
| <div class="control-group"> | |
| <label for="scrapingFilters">فیلترها:</label> | |
| <input type="text" id="scrapingFilters" placeholder="کلمات کلیدی (اختیاری)" class="form-input"> | |
| </div> | |
| </div> | |
| <div class="scraping-actions"> | |
| <button type="button" class="btn btn-primary" id="startScrapingBtn"> | |
| <i class="fas fa-play"></i> | |
| شروع اسکرپینگ | |
| </button> | |
| <button type="button" class="btn btn-danger" id="stopScrapingBtn"> | |
| <i class="fas fa-stop"></i> | |
| توقف اسکرپینگ | |
| </button> | |
| <button type="button" class="btn btn-secondary" id="refreshResultsBtn"> | |
| <i class="fas fa-refresh"></i> | |
| بروزرسانی نتایج | |
| </button> | |
| </div> | |
| <div class="scraping-status"> | |
| <div class="status-info"> | |
| <span class="status-label">وضعیت:</span> | |
| <span class="status-value" id="scrapingStatus">آماده</span> | |
| </div> | |
| <div class="progress-container"> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="scrapingProgress">0%</div> | |
| </div> | |
| </div> | |
| <div class="scraping-stats" id="scrapingStats"> | |
| <!-- Statistics will be displayed here --> | |
| </div> | |
| </div> | |
| <div class="scraping-results"> | |
| <h4>نتایج اسکرپینگ</h4> | |
| <div class="results-container" id="scrapingResults"> | |
| <!-- Results will be displayed here --> | |
| </div> | |
| </div> | |
| <div class="scraping-logs"> | |
| <div class="logs-header"> | |
| <h4>لاگهای اسکرپینگ</h4> | |
| <button type="button" class="btn btn-sm btn-secondary" id="clearLogsBtn"> | |
| <i class="fas fa-trash"></i> | |
| پاک کردن لاگها | |
| </button> | |
| </div> | |
| <div class="logs-container" id="scrapingLogs"> | |
| <!-- Logs will be displayed here --> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| </div> | |
| <!-- Toast Container --> | |
| <div class="toast-container" id="toastContainer"></div> | |
| <!-- Connection Status --> | |
| <div class="connection-status online" id="connectionStatus"> | |
| <div class="status-indicator"></div> | |
| <span>متصل به سرور</span> | |
| </div> | |
| <script> | |
| // API Configuration | |
| const API_ENDPOINTS = { | |
| // Dashboard endpoints | |
| dashboardSummary: '/api/dashboard/summary', | |
| chartsData: '/api/dashboard/charts-data', | |
| aiSuggestions: '/api/dashboard/ai-suggestions', | |
| trainAI: '/api/dashboard/ai-feedback', | |
| performanceMetrics: '/api/dashboard/performance-metrics', | |
| trends: '/api/dashboard/trends', | |
| // Documents endpoints | |
| documents: '/api/documents', | |
| documentSearch: '/api/documents/search/', | |
| categories: '/api/documents/categories/', | |
| sources: '/api/documents/sources/', | |
| // OCR endpoints | |
| ocrProcess: '/api/ocr/process', | |
| ocrProcessAndSave: '/api/ocr/process-and-save', | |
| ocrBatchProcess: '/api/ocr/batch-process', | |
| ocrQualityMetrics: '/api/ocr/quality-metrics', | |
| ocrModels: '/api/ocr/models', | |
| ocrStatus: '/api/ocr/status', | |
| // Analytics endpoints | |
| analyticsOverview: '/api/analytics/overview', | |
| analyticsTrends: '/api/analytics/trends', | |
| analyticsSimilarity: '/api/analytics/similarity', | |
| analyticsPerformance: '/api/analytics/performance', | |
| analyticsEntities: '/api/analytics/entities', | |
| analyticsQuality: '/api/analytics/quality-analysis', | |
| // Scraping endpoints | |
| scrapingStart: '/api/scraping/scrape', | |
| scrapingStatus: '/api/scraping/status', | |
| scrapingItems: '/api/scraping/items', | |
| scrapingStatistics: '/api/scraping/statistics', | |
| ratingSummary: '/api/scraping/rating/summary', | |
| // System endpoints | |
| health: '/api/health' | |
| }; | |
| // Enhanced error handling | |
| async function fetchWithErrorHandling(url, options = {}) { | |
| try { | |
| const response = await fetch(url, { | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| ...options.headers | |
| }, | |
| ...options | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP ${response.status}: ${response.statusText}`); | |
| } | |
| return await response.json(); | |
| } catch (error) { | |
| console.error(`API Error (${url}):`, error); | |
| showToast(`خطا در اتصال به سرور: ${error.message}`, 'error'); | |
| throw error; | |
| } | |
| } | |
| // Global variables | |
| let documentsChart = null; | |
| let statusChart = null; | |
| let chartJsLoaded = false; | |
| // Initialize when page loads | |
| document.addEventListener('DOMContentLoaded', function() { | |
| console.log('Dashboard loading...'); | |
| // Check if Chart.js is loaded | |
| setTimeout(() => { | |
| chartJsLoaded = typeof Chart !== 'undefined'; | |
| console.log('Chart.js loaded:', chartJsLoaded); | |
| if (chartJsLoaded) { | |
| initializeCharts(); | |
| } else { | |
| console.warn('Chart.js not loaded, keeping placeholders'); | |
| showToast('Chart.js بارگذاری نشد - نمودارها غیرفعال هستند', 'warning', 'هشدار'); | |
| } | |
| }, 1000); | |
| setupEventListeners(); | |
| loadInitialData(); | |
| showToast('داشبورد با موفقیت بارگذاری شد', 'success', 'خوش آمدید'); | |
| }); | |
| // Initialize charts if Chart.js is available | |
| function initializeCharts() { | |
| if (!chartJsLoaded) return; | |
| try { | |
| // Hide placeholders and show canvases | |
| document.getElementById('chartPlaceholder').style.display = 'none'; | |
| document.getElementById('statusPlaceholder').style.display = 'none'; | |
| document.getElementById('documentsChartCanvas').style.display = 'block'; | |
| document.getElementById('statusChartCanvas').style.display = 'block'; | |
| // Processing trends chart | |
| const documentsCtx = document.getElementById('documentsChartCanvas'); | |
| if (documentsCtx) { | |
| documentsChart = new Chart(documentsCtx, { | |
| type: 'line', | |
| data: { | |
| labels: ['هفته 1', 'هفته 2', 'هفته 3', 'هفته 4'], | |
| datasets: [ | |
| { | |
| label: 'پردازش شده', | |
| data: [85, 92, 78, 95], | |
| borderColor: '#10b981', | |
| backgroundColor: 'rgba(16, 185, 129, 0.1)', | |
| tension: 0.4, | |
| borderWidth: 3, | |
| pointRadius: 6, | |
| pointHoverRadius: 8 | |
| }, | |
| { | |
| label: 'آپلود شده', | |
| data: [95, 105, 88, 110], | |
| borderColor: '#3b82f6', | |
| backgroundColor: 'rgba(59, 130, 246, 0.1)', | |
| tension: 0.4, | |
| borderWidth: 3, | |
| pointRadius: 6, | |
| pointHoverRadius: 8 | |
| } | |
| ] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'top', | |
| labels: { | |
| usePointStyle: true, | |
| padding: 20, | |
| font: { | |
| family: 'Vazirmatn', | |
| size: 12 | |
| } | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| grid: { | |
| color: 'rgba(0, 0, 0, 0.05)' | |
| }, | |
| ticks: { | |
| font: { | |
| family: 'Vazirmatn' | |
| } | |
| } | |
| }, | |
| x: { | |
| grid: { | |
| color: 'rgba(0, 0, 0, 0.05)' | |
| }, | |
| ticks: { | |
| font: { | |
| family: 'Vazirmatn' | |
| } | |
| } | |
| } | |
| }, | |
| interaction: { | |
| intersect: false, | |
| mode: 'index' | |
| } | |
| } | |
| }); | |
| } | |
| // Status distribution chart | |
| const statusCtx = document.getElementById('statusChartCanvas'); | |
| if (statusCtx) { | |
| statusChart = new Chart(statusCtx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['پردازش شده', 'در حال پردازش', 'خطا', 'آپلود شده'], | |
| datasets: [{ | |
| data: [4, 1, 1, 0], | |
| backgroundColor: ['#10b981', '#f59e0b', '#ef4444', '#3b82f6'], | |
| borderColor: '#ffffff', | |
| borderWidth: 3, | |
| hoverBorderWidth: 5 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'bottom', | |
| labels: { | |
| usePointStyle: true, | |
| padding: 15, | |
| font: { | |
| family: 'Vazirmatn', | |
| size: 11 | |
| } | |
| } | |
| } | |
| }, | |
| cutout: '60%' | |
| } | |
| }); | |
| } | |
| console.log('Charts initialized successfully'); | |
| showToast('نمودارها بارگذاری شدند', 'success', 'موفقیت'); | |
| } catch (error) { | |
| console.error('Chart initialization failed:', error); | |
| showToast('خطا در بارگذاری نمودارها', 'error', 'خطا'); | |
| } | |
| } | |
| // Setup event listeners | |
| function setupEventListeners() { | |
| // Mobile menu toggle | |
| const mobileMenuToggle = document.getElementById('mobileMenuToggle'); | |
| const sidebar = document.getElementById('sidebar'); | |
| if (mobileMenuToggle && sidebar) { | |
| mobileMenuToggle.addEventListener('click', () => { | |
| sidebar.classList.toggle('open'); | |
| }); | |
| // Close sidebar when clicking outside on mobile | |
| document.addEventListener('click', (e) => { | |
| if (window.innerWidth <= 992 && | |
| sidebar.classList.contains('open') && | |
| !sidebar.contains(e.target) && | |
| !mobileMenuToggle.contains(e.target)) { | |
| sidebar.classList.remove('open'); | |
| } | |
| }); | |
| } | |
| // Search functionality | |
| const searchInput = document.getElementById('searchInput'); | |
| if (searchInput) { | |
| searchInput.addEventListener('input', function(e) { | |
| const searchTerm = e.target.value.trim(); | |
| if (searchTerm.length > 2) { | |
| showToast(`جستجو برای: ${searchTerm}`, 'info', 'جستجو'); | |
| } | |
| }); | |
| } | |
| } | |
| // Load initial data | |
| async function loadInitialData() { | |
| try { | |
| await Promise.all([ | |
| loadDashboardStats(), | |
| loadChartsData(), | |
| loadAISuggestions() | |
| ]); | |
| showToast('دادهها با موفقیت بارگذاری شدند', 'success'); | |
| } catch (error) { | |
| console.error('Error loading initial data:', error); | |
| showToast('خطا در بارگذاری دادهها', 'error'); | |
| } | |
| } | |
| // Load dashboard statistics from API | |
| async function loadDashboardStats() { | |
| try { | |
| const stats = await fetchWithErrorHandling(API_ENDPOINTS.dashboardSummary); | |
| // Update UI with real data | |
| document.getElementById('totalDocuments').textContent = stats.total_documents || 0; | |
| document.getElementById('processedDocuments').textContent = stats.processed_documents || 0; | |
| document.getElementById('errorDocuments').textContent = stats.error_documents || 0; | |
| document.getElementById('averageQuality').textContent = (stats.average_quality || 0).toFixed(1); | |
| // Update badge | |
| const badge = document.getElementById('totalDocumentsBadge'); | |
| if (badge) { | |
| badge.textContent = stats.total_documents || 0; | |
| } | |
| console.log('Dashboard stats loaded from API'); | |
| } catch (error) { | |
| console.error('Failed to load dashboard stats:', error); | |
| // Fallback to mock data | |
| updateStatsWithMockData(); | |
| } | |
| } | |
| // Load charts data from API | |
| async function loadChartsData() { | |
| try { | |
| const chartsData = await fetchWithErrorHandling(API_ENDPOINTS.chartsData); | |
| updateChartsWithRealData(chartsData); | |
| console.log('Charts data loaded from API'); | |
| } catch (error) { | |
| console.error('Failed to load charts data:', error); | |
| // Keep existing mock charts | |
| } | |
| } | |
| // Load AI suggestions from API | |
| async function loadAISuggestions() { | |
| try { | |
| const suggestions = await fetchWithErrorHandling(API_ENDPOINTS.aiSuggestions); | |
| updateAISuggestions(suggestions.suggestions || []); | |
| console.log('AI suggestions loaded from API'); | |
| } catch (error) { | |
| console.error('Failed to load AI suggestions:', error); | |
| updateAISuggestions([]); | |
| } | |
| } | |
| // Update charts with real data | |
| function updateChartsWithRealData(chartsData) { | |
| if (!chartJsLoaded || !documentsChart) return; | |
| // Update charts with real data from API | |
| if (chartsData.category_distribution) { | |
| // Update status chart with real category data | |
| const categories = Object.keys(chartsData.category_distribution); | |
| const values = Object.values(chartsData.category_distribution); | |
| if (statusChart) { | |
| statusChart.data.labels = categories; | |
| statusChart.data.datasets[0].data = values; | |
| statusChart.update('active'); | |
| } | |
| } | |
| } | |
| // Update AI suggestions | |
| function updateAISuggestions(suggestions) { | |
| const suggestionsContainer = document.getElementById('aiSuggestionsList'); | |
| if (!suggestionsContainer) return; | |
| if (suggestions.length === 0) { | |
| suggestionsContainer.innerHTML = '<p class="no-data">هیچ پیشنهادی یافت نشد</p>'; | |
| return; | |
| } | |
| const suggestionsHTML = suggestions.map(suggestion => ` | |
| <div class="suggestion-item"> | |
| <h4>${suggestion.title || 'بدون عنوان'}</h4> | |
| <p>${suggestion.description || 'توضیحات موجود نیست'}</p> | |
| <span class="suggestion-score">امتیاز: ${suggestion.score || 0}</span> | |
| </div> | |
| `).join(''); | |
| suggestionsContainer.innerHTML = suggestionsHTML; | |
| } | |
| // Fallback to mock data | |
| function updateStatsWithMockData() { | |
| const stats = { | |
| total: 6, | |
| processed: 4, | |
| error: 1, | |
| quality: 8.1 | |
| }; | |
| document.getElementById('totalDocuments').textContent = stats.total; | |
| document.getElementById('processedDocuments').textContent = stats.processed; | |
| document.getElementById('errorDocuments').textContent = stats.error; | |
| document.getElementById('averageQuality').textContent = stats.quality.toFixed(1); | |
| const badge = document.getElementById('totalDocumentsBadge'); | |
| if (badge) { | |
| badge.textContent = stats.total; | |
| } | |
| } | |
| // Chart update functions | |
| function updateChart(period) { | |
| if (!chartJsLoaded || !documentsChart) { | |
| showToast('نمودارها در دسترس نیستند', 'warning', 'هشدار'); | |
| return; | |
| } | |
| // Update active filter | |
| document.querySelectorAll('.chart-filter').forEach(btn => { | |
| btn.classList.remove('active'); | |
| }); | |
| event.target.classList.add('active'); | |
| // Mock data for different periods | |
| const data = { | |
| daily: { | |
| labels: ['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'], | |
| processed: [12, 19, 8, 15, 22, 18, 14], | |
| uploaded: [15, 23, 12, 18, 25, 21, 16] | |
| }, | |
| weekly: { | |
| labels: ['هفته 1', 'هفته 2', 'هفته 3', 'هفته 4'], | |
| processed: [85, 92, 78, 95], | |
| uploaded: [95, 105, 88, 110] | |
| }, | |
| monthly: { | |
| labels: ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور'], | |
| processed: [340, 380, 290, 420, 380, 450], | |
| uploaded: [380, 420, 320, 460, 410, 490] | |
| } | |
| }; | |
| const selectedData = data[period] || data.weekly; | |
| documentsChart.data.labels = selectedData.labels; | |
| documentsChart.data.datasets[0].data = selectedData.processed; | |
| documentsChart.data.datasets[1].data = selectedData.uploaded; | |
| documentsChart.update('active'); | |
| showToast(`نمودار به حالت ${period === 'daily' ? 'روزانه' : period === 'weekly' ? 'هفتگی' : 'ماهانه'} تغییر کرد`, 'info', 'بروزرسانی'); | |
| } | |
| function updateStatusChart(type) { | |
| if (!chartJsLoaded || !statusChart) { | |
| showToast('نمودارها در دسترس نیستند', 'warning', 'هشدار'); | |
| return; | |
| } | |
| // Update active filter | |
| const chartCard = event.target.closest('.chart-card'); | |
| chartCard.querySelectorAll('.chart-filter').forEach(btn => { | |
| btn.classList.remove('active'); | |
| }); | |
| event.target.classList.add('active'); | |
| const data = { | |
| status: { | |
| labels: ['پردازش شده', 'در حال پردازش', 'خطا', 'آپلود شده'], | |
| data: [4, 1, 1, 0], | |
| colors: ['#10b981', '#f59e0b', '#ef4444', '#3b82f6'] | |
| }, | |
| category: { | |
| labels: ['قراردادها', 'دادخواستها', 'احکام قضایی', 'آرای دیوان', 'سایر'], | |
| data: [1, 1, 1, 1, 2], | |
| colors: ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6'] | |
| } | |
| }; | |
| const selectedData = data[type] || data.status; | |
| statusChart.data.labels = selectedData.labels; | |
| statusChart.data.datasets[0].data = selectedData.data; | |
| statusChart.data.datasets[0].backgroundColor = selectedData.colors; | |
| statusChart.update('active'); | |
| showToast(`نمودار به حالت ${type === 'status' ? 'وضعیت' : 'دستهبندی'} تغییر کرد`, 'info', 'بروزرسانی'); | |
| } | |
| // Utility functions | |
| function toggleSidebar() { | |
| const sidebar = document.getElementById('sidebar'); | |
| const mainContent = document.getElementById('mainContent'); | |
| if (sidebar && mainContent) { | |
| sidebar.classList.toggle('collapsed'); | |
| mainContent.classList.toggle('sidebar-collapsed'); | |
| } | |
| } | |
| function showToast(message, type = 'info', title = 'اعلان') { | |
| const toastContainer = document.getElementById('toastContainer'); | |
| if (!toastContainer) return; | |
| const toast = document.createElement('div'); | |
| toast.className = `toast ${type}`; | |
| const icons = { | |
| success: 'check-circle', | |
| error: 'exclamation-triangle', | |
| warning: 'exclamation-circle', | |
| info: 'info-circle' | |
| }; | |
| toast.innerHTML = ` | |
| <div class="toast-icon"> | |
| <i class="fas fa-${icons[type]}"></i> | |
| </div> | |
| <div class="toast-content"> | |
| <div class="toast-title">${title}</div> | |
| <div class="toast-message">${message}</div> | |
| </div> | |
| <button type="button" class="toast-close" onclick="this.parentElement.remove()"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| `; | |
| toastContainer.appendChild(toast); | |
| // Show toast | |
| setTimeout(() => toast.classList.add('show'), 100); | |
| // Auto remove after 5 seconds | |
| setTimeout(() => { | |
| if (toast.parentElement) { | |
| toast.classList.remove('show'); | |
| setTimeout(() => { | |
| if (toast.parentElement) { | |
| toast.remove(); | |
| } | |
| }, 300); | |
| } | |
| }, 5000); | |
| } | |
| // Connection status monitoring | |
| async function checkConnectionStatus() { | |
| try { | |
| const response = await fetch(API_ENDPOINTS.health); | |
| const status = await response.json(); | |
| const connectionStatus = document.getElementById('connectionStatus'); | |
| if (connectionStatus) { | |
| if (status.status === 'healthy') { | |
| connectionStatus.className = 'connection-status online'; | |
| connectionStatus.innerHTML = ` | |
| <div class="status-indicator"></div> | |
| <span>متصل به سرور</span> | |
| `; | |
| } else { | |
| connectionStatus.className = 'connection-status offline'; | |
| connectionStatus.innerHTML = ` | |
| <div class="status-indicator"></div> | |
| <span>خطا در اتصال</span> | |
| `; | |
| } | |
| } | |
| } catch (error) { | |
| const connectionStatus = document.getElementById('connectionStatus'); | |
| if (connectionStatus) { | |
| connectionStatus.className = 'connection-status offline'; | |
| connectionStatus.innerHTML = ` | |
| <div class="status-indicator"></div> | |
| <span>خطا در اتصال</span> | |
| `; | |
| } | |
| } | |
| } | |
| // Check connection status every 30 seconds | |
| setInterval(checkConnectionStatus, 30000); | |
| // Initial connection check | |
| checkConnectionStatus(); | |
| // Enhanced Analytics Functions | |
| let analyticsCharts = {}; | |
| // Analytics API endpoints | |
| const ANALYTICS_ENDPOINTS = { | |
| realtime: '/api/analytics/realtime', | |
| trends: '/api/analytics/trends', | |
| predictions: '/api/analytics/predictions', | |
| similarity: '/api/analytics/similarity', | |
| clustering: '/api/analytics/clustering', | |
| quality: '/api/analytics/quality', | |
| health: '/api/analytics/health', | |
| performance: '/api/analytics/performance' | |
| }; | |
| // Refresh Overview Analytics | |
| async function refreshOverview() { | |
| try { | |
| showToast('در حال بروزرسانی نمای کلی...', 'info'); | |
| const response = await fetch(ANALYTICS_ENDPOINTS.realtime); | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| document.getElementById('totalAnalytics').textContent = data.data.total_documents || 0; | |
| document.getElementById('activeAnalytics').textContent = data.data.active_users || 0; | |
| document.getElementById('accuracyRate').textContent = '85%'; | |
| // Update overview chart | |
| updateOverviewChart(data.data); | |
| showToast('نمای کلی بروزرسانی شد', 'success'); | |
| } | |
| } catch (error) { | |
| console.error('Error refreshing overview:', error); | |
| showToast('خطا در بروزرسانی نمای کلی', 'error'); | |
| } | |
| } | |
| // Refresh Trends Analytics | |
| async function refreshTrends() { | |
| try { | |
| showToast('در حال بروزرسانی روندها...', 'info'); | |
| const response = await fetch(ANALYTICS_ENDPOINTS.trends); | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| updateTrendsChart(data.data); | |
| updateTrendsInsights(data.data); | |
| showToast('روندها بروزرسانی شد', 'success'); | |
| } | |
| } catch (error) { | |
| console.error('Error refreshing trends:', error); | |
| showToast('خطا در بروزرسانی روندها', 'error'); | |
| } | |
| } | |
| // Refresh Predictions Analytics | |
| async function refreshPredictions() { | |
| try { | |
| showToast('در حال بروزرسانی پیشبینیها...', 'info'); | |
| const response = await fetch(ANALYTICS_ENDPOINTS.predictions); | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| document.getElementById('weeklyForecast').textContent = data.data.predicted_uploads || 0; | |
| document.getElementById('confidenceLevel').textContent = `${(data.data.confidence * 100).toFixed(0)}%`; | |
| updatePredictionsChart(data.data); | |
| showToast('پیشبینیها بروزرسانی شد', 'success'); | |
| } | |
| } catch (error) { | |
| console.error('Error refreshing predictions:', error); | |
| showToast('خطا در بروزرسانی پیشبینیها', 'error'); | |
| } | |
| } | |
| // Refresh Quality Analytics | |
| async function refreshQuality() { | |
| try { | |
| showToast('در حال بروزرسانی کیفیت...', 'info'); | |
| const response = await fetch(ANALYTICS_ENDPOINTS.quality); | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| document.getElementById('overallQuality').textContent = data.data.overall_score || 0; | |
| document.getElementById('readabilityScore').textContent = data.data.readability || 0; | |
| document.getElementById('accuracyScore').textContent = data.data.accuracy || 0; | |
| updateQualityChart(data.data); | |
| showToast('کیفیت بروزرسانی شد', 'success'); | |
| } | |
| } catch (error) { | |
| console.error('Error refreshing quality:', error); | |
| showToast('خطا در بروزرسانی کیفیت', 'error'); | |
| } | |
| } | |
| // Refresh Health Analytics | |
| async function refreshHealth() { | |
| try { | |
| showToast('در حال بروزرسانی سلامت سیستم...', 'info'); | |
| const response = await fetch(ANALYTICS_ENDPOINTS.health); | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| document.getElementById('cpuUsage').textContent = `${data.data.cpu_usage || 0}%`; | |
| document.getElementById('memoryUsage').textContent = `${data.data.memory_usage || 0}%`; | |
| document.getElementById('diskUsage').textContent = `${data.data.disk_usage || 0}%`; | |
| updateHealthStatus(data.data); | |
| updateHealthChart(data.data); | |
| showToast('سلامت سیستم بروزرسانی شد', 'success'); | |
| } | |
| } catch (error) { | |
| console.error('Error refreshing health:', error); | |
| showToast('خطا در بروزرسانی سلامت سیستم', 'error'); | |
| } | |
| } | |
| // Refresh Clustering Analytics | |
| async function refreshClustering() { | |
| try { | |
| showToast('در حال بروزرسانی خوشهبندی...', 'info'); | |
| const response = await fetch(ANALYTICS_ENDPOINTS.clustering); | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| document.getElementById('clusterCount').textContent = data.data.total_clusters || 0; | |
| document.getElementById('avgSimilarity').textContent = `${(data.data.avg_similarity * 100).toFixed(0)}%`; | |
| updateClusteringChart(data.data); | |
| updateClusteringList(data.data); | |
| showToast('خوشهبندی بروزرسانی شد', 'success'); | |
| } | |
| } catch (error) { | |
| console.error('Error refreshing clustering:', error); | |
| showToast('خطا در بروزرسانی خوشهبندی', 'error'); | |
| } | |
| } | |
| // Refresh Similarity Analytics | |
| async function refreshSimilarity() { | |
| try { | |
| showToast('در حال بروزرسانی شباهت...', 'info'); | |
| const response = await fetch(ANALYTICS_ENDPOINTS.similarity); | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| updateSimilarityResults(data.data); | |
| updateSimilarityChart(data.data); | |
| showToast('شباهت بروزرسانی شد', 'success'); | |
| } | |
| } catch (error) { | |
| console.error('Error refreshing similarity:', error); | |
| showToast('خطا در بروزرسانی شباهت', 'error'); | |
| } | |
| } | |
| // Chart update functions | |
| function updateOverviewChart(data) { | |
| const ctx = document.getElementById('overviewChartCanvas'); | |
| if (!ctx) return; | |
| if (analyticsCharts.overview) { | |
| analyticsCharts.overview.destroy(); | |
| } | |
| analyticsCharts.overview = new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: ['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'], | |
| datasets: [{ | |
| label: 'اسناد پردازش شده', | |
| data: [12, 19, 8, 15, 22, 18, 14], | |
| borderColor: '#3b82f6', | |
| backgroundColor: 'rgba(59, 130, 246, 0.1)', | |
| tension: 0.4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| function updateTrendsChart(data) { | |
| const ctx = document.getElementById('trendsChartCanvas'); | |
| if (!ctx) return; | |
| if (analyticsCharts.trends) { | |
| analyticsCharts.trends.destroy(); | |
| } | |
| analyticsCharts.trends = new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: data.daily_uploads ? data.daily_uploads.map((_, i) => `روز ${i + 1}`) : [], | |
| datasets: [{ | |
| label: 'آپلود روزانه', | |
| data: data.daily_uploads || [12, 15, 8, 20, 18, 22, 16], | |
| backgroundColor: '#10b981' | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false | |
| } | |
| }); | |
| } | |
| function updatePredictionsChart(data) { | |
| const ctx = document.getElementById('predictionsChartCanvas'); | |
| if (!ctx) return; | |
| if (analyticsCharts.predictions) { | |
| analyticsCharts.predictions.destroy(); | |
| } | |
| analyticsCharts.predictions = new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: ['هفته 1', 'هفته 2', 'هفته 3', 'هفته 4'], | |
| datasets: [{ | |
| label: 'پیشبینی', | |
| data: data.next_week_forecast || [15, 18, 20, 17], | |
| borderColor: '#f59e0b', | |
| backgroundColor: 'rgba(245, 158, 11, 0.1)', | |
| tension: 0.4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false | |
| } | |
| }); | |
| } | |
| function updateQualityChart(data) { | |
| const ctx = document.getElementById('qualityChartCanvas'); | |
| if (!ctx) return; | |
| if (analyticsCharts.quality) { | |
| analyticsCharts.quality.destroy(); | |
| } | |
| analyticsCharts.quality = new Chart(ctx, { | |
| type: 'radar', | |
| data: { | |
| labels: ['خوانایی', 'دقت', 'کامل بودن', 'سازگاری'], | |
| datasets: [{ | |
| label: 'امتیاز کیفیت', | |
| data: [data.readability || 7.8, data.accuracy || 8.7, data.completeness || 9.2, 8.5], | |
| backgroundColor: 'rgba(59, 130, 246, 0.2)', | |
| borderColor: '#3b82f6', | |
| pointBackgroundColor: '#3b82f6' | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| scales: { | |
| r: { | |
| beginAtZero: true, | |
| max: 10 | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| function updateHealthChart(data) { | |
| const ctx = document.getElementById('healthChartCanvas'); | |
| if (!ctx) return; | |
| if (analyticsCharts.health) { | |
| analyticsCharts.health.destroy(); | |
| } | |
| analyticsCharts.health = new Chart(ctx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['CPU', 'RAM', 'دیسک'], | |
| datasets: [{ | |
| data: [data.cpu_usage || 23, data.memory_usage || 45, data.disk_usage || 67], | |
| backgroundColor: ['#3b82f6', '#10b981', '#f59e0b'] | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false | |
| } | |
| }); | |
| } | |
| function updateClusteringChart(data) { | |
| const ctx = document.getElementById('clusteringChartCanvas'); | |
| if (!ctx) return; | |
| if (analyticsCharts.clustering) { | |
| analyticsCharts.clustering.destroy(); | |
| } | |
| analyticsCharts.clustering = new Chart(ctx, { | |
| type: 'pie', | |
| data: { | |
| labels: data.clusters ? data.clusters.map(c => c.name) : ['خوشه 1', 'خوشه 2', 'خوشه 3'], | |
| datasets: [{ | |
| data: data.clusters ? data.clusters.map(c => c.size) : [25, 18, 12], | |
| backgroundColor: ['#3b82f6', '#10b981', '#f59e0b', '#ef4444'] | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false | |
| } | |
| }); | |
| } | |
| // Update functions for insights and data | |
| function updateTrendsInsights(data) { | |
| const insightsContainer = document.getElementById('trendsInsights'); | |
| if (!insightsContainer) return; | |
| // Update insights based on data | |
| const insights = [ | |
| { icon: 'arrow-up', text: 'افزایش 15% در پردازش اسناد', class: 'text-success' }, | |
| { icon: 'clock', text: 'کاهش زمان پردازش به 2.3 ثانیه', class: 'text-warning' } | |
| ]; | |
| insightsContainer.innerHTML = insights.map(insight => ` | |
| <div class="insight-item"> | |
| <i class="fas fa-${insight.icon} ${insight.class}"></i> | |
| <span>${insight.text}</span> | |
| </div> | |
| `).join(''); | |
| } | |
| function updateHealthStatus(data) { | |
| const statusElement = document.getElementById('systemStatus'); | |
| if (!statusElement) return; | |
| const isHealthy = data.system_status === 'healthy'; | |
| statusElement.innerHTML = ` | |
| <i class="fas fa-circle" style="color: ${isHealthy ? '#10b981' : '#ef4444'}"></i> | |
| <span>${isHealthy ? 'سیستم سالم' : 'مشکل در سیستم'}</span> | |
| `; | |
| } | |
| function updateClusteringList(data) { | |
| const listContainer = document.getElementById('clusteringList'); | |
| if (!listContainer || !data.clusters) return; | |
| listContainer.innerHTML = data.clusters.map(cluster => ` | |
| <div class="cluster-item"> | |
| <div class="cluster-name">${cluster.name}</div> | |
| <div class="cluster-size">${cluster.size} سند</div> | |
| <div class="cluster-similarity">${(cluster.avg_similarity * 100).toFixed(0)}% شباهت</div> | |
| </div> | |
| `).join(''); | |
| } | |
| function updateSimilarityResults(data) { | |
| const resultsContainer = document.getElementById('similarityResults'); | |
| if (!resultsContainer || !data.similar_documents) return; | |
| resultsContainer.innerHTML = data.similar_documents.map(doc => ` | |
| <div class="similarity-item"> | |
| <div class="doc-title">${doc.title}</div> | |
| <div class="doc-similarity">${(doc.similarity * 100).toFixed(0)}% شباهت</div> | |
| </div> | |
| `).join(''); | |
| } | |
| function updateSimilarityChart(data) { | |
| const ctx = document.getElementById('similarityChartCanvas'); | |
| if (!ctx) return; | |
| if (analyticsCharts.similarity) { | |
| analyticsCharts.similarity.destroy(); | |
| } | |
| analyticsCharts.similarity = new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: data.similar_documents ? data.similar_documents.map(d => d.title) : [], | |
| datasets: [{ | |
| label: 'درصد شباهت', | |
| data: data.similar_documents ? data.similar_documents.map(d => d.similarity * 100) : [], | |
| backgroundColor: '#8b5cf6' | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false | |
| } | |
| }); | |
| } | |
| // Initialize analytics on page load | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Load initial analytics data | |
| refreshOverview(); | |
| refreshTrends(); | |
| refreshPredictions(); | |
| refreshQuality(); | |
| refreshHealth(); | |
| refreshClustering(); | |
| refreshSimilarity(); | |
| // Set up refresh button | |
| const refreshBtn = document.getElementById('refreshAnalyticsBtn'); | |
| if (refreshBtn) { | |
| refreshBtn.addEventListener('click', function() { | |
| refreshOverview(); | |
| refreshTrends(); | |
| refreshPredictions(); | |
| refreshQuality(); | |
| refreshHealth(); | |
| refreshClustering(); | |
| refreshSimilarity(); | |
| }); | |
| } | |
| }); | |
| console.log('🚀 Legal Dashboard Main Page Ready!'); | |
| </script> | |
| </body> | |
| </html> |