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> | |
| <!-- Load API Client and Core System --> | |
| <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> | |
| <script src="js/notifications.js"></script> | |
| <style> | |
| :root { | |
| /* رنگبندی مدرن و هارمونیک */ | |
| --text-primary: #0f172a; | |
| --text-secondary: #475569; | |
| --text-muted: #64748b; | |
| --text-light: #ffffff; | |
| /* پسزمینههای بهبود یافته */ | |
| --body-bg: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 50%, #cbd5e1 100%); | |
| --card-bg: rgba(255, 255, 255, 0.95); | |
| --glass-bg: rgba(255, 255, 255, 0.9); | |
| --glass-border: rgba(148, 163, 184, 0.2); | |
| /* گرادیانهای مدرن */ | |
| --primary-gradient: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); | |
| --secondary-gradient: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%); | |
| --success-gradient: linear-gradient(135deg, #10b981 0%, #047857 100%); | |
| --warning-gradient: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); | |
| --danger-gradient: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); | |
| /* سایههای ملایم */ | |
| --shadow-xs: 0 1px 3px rgba(0, 0, 0, 0.05); | |
| --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08); | |
| --shadow-md: 0 4px 15px rgba(0, 0, 0, 0.1); | |
| --shadow-lg: 0 8px 25px rgba(0, 0, 0, 0.12); | |
| --shadow-glow-primary: 0 0 20px rgba(59, 130, 246, 0.15); | |
| /* متغیرهای کامپکت */ | |
| --sidebar-width: 260px; | |
| --border-radius: 12px; | |
| --border-radius-sm: 8px; | |
| --transition-smooth: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); | |
| --transition-fast: all 0.15s ease-in-out; | |
| /* فونتهای کامپکت */ | |
| --font-size-xs: 0.7rem; | |
| --font-size-sm: 0.8rem; | |
| --font-size-base: 0.9rem; | |
| --font-size-lg: 1.1rem; | |
| --font-size-xl: 1.25rem; | |
| --font-size-2xl: 1.5rem; | |
| } | |
| /* ریست و تنظیمات پایه */ | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Vazirmatn', -apple-system, BlinkMacSystemFont, sans-serif; | |
| background: var(--body-bg); | |
| color: var(--text-primary); | |
| line-height: 1.6; | |
| overflow-x: hidden; | |
| font-size: var(--font-size-base); | |
| } | |
| /* اسکرولبار مدرن */ | |
| ::-webkit-scrollbar { | |
| 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; | |
| } | |
| /* کانتینر اصلی */ | |
| .dashboard-container { | |
| display: flex; | |
| min-block-size: 100vh; | |
| inline-size: 100%; | |
| } | |
| /* سایدبار کامپکت */ | |
| .sidebar { | |
| inline-size: var(--sidebar-width); | |
| background: linear-gradient(135deg, | |
| rgba(248, 250, 252, 0.98) 0%, | |
| rgba(241, 245, 249, 0.95) 25%, | |
| rgba(226, 232, 240, 0.98) 50%, | |
| rgba(203, 213, 225, 0.95) 75%, | |
| rgba(148, 163, 184, 0.1) 100%); | |
| backdrop-filter: blur(25px); | |
| padding: 1rem 0; | |
| position: fixed; | |
| block-size: 100vh; | |
| inset-inline-end: 0; | |
| inset-block-start: 0; | |
| z-index: 1000; | |
| overflow-y: auto; | |
| box-shadow: -8px 0 32px rgba(59, 130, 246, 0.12); | |
| border-inline-start: 1px solid rgba(59, 130, 246, 0.15); | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .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); | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.6rem; | |
| color: var(--text-primary); | |
| text-decoration: none; | |
| } | |
| .logo-icon { | |
| inline-size: 2rem; | |
| block-size: 2rem; | |
| background: var(--primary-gradient); | |
| border-radius: var(--border-radius-sm); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1rem; | |
| color: white; | |
| box-shadow: var(--shadow-glow-primary); | |
| } | |
| .logo-text { | |
| font-size: var(--font-size-lg); | |
| font-weight: 700; | |
| background: var(--primary-gradient); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .nav-section { | |
| margin-block-end: 1rem; | |
| } | |
| .nav-title { | |
| padding: 0 1rem 0.4rem; | |
| font-size: var(--font-size-xs); | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| color: var(--text-secondary); | |
| } | |
| .nav-menu { | |
| list-style: none; | |
| } | |
| .nav-item { | |
| margin: 0.15rem 0.5rem; | |
| } | |
| .nav-link { | |
| display: flex; | |
| align-items: center; | |
| padding: 0.6rem 0.8rem; | |
| color: var(--text-primary); | |
| text-decoration: none; | |
| border-radius: var(--border-radius-sm); | |
| transition: var(--transition-smooth); | |
| font-weight: 500; | |
| font-size: var(--font-size-sm); | |
| cursor: pointer; | |
| border: 1px solid transparent; | |
| } | |
| .nav-link:hover { | |
| color: var(--text-primary); | |
| transform: translateX(-2px); | |
| border-color: rgba(59, 130, 246, 0.15); | |
| background: rgba(59, 130, 246, 0.05); | |
| } | |
| .nav-link.active { | |
| background: var(--primary-gradient); | |
| color: var(--text-light); | |
| box-shadow: var(--shadow-md); | |
| } | |
| .nav-icon { | |
| margin-inline-start: 0.6rem; | |
| inline-size: 1rem; | |
| text-align: center; | |
| font-size: 0.9rem; | |
| } | |
| .nav-badge { | |
| background: var(--danger-gradient); | |
| color: white; | |
| padding: 0.15rem 0.4rem; | |
| border-radius: 10px; | |
| font-size: var(--font-size-xs); | |
| font-weight: 600; | |
| margin-inline-end: auto; | |
| min-inline-size: 1.2rem; | |
| text-align: center; | |
| } | |
| /* محتوای اصلی */ | |
| .main-content { | |
| flex: 1; | |
| margin-inline-end: var(--sidebar-width); | |
| padding: 1rem; | |
| min-block-size: 100vh; | |
| inline-size: calc(100% - var(--sidebar-width)); | |
| } | |
| /* هدر کامپکت */ | |
| .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; | |
| background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.6rem; | |
| } | |
| .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); | |
| 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: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); | |
| 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); | |
| } | |
| .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); | |
| border-radius: var(--border-radius); | |
| padding: 1.2rem; | |
| box-shadow: var(--shadow-md); | |
| 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.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: var(--shadow-lg); | |
| } | |
| .stat-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: flex-start; | |
| margin-block-end: 0.8rem; | |
| } | |
| .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: var(--shadow-sm); | |
| transition: var(--transition-smooth); | |
| } | |
| .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; | |
| } | |
| .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); | |
| border-radius: var(--border-radius); | |
| padding: 1.5rem; | |
| box-shadow: var(--shadow-md); | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| transition: var(--transition-smooth); | |
| } | |
| .chart-card:hover { | |
| transform: translateY(-4px); | |
| box-shadow: var(--shadow-lg); | |
| } | |
| .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; | |
| } | |
| /* دسترسی سریع */ | |
| .quick-access-section { | |
| margin-block-end: 1.5rem; | |
| } | |
| .quick-access-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); | |
| gap: 1rem; | |
| padding: 1rem 0; | |
| } | |
| .quick-access-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 1rem; | |
| padding: 1rem; | |
| background: rgba(255, 255, 255, 0.7); | |
| border-radius: var(--border-radius-sm); | |
| text-decoration: none; | |
| color: var(--text-primary); | |
| transition: var(--transition-smooth); | |
| border: 1px solid rgba(59, 130, 246, 0.1); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .quick-access-item::before { | |
| content: ''; | |
| position: absolute; | |
| inset-block-start: 0; | |
| inset-inline-start: 0; | |
| inset-block-end: 0; | |
| inline-size: 4px; | |
| background: var(--primary-gradient); | |
| opacity: 0; | |
| transition: var(--transition-smooth); | |
| } | |
| .quick-access-item:hover { | |
| background: rgba(255, 255, 255, 0.9); | |
| transform: translateY(-2px); | |
| box-shadow: var(--shadow-md); | |
| border-color: rgba(59, 130, 246, 0.3); | |
| } | |
| .quick-access-item:hover::before { | |
| opacity: 1; | |
| } | |
| .quick-access-icon { | |
| inline-size: 3rem; | |
| block-size: 3rem; | |
| background: var(--primary-gradient); | |
| border-radius: var(--border-radius-sm); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: white; | |
| font-size: 1.2rem; | |
| flex-shrink: 0; | |
| box-shadow: var(--shadow-sm); | |
| transition: var(--transition-smooth); | |
| } | |
| .quick-access-item:hover .quick-access-icon { | |
| transform: scale(1.1); | |
| box-shadow: var(--shadow-md); | |
| } | |
| .quick-access-content h3 { | |
| font-size: var(--font-size-base); | |
| font-weight: 600; | |
| color: var(--text-primary); | |
| margin-block-end: 0.3rem; | |
| } | |
| .quick-access-content p { | |
| font-size: var(--font-size-sm); | |
| color: var(--text-secondary); | |
| margin: 0; | |
| } | |
| /* 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; | |
| } | |
| /* واکنشگرایی */ | |
| @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%; | |
| padding: 1rem; | |
| } | |
| .dashboard-header { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: 0.8rem; | |
| } | |
| .header-actions { | |
| width: 100%; | |
| justify-content: space-between; | |
| flex-direction: column; | |
| gap: 0.8rem; | |
| } | |
| .search-container { | |
| inline-size: 100%; | |
| } | |
| .search-input { | |
| inline-size: 100%; | |
| } | |
| .stats-grid { | |
| grid-template-columns: repeat(2, 1fr); | |
| } | |
| .charts-section { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| @media (max-inline-size: 768px) { | |
| .main-content { | |
| padding: 0.8rem; | |
| } | |
| .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; | |
| } | |
| } | |
| </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> | |
| </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"> | |
| <i class="fas fa-chart-pie nav-icon"></i> | |
| <span>نمای کلی</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="enhanced_analytics_dashboard.html" class="nav-link"> | |
| <i class="fas fa-chart-area 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"> | |
| <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"> | |
| <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"> | |
| <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"> | |
| <i class="fas fa-globe nav-icon"></i> | |
| <span>استخراج محتوا</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="scraping_dashboard.html" class="nav-link"> | |
| <i class="fas fa-spider nav-icon"></i> | |
| <span>داشبورد اسکرپینگ</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="analytics.html" class="nav-link"> | |
| <i class="fas fa-chart-line nav-icon"></i> | |
| <span>آمار و تحلیل</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="reports.html" class="nav-link"> | |
| <i class="fas fa-file-export nav-icon"></i> | |
| <span>گزارشها</span> | |
| </a> | |
| </li> | |
| </ul> | |
| </div> | |
| <div class="nav-section"> | |
| <h6 class="nav-title">تنظیمات و ابزارهای توسعه</h6> | |
| <ul class="nav-menu"> | |
| <li class="nav-item"> | |
| <a href="settings.html" class="nav-link"> | |
| <i class="fas fa-cog nav-icon"></i> | |
| <span>تنظیمات</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="dev/api-test.html" class="nav-link"> | |
| <i class="fas fa-code nav-icon"></i> | |
| <span>تست API</span> | |
| </a> | |
| </li> | |
| <li class="nav-item"> | |
| <a href="#" class="nav-link"> | |
| <i class="fas fa-sign-out-alt nav-icon"></i> | |
| <span>خروج</span> | |
| </a> | |
| </li> | |
| </ul> | |
| </div> | |
| </nav> | |
| </aside> | |
| <!-- محتوای اصلی --> | |
| <main class="main-content" id="mainContent"> | |
| <!-- هدر --> | |
| <header class="dashboard-header"> | |
| <div> | |
| <h1 class="dashboard-title"> | |
| <i class="fas fa-chart-pie"></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"> | |
| <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"> | |
| <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"> | |
| <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"> | |
| <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"> | |
| <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"> | |
| <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> | |
| <!-- دسترسی سریع --> | |
| <section class="quick-access-section"> | |
| <div class="chart-card"> | |
| <div class="chart-header"> | |
| <h2 class="chart-title"> | |
| <i class="fas fa-bolt"></i> | |
| دسترسی سریع | |
| </h2> | |
| </div> | |
| <div class="quick-access-grid"> | |
| <a href="upload.html" class="quick-access-item"> | |
| <div class="quick-access-icon"> | |
| <i class="fas fa-cloud-upload-alt"></i> | |
| </div> | |
| <div class="quick-access-content"> | |
| <h3>آپلود سند جدید</h3> | |
| <p>آپلود و پردازش اسناد PDF</p> | |
| </div> | |
| </a> | |
| <a href="documents.html" class="quick-access-item"> | |
| <div class="quick-access-icon"> | |
| <i class="fas fa-folder-open"></i> | |
| </div> | |
| <div class="quick-access-content"> | |
| <h3>مدیریت اسناد</h3> | |
| <p>مشاهده و ویرایش اسناد</p> | |
| </div> | |
| </a> | |
| <a href="search.html" class="quick-access-item"> | |
| <div class="quick-access-icon"> | |
| <i class="fas fa-search"></i> | |
| </div> | |
| <div class="quick-access-content"> | |
| <h3>جستجو در اسناد</h3> | |
| <p>جستجوی هوشمند در محتوا</p> | |
| </div> | |
| </a> | |
| <a href="scraping.html" class="quick-access-item"> | |
| <div class="quick-access-icon"> | |
| <i class="fas fa-globe"></i> | |
| </div> | |
| <div class="quick-access-content"> | |
| <h3>استخراج از وب</h3> | |
| <p>دریافت محتوا از وبسایتها</p> | |
| </div> | |
| </a> | |
| <a href="analytics.html" class="quick-access-item"> | |
| <div class="quick-access-icon"> | |
| <i class="fas fa-chart-line"></i> | |
| </div> | |
| <div class="quick-access-content"> | |
| <h3>آمار و تحلیل</h3> | |
| <p>تحلیل عملکرد و آمار</p> | |
| </div> | |
| </a> | |
| <a href="reports.html" class="quick-access-item"> | |
| <div class="quick-access-icon"> | |
| <i class="fas fa-file-export"></i> | |
| </div> | |
| <div class="quick-access-content"> | |
| <h3>گزارشگیری</h3> | |
| <p>تولید گزارشهای تفصیلی</p> | |
| </div> | |
| </a> | |
| </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> | |
| // Global variables | |
| let documentsChart = null; | |
| let statusChart = null; | |
| let chartJsLoaded = false; | |
| let isOnline = false; | |
| // 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' | |
| }; | |
| // Initialize when page loads | |
| document.addEventListener('DOMContentLoaded', function() { | |
| console.log('🏠 Dashboard loading...'); | |
| initializeDashboard(); | |
| }); | |
| async function initializeDashboard() { | |
| try { | |
| // Test connection first | |
| isOnline = await testConnection(); | |
| // Setup Chart.js loading check | |
| 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(); | |
| await loadInitialData(); | |
| showToast('داشبورد با موفقیت بارگذاری شد', 'success', 'خوش آمدید'); | |
| } catch (error) { | |
| console.error('Failed to initialize dashboard:', error); | |
| isOnline = false; | |
| setupEventListeners(); | |
| showToast('حالت آفلاین فعال است', 'warning', 'اتصال ناموفق'); | |
| } | |
| } | |
| async function testConnection() { | |
| try { | |
| // Try to connect to API if available | |
| if (window.legalAPI && window.legalAPI.healthCheck) { | |
| await window.legalAPI.healthCheck(); | |
| return true; | |
| } else { | |
| // Fallback to simple fetch | |
| const response = await fetch(API_ENDPOINTS.health); | |
| return response.ok; | |
| } | |
| } catch (error) { | |
| console.log('API connection failed, using offline mode'); | |
| return false; | |
| } | |
| } | |
| // 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 { | |
| showToast('در حال بارگذاری دادهها...', 'info'); | |
| if (isOnline) { | |
| // Try to load real data from API | |
| await Promise.all([ | |
| loadDashboardStats(), | |
| loadChartsData(), | |
| updateDocumentsBadge() | |
| ]); | |
| showToast('دادهها از سرور بارگذاری شدند', 'success'); | |
| } else { | |
| // Use mock data in offline mode | |
| loadMockData(); | |
| showToast('دادههای آزمایشی بارگذاری شدند', 'info'); | |
| } | |
| } catch (error) { | |
| console.error('Error loading initial data:', error); | |
| // Fallback to mock data | |
| loadMockData(); | |
| showToast('خطا در بارگذاری - از دادههای آزمایشی استفاده شد', 'warning'); | |
| } | |
| } | |
| // Load dashboard statistics from API | |
| async function loadDashboardStats() { | |
| try { | |
| const response = await fetch(API_ENDPOINTS.dashboardSummary); | |
| if (!response.ok) throw new Error('Failed to load dashboard stats'); | |
| const stats = await response.json(); | |
| // 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); | |
| console.log('Dashboard stats loaded from API'); | |
| } catch (error) { | |
| console.error('Failed to load dashboard stats:', error); | |
| throw error; | |
| } | |
| } | |
| // Load charts data from API | |
| async function loadChartsData() { | |
| try { | |
| const response = await fetch(API_ENDPOINTS.chartsData); | |
| if (!response.ok) throw new Error('Failed to load charts data'); | |
| const chartsData = await response.json(); | |
| console.log('Charts data loaded from API'); | |
| return chartsData; | |
| } catch (error) { | |
| console.error('Failed to load charts data:', error); | |
| throw error; | |
| } | |
| } | |
| // Update documents badge | |
| async function updateDocumentsBadge() { | |
| try { | |
| const response = await fetch(API_ENDPOINTS.documents); | |
| if (!response.ok) throw new Error('Failed to load documents count'); | |
| const data = await response.json(); | |
| const badge = document.getElementById('totalDocumentsBadge'); | |
| if (badge && data.total_count !== undefined) { | |
| badge.textContent = data.total_count; | |
| } | |
| } catch (error) { | |
| console.error('Failed to update documents badge:', error); | |
| throw error; | |
| } | |
| } | |
| // Load mock data for offline mode | |
| function loadMockData() { | |
| // Update stats with mock data | |
| document.getElementById('totalDocuments').textContent = '6'; | |
| document.getElementById('processedDocuments').textContent = '4'; | |
| document.getElementById('errorDocuments').textContent = '1'; | |
| document.getElementById('averageQuality').textContent = '8.1'; | |
| // Update badge | |
| const badge = document.getElementById('totalDocumentsBadge'); | |
| if (badge) { | |
| badge.textContent = '6'; | |
| } | |
| } | |
| // 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', 'بروزرسانی'); | |
| } | |
| 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 = response.ok; | |
| const connectionStatus = document.getElementById('connectionStatus'); | |
| if (connectionStatus) { | |
| if (status) { | |
| connectionStatus.className = 'connection-status online'; | |
| connectionStatus.innerHTML = ` | |
| <div class="status-indicator"></div> | |
| <span>متصل به سرور</span> | |
| `; | |
| // Update online status and refresh data if needed | |
| if (!isOnline) { | |
| isOnline = true; | |
| loadInitialData(); // Refresh with real data | |
| } | |
| } else { | |
| throw new Error('Server not responding'); | |
| } | |
| } | |
| } catch (error) { | |
| const connectionStatus = document.getElementById('connectionStatus'); | |
| if (connectionStatus) { | |
| connectionStatus.className = 'connection-status offline'; | |
| connectionStatus.innerHTML = ` | |
| <div class="status-indicator"></div> | |
| <span>خطا در اتصال</span> | |
| `; | |
| // Update offline status | |
| if (isOnline) { | |
| isOnline = false; | |
| showToast('اتصال قطع شد - حالت آفلاین فعال', 'warning', 'اتصال'); | |
| } | |
| } | |
| } | |
| } | |
| // Check connection status every 30 seconds | |
| setInterval(checkConnectionStatus, 30000); | |
| // Initial connection check after 2 seconds | |
| setTimeout(checkConnectionStatus, 2000); | |
| console.log('🏠 Legal Dashboard Index Page Ready!'); | |
| </script> | |
| </body> | |
| </html> |