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> | |
<!-- Fonts --> | |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap&subset=latin" rel="stylesheet"> | |
<link href="https://cdn.jsdelivr.net/gh/rastikerdar/[email protected]/Vazirmatn-font-face.css" rel="stylesheet"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
:root { | |
/* Professional Color Palette */ | |
--bg-primary: #0a0a0a; | |
--bg-secondary: #1a1a1a; | |
--bg-tertiary: #2a2a2a; | |
--surface: #ffffff; | |
--surface-variant: #f8f9fa; | |
/* Text Colors */ | |
--text-primary: #000000; | |
--text-secondary: #4a5568; | |
--text-muted: #a0aec0; | |
--text-inverse: #ffffff; | |
/* Metallic Gradients */ | |
--gold-gradient: linear-gradient(135deg, #ffd700 0%, #ffed4e 50%, #ffd700 100%); | |
--silver-gradient: linear-gradient(135deg, #c0c0c0 0%, #e8e8e8 50%, #c0c0c0 100%); | |
--platinum-gradient: linear-gradient(135deg, #e5e4e2 0%, #f7f7f7 50%, #e5e4e2 100%); | |
--bronze-gradient: linear-gradient(135deg, #cd7f32 0%, #daa520 50%, #cd7f32 100%); | |
/* Accent Colors */ | |
--accent-primary: #3b82f6; | |
--accent-secondary: #10b981; | |
--accent-tertiary: #f59e0b; | |
--accent-error: #ef4444; | |
/* Status Colors */ | |
--success: #10b981; | |
--warning: #f59e0b; | |
--error: #ef4444; | |
--info: #3b82f6; | |
/* Shadows */ | |
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1); | |
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); | |
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); | |
--shadow-xl: 0 25px 50px rgba(0, 0, 0, 0.15); | |
--shadow-layered: 0 5px 15px rgba(0,0,0,0.08); | |
/* Border Radius */ | |
--radius-sm: 6px; | |
--radius-md: 8px; | |
--radius-lg: 12px; | |
--radius-xl: 16px; | |
/* Transitions */ | |
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
--transition-smooth: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); | |
--transition-elegant: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1); | |
/* Layout */ | |
--sidebar-width: 300px; | |
--sidebar-collapsed: 80px; | |
} | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: 'Vazirmatn', 'Inter', sans-serif; | |
background: linear-gradient(135deg, var(--bg-primary) 0%, #111 100%); | |
color: var(--text-inverse); | |
line-height: 1.6; | |
font-size: 15px; | |
font-weight: 400; | |
overflow-x: hidden; | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
} | |
/* Loading Screen */ | |
.loading-screen { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: var(--bg-primary); | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
z-index: 9999; | |
transition: opacity 0.3s ease; | |
} | |
.loading-screen.hidden { | |
opacity: 0; | |
pointer-events: none; | |
} | |
.spinner { | |
width: 40px; | |
height: 40px; | |
border: 3px solid transparent; | |
border-top: 3px solid var(--surface); | |
border-radius: 50%; | |
animation: spin 1s linear infinite; | |
margin-bottom: 1rem; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.loading-text { | |
color: var(--text-inverse); | |
font-size: 16px; | |
font-weight: 500; | |
} | |
/* Main Layout */ | |
.dashboard { | |
display: flex; | |
min-height: 100vh; | |
opacity: 0; | |
transition: opacity 0.3s ease; | |
} | |
.dashboard.loaded { | |
opacity: 1; | |
} | |
/* Mobile Menu Button */ | |
.mobile-menu-btn { | |
display: none; | |
position: fixed; | |
top: 15px; | |
left: 15px; | |
z-index: 1100; | |
width: 44px; | |
height: 44px; | |
background: var(--gold-gradient); | |
border: none; | |
border-radius: var(--radius-md); | |
cursor: pointer; | |
transition: var(--transition-smooth); | |
color: #000; | |
font-size: 18px; | |
} | |
.mobile-menu-btn:hover { | |
transform: scale(1.05); | |
box-shadow: var(--shadow-md); | |
} | |
/* Sidebar Overlay for Mobile */ | |
.sidebar-overlay { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: rgba(0, 0, 0, 0.5); | |
z-index: 999; | |
opacity: 0; | |
visibility: hidden; | |
transition: all 0.3s ease; | |
} | |
.sidebar-overlay.active { | |
opacity: 1; | |
visibility: visible; | |
} | |
/* Enhanced Sidebar */ | |
.sidebar { | |
width: var(--sidebar-width); | |
background: var(--bg-secondary); | |
border-left: 1px solid rgba(255,255,255,0.05); | |
position: fixed; | |
height: 100vh; | |
right: 0; | |
top: 0; | |
overflow-y: auto; | |
transition: var(--transition); | |
z-index: 1000; | |
box-shadow: -5px 0 15px rgba(0,0,0,0.2); | |
display: flex; | |
flex-direction: column; | |
} | |
.sidebar.collapsed { | |
width: var(--sidebar-collapsed); | |
} | |
.sidebar-header { | |
padding: 1.5rem; | |
border-bottom: 1px solid rgba(255,255,255,0.1); | |
position: relative; | |
text-align: center; | |
} | |
.sidebar.collapsed .sidebar-header { | |
padding: 1.5rem 0.5rem; | |
} | |
.logo { | |
font-size: 22px; | |
font-weight: 700; | |
color: var(--text-inverse); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
gap: 10px; | |
} | |
.logo-icon { | |
background: var(--gold-gradient); | |
width: 36px; | |
height: 36px; | |
border-radius: 50%; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: #000; | |
font-size: 18px; | |
} | |
.logo-text { | |
transition: var(--transition); | |
} | |
.sidebar.collapsed .logo-text { | |
display: none; | |
} | |
.subtitle { | |
font-size: 13px; | |
color: #aaa; | |
margin-top: 0.5rem; | |
transition: var(--transition); | |
} | |
.sidebar.collapsed .subtitle { | |
display: none; | |
} | |
.toggle-btn { | |
position: absolute; | |
left: -12px; | |
top: 50%; | |
transform: translateY(-50%); | |
width: 28px; | |
height: 28px; | |
background: var(--bg-secondary); | |
border: 1px solid rgba(255,255,255,0.1); | |
border-radius: 50%; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
cursor: pointer; | |
color: var(--text-inverse); | |
transition: var(--transition); | |
box-shadow: 0 2px 5px rgba(0,0,0,0.2); | |
} | |
.toggle-btn:hover { | |
background: var(--bg-tertiary); | |
transform: translateY(-50%) scale(1.1); | |
border-color: rgba(255,215,0,0.3); | |
} | |
/* Navigation */ | |
.nav { | |
padding: 1.5rem 0; | |
flex-grow: 1; | |
} | |
.nav-group { | |
margin-bottom: 1.5rem; | |
} | |
.nav-group-title { | |
padding: 0.5rem 1.5rem; | |
font-size: 12px; | |
color: #777; | |
text-transform: uppercase; | |
letter-spacing: 1px; | |
margin-bottom: 0.5rem; | |
} | |
.sidebar.collapsed .nav-group-title { | |
display: none; | |
} | |
.nav-item { | |
position: relative; | |
margin-bottom: 0.25rem; | |
} | |
.nav-link { | |
display: flex; | |
align-items: center; | |
padding: 1rem 1.5rem; | |
color: #ccc; | |
text-decoration: none; | |
transition: var(--transition); | |
cursor: pointer; | |
font-weight: 500; | |
font-size: 15px; | |
position: relative; | |
overflow: hidden; | |
border-radius: var(--radius-sm); | |
margin: 0 0.5rem; | |
} | |
.nav-link:hover { | |
background: rgba(255,255,255,0.05); | |
color: #fff; | |
} | |
.nav-link.active { | |
background: linear-gradient(90deg, rgba(59, 130, 246, 0.2), transparent); | |
color: var(--accent-primary); | |
font-weight: 600; | |
} | |
.nav-link.active::after { | |
content: ''; | |
position: absolute; | |
right: 0; | |
top: 0; | |
bottom: 0; | |
width: 3px; | |
background: var(--accent-primary); | |
} | |
.nav-icon { | |
width: 24px; | |
height: 24px; | |
margin-left: 1rem; | |
flex-shrink: 0; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
transition: var(--transition); | |
font-size: 18px; | |
color: #aaa; | |
} | |
.nav-link.active .nav-icon, | |
.nav-link:hover .nav-icon { | |
color: var(--accent-primary); | |
} | |
.nav-text { | |
transition: var(--transition); | |
font-weight: 500; | |
} | |
.sidebar.collapsed .nav-text { | |
display: none; | |
} | |
.sidebar.collapsed .nav-link { | |
justify-content: center; | |
padding: 1.1rem 0.5rem; | |
margin: 0.25rem; | |
border-radius: var(--radius-md); | |
} | |
.sidebar.collapsed .nav-icon { | |
margin: 0; | |
font-size: 20px; | |
} | |
/* User Section */ | |
.sidebar-footer { | |
padding: 1.5rem; | |
border-top: 1px solid rgba(255,255,255,0.1); | |
display: flex; | |
align-items: center; | |
gap: 1rem; | |
} | |
.user-avatar { | |
width: 40px; | |
height: 40px; | |
border-radius: 50%; | |
background: var(--gold-gradient); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: #000; | |
font-weight: bold; | |
flex-shrink: 0; | |
font-size: 16px; | |
} | |
.user-info { | |
flex-grow: 1; | |
} | |
.user-name { | |
font-weight: 600; | |
color: #fff; | |
font-size: 14px; | |
} | |
.user-role { | |
font-size: 12px; | |
color: #aaa; | |
} | |
.logout-btn { | |
background: none; | |
border: none; | |
color: #999; | |
font-size: 18px; | |
cursor: pointer; | |
transition: var(--transition); | |
padding: 0.5rem; | |
border-radius: var(--radius-sm); | |
} | |
.logout-btn:hover { | |
color: var(--accent-error); | |
background: rgba(239, 68, 68, 0.1); | |
} | |
.sidebar.collapsed .user-info, | |
.sidebar.collapsed .logout-btn { | |
display: none; | |
} | |
.sidebar.collapsed .user-avatar { | |
margin: 0 auto; | |
} | |
/* Main Content */ | |
.main-content { | |
flex: 1; | |
margin-right: var(--sidebar-width); | |
background: linear-gradient(to bottom, #f9fafb, #ffffff); | |
min-height: 100vh; | |
transition: var(--transition); | |
} | |
.main-content.collapsed { | |
margin-right: var(--sidebar-collapsed); | |
} | |
/* Header */ | |
.header { | |
background: var(--surface); | |
padding: 1.5rem 2rem; | |
border-bottom: 1px solid #e2e8f0; | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
position: sticky; | |
top: 0; | |
z-index: 100; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.05); | |
} | |
.header-title { | |
font-size: 24px; | |
font-weight: 700; | |
color: var(--text-primary); | |
background: var(--gold-gradient); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
background-clip: text; | |
} | |
.header-actions { | |
display: flex; | |
align-items: center; | |
gap: 1rem; | |
} | |
.search-box { | |
position: relative; | |
} | |
.search-input { | |
width: 300px; | |
padding: 0.75rem 1rem 0.75rem 2.5rem; | |
border: 1px solid #d1d5db; | |
border-radius: var(--radius-lg); | |
background: var(--surface-variant); | |
color: var(--text-primary); | |
font-size: 14px; | |
transition: var(--transition); | |
} | |
.search-input:focus { | |
outline: none; | |
border-color: var(--accent-primary); | |
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); | |
} | |
.search-icon { | |
position: absolute; | |
right: 0.75rem; | |
top: 50%; | |
transform: translateY(-50%); | |
color: var(--text-muted); | |
} | |
.btn { | |
padding: 0.5rem 1rem; | |
border: none; | |
border-radius: var(--radius-md); | |
font-size: 14px; | |
font-weight: 500; | |
cursor: pointer; | |
transition: var(--transition-smooth); | |
display: inline-flex; | |
align-items: center; | |
gap: 0.5rem; | |
} | |
.btn-primary { | |
background: var(--accent-primary); | |
color: white; | |
} | |
.btn-primary:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3); | |
} | |
/* Content Area */ | |
.content { | |
padding: 2rem; | |
} | |
/* Enhanced Stats Grid */ | |
.stats-grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
gap: 1.5rem; | |
margin-bottom: 2rem; | |
} | |
.stat-card { | |
background: var(--surface); | |
padding: 1.5rem; | |
border-radius: var(--radius-xl); | |
border: 1px solid #e2e8f0; | |
box-shadow: var(--shadow-layered); | |
transition: var(--transition-elegant); | |
position: relative; | |
overflow: hidden; | |
} | |
.stat-card::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: 3px; | |
background: var(--gold-gradient); | |
} | |
.stat-card:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 20px 30px -10px rgba(0,0,0,0.2); | |
} | |
.stat-card.gold::before { background: var(--gold-gradient); } | |
.stat-card.silver::before { background: var(--silver-gradient); } | |
.stat-card.bronze::before { background: var(--bronze-gradient); } | |
.stat-card.platinum::before { background: var(--platinum-gradient); } | |
.stat-header { | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
margin-bottom: 1rem; | |
} | |
.stat-title { | |
font-size: 14px; | |
color: var(--text-muted); | |
font-weight: 500; | |
} | |
.stat-icon { | |
width: 40px; | |
height: 40px; | |
border-radius: var(--radius-md); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: var(--text-inverse); | |
font-size: 18px; | |
} | |
.stat-card.gold .stat-icon { background: var(--gold-gradient); color: #000; } | |
.stat-card.silver .stat-icon { background: var(--silver-gradient); color: var(--text-primary); } | |
.stat-card.bronze .stat-icon { background: var(--bronze-gradient); } | |
.stat-card.platinum .stat-icon { background: var(--platinum-gradient); color: var(--text-primary); } | |
.stat-value { | |
font-size: 28px; | |
font-weight: 700; | |
color: var(--text-primary); | |
margin-bottom: 0.5rem; | |
} | |
.stat-change { | |
font-size: 12px; | |
display: flex; | |
align-items: center; | |
gap: 0.25rem; | |
} | |
.stat-change.positive { color: var(--success); } | |
.stat-change.negative { color: var(--error); } | |
/* Charts Section */ | |
.charts-grid { | |
display: grid; | |
grid-template-columns: 2fr 1fr; | |
gap: 2rem; | |
margin-bottom: 2rem; | |
} | |
.chart-card { | |
background: var(--surface); | |
padding: 1.5rem; | |
border-radius: var(--radius-xl); | |
border: 1px solid #e2e8f0; | |
box-shadow: var(--shadow-layered); | |
direction: rtl; | |
text-align: right; | |
} | |
.chart-header { | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
margin-bottom: 1.5rem; | |
} | |
.chart-title { | |
font-size: 16px; | |
font-weight: 600; | |
color: var(--text-primary); | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
} | |
.chart-container { | |
position: relative; | |
height: 300px; | |
direction: rtl; | |
} | |
/* Table */ | |
.table-card { | |
background: var(--surface); | |
border-radius: var(--radius-xl); | |
border: 1px solid #e2e8f0; | |
box-shadow: var(--shadow-layered); | |
overflow: hidden; | |
direction: rtl; | |
text-align: right; | |
} | |
.table-header { | |
padding: 1.5rem; | |
border-bottom: 1px solid #e2e8f0; | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
} | |
.table-title { | |
font-size: 16px; | |
font-weight: 600; | |
color: var(--text-primary); | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
} | |
.table { | |
width: 100%; | |
border-collapse: collapse; | |
} | |
.table th { | |
padding: 1rem 1.5rem; | |
text-align: right; | |
font-weight: 600; | |
color: var(--text-secondary); | |
background: var(--surface-variant); | |
border-bottom: 1px solid #e2e8f0; | |
font-size: 13px; | |
} | |
.table td { | |
padding: 1rem 1.5rem; | |
color: var(--text-primary); | |
border-bottom: 1px solid #f1f5f9; | |
font-size: 14px; | |
text-align: right; | |
} | |
.table tbody tr:hover { | |
background: var(--surface-variant); | |
} | |
.status-badge { | |
padding: 0.25rem 0.75rem; | |
border-radius: var(--radius-sm); | |
font-size: 12px; | |
font-weight: 500; | |
color: var(--text-inverse); | |
} | |
.status-badge.published { background: var(--success); } | |
.status-badge.pending { background: var(--warning); } | |
.status-badge.error { background: var(--error); } | |
/* AI Panel */ | |
.ai-panel { | |
background: var(--surface); | |
border-radius: var(--radius-xl); | |
border: 1px solid #e2e8f0; | |
box-shadow: var(--shadow-layered); | |
margin-top: 2rem; | |
overflow: hidden; | |
} | |
.ai-panel-header { | |
padding: 1.5rem; | |
border-bottom: 1px solid #e2e8f0; | |
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); | |
color: white; | |
} | |
.ai-panel-title { | |
font-size: 16px; | |
font-weight: 600; | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
} | |
.ai-suggestions-list { | |
padding: 1rem; | |
} | |
.ai-suggestion-item { | |
padding: 1rem; | |
border: 1px solid #e2e8f0; | |
border-radius: var(--radius-md); | |
margin-bottom: 1rem; | |
background: var(--surface-variant); | |
} | |
.confidence-badge { | |
display: inline-block; | |
padding: 0.25rem 0.5rem; | |
border-radius: var(--radius-sm); | |
font-size: 11px; | |
font-weight: 600; | |
margin-right: 0.5rem; | |
} | |
.confidence-high { background: var(--success); color: white; } | |
.confidence-medium { background: var(--warning); color: white; } | |
.confidence-low { background: var(--error); color: white; } | |
/* No results message */ | |
.no-results { | |
text-align: center; | |
padding: 2rem; | |
color: var(--text-muted); | |
font-style: italic; | |
} | |
/* Chart placeholder for when Chart.js fails */ | |
.chart-placeholder { | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
height: 100%; | |
color: var(--text-muted); | |
font-style: italic; | |
background: var(--surface-variant); | |
border-radius: var(--radius-md); | |
border: 2px dashed #ddd; | |
} | |
/* Modal Styles */ | |
.modal-overlay { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: rgba(0, 0, 0, 0.5); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
z-index: 2000; | |
backdrop-filter: blur(5px); | |
} | |
.modal-content { | |
background: var(--surface); | |
border-radius: var(--radius-xl); | |
box-shadow: var(--shadow-xl); | |
max-width: 600px; | |
width: 90%; | |
max-height: 80vh; | |
overflow: hidden; | |
direction: rtl; | |
} | |
.modal-header { | |
padding: 1.5rem; | |
border-bottom: 1px solid #e2e8f0; | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
} | |
.modal-title { | |
font-size: 18px; | |
font-weight: 600; | |
color: var(--text-primary); | |
} | |
.modal-close { | |
background: none; | |
border: none; | |
font-size: 20px; | |
color: var(--text-muted); | |
cursor: pointer; | |
padding: 0.5rem; | |
border-radius: var(--radius-sm); | |
transition: var(--transition); | |
} | |
.modal-close:hover { | |
color: var(--text-primary); | |
background: var(--surface-variant); | |
} | |
.modal-body { | |
padding: 1.5rem; | |
max-height: 60vh; | |
overflow-y: auto; | |
} | |
.modal-footer { | |
padding: 1.5rem; | |
border-top: 1px solid #e2e8f0; | |
display: flex; | |
gap: 1rem; | |
justify-content: flex-end; | |
} | |
/* Toast Notifications */ | |
.toast-container { | |
position: fixed; | |
top: 20px; | |
left: 20px; | |
z-index: 3000; | |
display: flex; | |
flex-direction: column; | |
gap: 0.5rem; | |
} | |
.toast { | |
background: var(--surface); | |
border-radius: var(--radius-md); | |
padding: 1rem 1.5rem; | |
box-shadow: var(--shadow-lg); | |
border-left: 4px solid var(--accent-primary); | |
min-width: 300px; | |
animation: slideIn 0.3s ease; | |
} | |
.toast.success { | |
border-left-color: var(--success); | |
} | |
.toast.error { | |
border-left-color: var(--error); | |
} | |
.toast.warning { | |
border-left-color: var(--warning); | |
} | |
.toast-header { | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
margin-bottom: 0.5rem; | |
} | |
.toast-title { | |
font-weight: 600; | |
color: var(--text-primary); | |
} | |
.toast-close { | |
background: none; | |
border: none; | |
color: var(--text-muted); | |
cursor: pointer; | |
font-size: 16px; | |
} | |
.toast-message { | |
color: var(--text-secondary); | |
font-size: 14px; | |
} | |
@keyframes slideIn { | |
from { | |
transform: translateX(-100%); | |
opacity: 0; | |
} | |
to { | |
transform: translateX(0); | |
opacity: 1; | |
} | |
} | |
@keyframes slideOut { | |
from { | |
transform: translateX(0); | |
opacity: 1; | |
} | |
to { | |
transform: translateX(-100%); | |
opacity: 0; | |
} | |
} | |
/* No results styling */ | |
.no-results { | |
text-align: center; | |
padding: 3rem 2rem; | |
color: var(--text-muted); | |
} | |
.no-results i { | |
display: block; | |
margin-bottom: 1rem; | |
} | |
/* Confidence badges */ | |
.confidence-badge { | |
padding: 0.25rem 0.5rem; | |
border-radius: var(--radius-sm); | |
font-size: 0.75rem; | |
font-weight: 500; | |
} | |
.confidence-high { | |
background: var(--success); | |
color: white; | |
} | |
.confidence-medium { | |
background: var(--warning); | |
color: white; | |
} | |
.confidence-low { | |
background: var(--error); | |
color: white; | |
} | |
/* AI suggestions panel */ | |
.ai-suggestion-item { | |
background: var(--surface); | |
border: 1px solid var(--surface-variant); | |
border-radius: var(--radius-md); | |
padding: 1rem; | |
margin-bottom: 1rem; | |
} | |
.ai-suggestion-item:last-child { | |
margin-bottom: 0; | |
} | |
/* Enhanced Mobile Responsive Design */ | |
@media (max-width: 768px) { | |
.mobile-menu-btn { | |
display: block; | |
} | |
.sidebar { | |
width: 80%; | |
transform: translateX(100%); | |
transition: transform 0.3s ease; | |
} | |
.sidebar.open { | |
transform: translateX(0); | |
} | |
.main-content, | |
.main-content.collapsed { | |
margin-right: 0; | |
} | |
.header { | |
padding: 1rem; | |
padding-left: 4rem; | |
} | |
.content { | |
padding: 1rem; | |
} | |
.search-input { | |
width: 200px; | |
} | |
.header-title { | |
font-size: 20px; | |
} | |
} | |
@media (max-width: 480px) { | |
.search-input { | |
width: 150px; | |
} | |
.header-actions { | |
flex-direction: column; | |
gap: 0.5rem; | |
} | |
.stat-card { | |
padding: 1rem; | |
} | |
.chart-container { | |
height: 250px; | |
} | |
.modal-content { | |
width: 95%; | |
margin: 1rem; | |
} | |
.toast { | |
min-width: 250px; | |
} | |
} | |
/* Additional Polish Styles */ | |
.btn:disabled { | |
opacity: 0.6; | |
cursor: not-allowed; | |
} | |
.btn:disabled:hover { | |
transform: none; | |
box-shadow: none; | |
} | |
/* Smooth scrolling */ | |
html { | |
scroll-behavior: smooth; | |
} | |
/* Focus styles for accessibility */ | |
.btn:focus, | |
.search-input:focus, | |
.modal-close:focus { | |
outline: 2px solid var(--accent-primary); | |
outline-offset: 2px; | |
} | |
/* Loading states */ | |
.loading { | |
opacity: 0.6; | |
pointer-events: none; | |
} | |
.loading::after { | |
content: ''; | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
width: 20px; | |
height: 20px; | |
margin: -10px 0 0 -10px; | |
border: 2px solid var(--accent-primary); | |
border-top: 2px solid transparent; | |
border-radius: 50%; | |
animation: spin 1s linear infinite; | |
} | |
/* Hover effects for interactive elements */ | |
.nav-link:hover, | |
.btn:hover, | |
.stat-card:hover, | |
.ai-suggestion-item:hover { | |
transform: translateY(-2px); | |
box-shadow: var(--shadow-lg); | |
} | |
/* Print styles */ | |
@media print { | |
.sidebar, | |
.header, | |
.mobile-menu-btn, | |
.toast-container, | |
.modal-overlay { | |
display: none ; | |
} | |
.main-content { | |
margin: 0 ; | |
} | |
.content { | |
padding: 0 ; | |
} | |
} | |
/* High contrast mode support */ | |
@media (prefers-contrast: high) { | |
:root { | |
--text-primary: #000000; | |
--text-secondary: #333333; | |
--text-muted: #666666; | |
--surface: #ffffff; | |
--surface-variant: #f0f0f0; | |
} | |
} | |
/* Reduced motion support */ | |
@media (prefers-reduced-motion: reduce) { | |
*, | |
*::before, | |
*::after { | |
animation-duration: 0.01ms ; | |
animation-iteration-count: 1 ; | |
transition-duration: 0.01ms ; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<!-- Loading Screen --> | |
<div class="loading-screen" id="loadingScreen"> | |
<div class="spinner"></div> | |
<div class="loading-text">در حال بارگذاری...</div> | |
</div> | |
<!-- Mobile Menu Button --> | |
<button class="mobile-menu-btn" id="mobileMenuBtn" type="button" onclick="toggleMobileSidebar()" aria-label="منوی موبایل"> | |
<i class="fas fa-bars"></i> | |
</button> | |
<!-- Sidebar Overlay for Mobile --> | |
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeMobileSidebar()"></div> | |
<!-- Dashboard Container --> | |
<div class="dashboard" id="dashboard"> | |
<!-- Enhanced Sidebar --> | |
<aside class="sidebar" id="sidebar"> | |
<div class="sidebar-header"> | |
<div class="toggle-btn" onclick="toggleSidebar()"> | |
<i class="fas fa-chevron-left"></i> | |
</div> | |
<div class="logo"> | |
<div class="logo-icon"> | |
<i class="fas fa-balance-scale"></i> | |
</div> | |
<div class="logo-text">سیستم حقوقی پیشرفته</div> | |
</div> | |
<div class="subtitle">مدیریت هوشمند منابع قضایی</div> | |
</div> | |
<nav class="nav"> | |
<div class="nav-group"> | |
<div class="nav-group-title">منوی اصلی</div> | |
<div class="nav-item"> | |
<a href="#" class="nav-link active"> | |
<div class="nav-icon"> | |
<i class="fas fa-chart-line"></i> | |
</div> | |
<span class="nav-text">داشبورد اصلی</span> | |
</a> | |
</div> | |
<div class="nav-item"> | |
<a href="#" class="nav-link"> | |
<div class="nav-icon"> | |
<i class="fas fa-folder"></i> | |
</div> | |
<span class="nav-text">دستهبندیها</span> | |
</a> | |
</div> | |
<div class="nav-item"> | |
<a href="#" class="nav-link"> | |
<div class="nav-icon"> | |
<i class="fas fa-database"></i> | |
</div> | |
<span class="nav-text">منابع داده</span> | |
</a> | |
</div> | |
<div class="nav-item"> | |
<a href="#" class="nav-link"> | |
<div class="nav-icon"> | |
<i class="fas fa-users"></i> | |
</div> | |
<span class="nav-text">کاربران سیستم</span> | |
</a> | |
</div> | |
</div> | |
<div class="nav-group"> | |
<div class="nav-group-title">ابزارها</div> | |
<div class="nav-item"> | |
<a href="#" class="nav-link"> | |
<div class="nav-icon"> | |
<i class="fas fa-search"></i> | |
</div> | |
<span class="nav-text">جستجوی پیشرفته</span> | |
</a> | |
</div> | |
<div class="nav-item"> | |
<a href="#" class="nav-link"> | |
<div class="nav-icon"> | |
<i class="fas fa-chart-pie"></i> | |
</div> | |
<span class="nav-text">گزارشهای تحلیلی</span> | |
</a> | |
</div> | |
<div class="nav-item"> | |
<a href="#" class="nav-link"> | |
<div class="nav-icon"> | |
<i class="fas fa-cog"></i> | |
</div> | |
<span class="nav-text">تنظیمات سیستم</span> | |
</a> | |
</div> | |
</div> | |
</nav> | |
<div class="sidebar-footer"> | |
<div class="user-avatar">فا</div> | |
<div class="user-info"> | |
<div class="user-name">فاطمه احمدی</div> | |
<div class="user-role">مدیر سیستم حقوقی</div> | |
</div> | |
<button class="logout-btn" type="button" aria-label="خروج از سیستم"> | |
<i class="fas fa-sign-out-alt"></i> | |
</button> | |
</div> | |
</aside> | |
<!-- Main Content --> | |
<main class="main-content" id="mainContent"> | |
<!-- Header --> | |
<header class="header"> | |
<h1 class="header-title">داشبورد مدیریتی حقوقی</h1> | |
<div class="header-actions"> | |
<div class="search-box"> | |
<input type="text" class="search-input" id="searchInput" placeholder="جستجو در اسناد حقوقی..."> | |
<i class="fas fa-search search-icon"></i> | |
</div> | |
<button class="btn btn-primary" type="button"> | |
<i class="fas fa-plus"></i> | |
سند جدید | |
</button> | |
</div> | |
</header> | |
<!-- Content --> | |
<div class="content"> | |
<!-- Stats Grid --> | |
<div class="stats-grid" id="stats"> | |
<!-- Dynamic stats cards will be populated by JavaScript --> | |
</div> | |
<!-- Charts Grid --> | |
<div class="charts-grid" id="charts"> | |
<!-- Dynamic charts will be populated by JavaScript --> | |
</div> | |
<!-- Documents Table --> | |
<div class="table-card" id="documents"> | |
<!-- Dynamic documents table will be populated by JavaScript --> | |
</div> | |
<!-- AI Suggestions Panel --> | |
<div class="ai-panel" id="aiSuggestions"> | |
<div class="ai-panel-header"> | |
<div class="ai-panel-title"> | |
<i class="fas fa-brain"></i> | |
پیشنهادات هوش مصنوعی | |
</div> | |
</div> | |
<div class="ai-suggestions-list" id="aiSuggestionsList"> | |
<!-- AI suggestions will be populated by JavaScript --> | |
</div> | |
</div> | |
<!-- Document Details Modal --> | |
<div class="modal-overlay" id="documentModal" style="display: none;"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h3 class="modal-title">جزئیات سند</h3> | |
<button class="modal-close" type="button" onclick="closeDocumentModal()" aria-label="بستن"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="modal-body" id="modalBody"> | |
<!-- Document details will be populated by JavaScript --> | |
</div> | |
<div class="modal-footer"> | |
<button class="btn btn-primary" type="button" onclick="approveDocument()"> | |
<i class="fas fa-check"></i> | |
تایید | |
</button> | |
<button class="btn" type="button" onclick="rejectDocument()"> | |
<i class="fas fa-times"></i> | |
رد | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Toast Notifications --> | |
<div class="toast-container" id="toastContainer"> | |
<!-- Toast notifications will be added here --> | |
</div> | |
</div> | |
</main> | |
</div> | |
<script> | |
// Basic initialization | |
document.addEventListener('DOMContentLoaded', function() { | |
// Show loading screen | |
setTimeout(() => { | |
document.getElementById('loadingScreen').classList.add('hidden'); | |
document.getElementById('dashboard').classList.add('loaded'); | |
}, 1500); | |
}); | |
// Enhanced sidebar functionality | |
function toggleSidebar() { | |
const sidebar = document.getElementById('sidebar'); | |
const mainContent = document.getElementById('mainContent'); | |
sidebar.classList.toggle('collapsed'); | |
mainContent.classList.toggle('collapsed'); | |
} | |
// Mobile sidebar functions | |
function toggleMobileSidebar() { | |
const sidebar = document.getElementById('sidebar'); | |
const overlay = document.getElementById('sidebarOverlay'); | |
sidebar.classList.add('open'); | |
overlay.classList.add('active'); | |
} | |
function closeMobileSidebar() { | |
const sidebar = document.getElementById('sidebar'); | |
const overlay = document.getElementById('sidebarOverlay'); | |
sidebar.classList.remove('open'); | |
overlay.classList.remove('active'); | |
} | |
// Global variables for data management | |
let currentData = { | |
documents: [], | |
stats: {}, | |
charts: {}, | |
aiSuggestions: [] | |
}; | |
let currentPage = 1; | |
const itemsPerPage = 10; | |
let websocket = null; | |
// API endpoints - Updated to work with your FastAPI backend | |
const API_ENDPOINTS = { | |
dashboardSummary: 'http://localhost:8000/api/dashboard-summary', | |
documents: 'http://localhost:8000/api/documents', | |
chartsData: 'http://localhost:8000/api/charts-data', | |
aiSuggestions: 'http://localhost:8000/api/ai-suggestions', | |
trainAI: 'http://localhost:8000/api/train-ai', | |
scrapeTrigger: 'http://localhost:8000/api/scrape-trigger' | |
}; | |
// WebSocket connection - Updated for your backend | |
function connectWebSocket() { | |
try { | |
// For now, we'll use polling instead of WebSocket since your backend doesn't have WebSocket yet | |
console.log('WebSocket not implemented yet - using polling'); | |
// Set up polling for updates every 30 seconds | |
setInterval(() => { | |
loadDashboardData(); | |
}, 30000); | |
} catch (error) { | |
console.error('Failed to connect WebSocket:', error); | |
} | |
} | |
// Handle WebSocket messages | |
function handleWebSocketMessage(data) { | |
switch (data.type) { | |
case 'new_document': | |
showToast('سند جدید اضافه شد', 'success'); | |
loadDashboardData(); | |
break; | |
case 'scraping_completed': | |
showToast(`${data.documents_added} سند جدید اضافه شد`, 'success'); | |
loadDashboardData(); | |
break; | |
case 'ai_training_update': | |
showToast('آموزش هوش مصنوعی بهروزرسانی شد', 'info'); | |
loadAISuggestions(); | |
break; | |
default: | |
console.log('Unknown WebSocket message type:', data.type); | |
} | |
} | |
// Load dashboard data with error handling | |
async function loadDashboardData() { | |
try { | |
console.log('Loading dashboard data...'); | |
// Load stats | |
const statsResponse = await fetch(API_ENDPOINTS.dashboardSummary); | |
if (!statsResponse.ok) { | |
throw new Error(`Stats API error: ${statsResponse.status}`); | |
} | |
const stats = await statsResponse.json(); | |
currentData.stats = stats; | |
updateStatsDisplay(stats); | |
// Load charts data | |
const chartsResponse = await fetch(API_ENDPOINTS.chartsData); | |
if (!chartsResponse.ok) { | |
throw new Error(`Charts API error: ${chartsResponse.status}`); | |
} | |
const charts = await chartsResponse.json(); | |
currentData.charts = charts; | |
updateChartsDisplay(charts); | |
// Load documents | |
await loadDocuments(); | |
// Load AI suggestions (if endpoint exists) | |
try { | |
await loadAISuggestions(); | |
} catch (error) { | |
console.log('AI suggestions endpoint not available yet'); | |
} | |
} catch (error) { | |
console.error('Error loading dashboard data:', error); | |
showToast('خطا در بارگذاری اطلاعات: ' + error.message, 'error'); | |
// Show fallback data | |
showFallbackData(); | |
} | |
} | |
// Show fallback data when API is not available | |
function showFallbackData() { | |
const fallbackStats = { | |
total_documents: 0, | |
documents_today: 0, | |
error_documents: 0, | |
average_score: 0 | |
}; | |
updateStatsDisplay(fallbackStats); | |
const fallbackCharts = { | |
trend_data: [], | |
category_data: [] | |
}; | |
updateChartsDisplay(fallbackCharts); | |
updateDocumentsTable([]); | |
} | |
// Update stats display with better error handling | |
function updateStatsDisplay(stats) { | |
const statsContainer = document.getElementById('stats'); | |
const statsCards = [ | |
{ | |
title: 'کل اسناد', | |
value: stats.total_documents || 0, | |
icon: 'fas fa-file-alt', | |
type: 'gold', | |
change: '+12.5%' | |
}, | |
{ | |
title: 'اسناد جدید امروز', | |
value: stats.documents_today || 0, | |
icon: 'fas fa-file-plus', | |
type: 'silver', | |
change: '+8.3%' | |
}, | |
{ | |
title: 'اسناد با خطا', | |
value: stats.error_documents || 0, | |
icon: 'fas fa-exclamation-triangle', | |
type: 'bronze', | |
change: '-15.2%' | |
}, | |
{ | |
title: 'امتیاز میانگین', | |
value: stats.average_score || 0, | |
icon: 'fas fa-star', | |
type: 'platinum', | |
change: '+0.3' | |
} | |
]; | |
statsContainer.innerHTML = statsCards.map(card => ` | |
<div class="stat-card ${card.type}"> | |
<div class="stat-header"> | |
<div class="stat-title">${card.title}</div> | |
<div class="stat-icon"> | |
<i class="${card.icon}"></i> | |
</div> | |
</div> | |
<div class="stat-value">${card.value.toLocaleString()}</div> | |
<div class="stat-change positive"> | |
<i class="fas fa-arrow-up"></i> | |
${card.change} | |
</div> | |
</div> | |
`).join(''); | |
} | |
// Update charts display with better error handling | |
function updateChartsDisplay(charts) { | |
const chartsContainer = document.getElementById('charts'); | |
chartsContainer.innerHTML = ` | |
<div class="chart-card"> | |
<div class="chart-header"> | |
<div class="chart-title"> | |
<i class="fas fa-chart-line"></i> | |
روند جمعآوری اسناد | |
</div> | |
</div> | |
<div class="chart-container"> | |
<canvas id="trendChart"></canvas> | |
<div class="chart-placeholder" id="trendPlaceholder" style="display: none;"> | |
<i class="fas fa-chart-line" style="margin-left: 0.5rem;"></i> | |
نمودار در حال بارگذاری... | |
</div> | |
</div> | |
</div> | |
<div class="chart-card"> | |
<div class="chart-header"> | |
<div class="chart-title"> | |
<i class="fas fa-chart-pie"></i> | |
توزیع دستهبندی | |
</div> | |
</div> | |
<div class="chart-container"> | |
<canvas id="categoryChart"></canvas> | |
<div class="chart-placeholder" id="categoryPlaceholder" style="display: none;"> | |
<i class="fas fa-chart-pie" style="margin-left: 0.5rem;"></i> | |
نمودار در حال بارگذاری... | |
</div> | |
</div> | |
</div> | |
`; | |
// Initialize charts after DOM update | |
setTimeout(() => { | |
initializeCharts(charts); | |
}, 100); | |
} | |
// Load documents with better error handling | |
async function loadDocuments(page = 1, filters = {}) { | |
try { | |
const params = new URLSearchParams({ | |
limit: itemsPerPage, | |
offset: (page - 1) * itemsPerPage, | |
...filters | |
}); | |
const response = await fetch(`${API_ENDPOINTS.documents}?${params}`); | |
if (!response.ok) { | |
throw new Error(`Documents API error: ${response.status}`); | |
} | |
const documents = await response.json(); | |
currentData.documents = documents; | |
currentPage = page; | |
updateDocumentsTable(documents); | |
} catch (error) { | |
console.error('Error loading documents:', error); | |
showToast('خطا در بارگذاری اسناد: ' + error.message, 'error'); | |
updateDocumentsTable([]); | |
} | |
} | |
// Update documents table with better error handling | |
function updateDocumentsTable(documents) { | |
const tableContainer = document.getElementById('documents'); | |
if (!documents || documents.length === 0) { | |
tableContainer.innerHTML = ` | |
<div class="table-header"> | |
<div class="table-title"> | |
<i class="fas fa-list"></i> | |
آخرین اسناد جمعآوری شده | |
</div> | |
<button class="btn btn-primary" type="button" onclick="triggerScraping()"> | |
<i class="fas fa-sync"></i> | |
شروع جمعآوری | |
</button> | |
</div> | |
<div class="no-results"> | |
<i class="fas fa-inbox" style="font-size: 3rem; color: var(--text-muted); margin-bottom: 1rem;"></i> | |
<p>هیچ سندی یافت نشد</p> | |
<p style="font-size: 0.9rem; color: var(--text-muted);">برای شروع، دکمه "شروع جمعآوری" را کلیک کنید</p> | |
</div> | |
`; | |
return; | |
} | |
const tableHTML = ` | |
<div class="table-header"> | |
<div class="table-title"> | |
<i class="fas fa-list"></i> | |
آخرین اسناد جمعآوری شده | |
</div> | |
<button class="btn btn-primary" type="button" onclick="triggerScraping()"> | |
<i class="fas fa-sync"></i> | |
جمعآوری جدید | |
</button> | |
</div> | |
<table class="table"> | |
<thead> | |
<tr> | |
<th>عنوان سند</th> | |
<th>منبع</th> | |
<th>دستهبندی</th> | |
<th>امتیاز کیفیت</th> | |
<th>تاریخ</th> | |
<th>وضعیت</th> | |
<th>عملیات</th> | |
</tr> | |
</thead> | |
<tbody> | |
${documents.map(doc => ` | |
<tr> | |
<td><strong>${doc.title || 'بدون عنوان'}</strong></td> | |
<td>${doc.source || 'نامشخص'}</td> | |
<td>${doc.category || 'نامشخص'}</td> | |
<td><strong style="color: var(--accent-primary);">${doc.final_score?.toFixed(1) || 'N/A'}</strong></td> | |
<td>${doc.publication_date || doc.extracted_at?.split('T')[0] || 'N/A'}</td> | |
<td><span class="status-badge ${doc.status || 'pending'}">${getStatusText(doc.status)}</span></td> | |
<td> | |
<button class="btn" type="button" onclick="viewDocument('${doc.id}')" style="font-size: 12px; padding: 0.25rem 0.5rem;"> | |
<i class="fas fa-eye"></i> | |
مشاهده | |
</button> | |
</td> | |
</tr> | |
`).join('')} | |
</tbody> | |
</table> | |
`; | |
tableContainer.innerHTML = tableHTML; | |
} | |
// Load AI suggestions with better error handling | |
async function loadAISuggestions() { | |
try { | |
const response = await fetch(API_ENDPOINTS.aiSuggestions); | |
if (!response.ok) { | |
throw new Error(`AI suggestions API error: ${response.status}`); | |
} | |
const suggestions = await response.json(); | |
currentData.aiSuggestions = suggestions; | |
updateAISuggestions(suggestions); | |
} catch (error) { | |
console.error('Error loading AI suggestions:', error); | |
// Don't show error toast for AI suggestions as it's optional | |
updateAISuggestions([]); | |
} | |
} | |
// Update AI suggestions | |
function updateAISuggestions(suggestions) { | |
const suggestionsContainer = document.getElementById('aiSuggestionsList'); | |
if (!suggestions || suggestions.length === 0) { | |
suggestionsContainer.innerHTML = '<p style="text-align: center; color: var(--text-muted); padding: 2rem;">هیچ پیشنهاد هوش مصنوعی موجود نیست</p>'; | |
return; | |
} | |
suggestionsContainer.innerHTML = suggestions.map(suggestion => ` | |
<div class="ai-suggestion-item"> | |
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.5rem;"> | |
<h4 style="margin: 0; color: var(--text-primary);">${suggestion.title || 'بدون عنوان'}</h4> | |
<span class="confidence-badge ${getConfidenceClass(suggestion.confidence)}"> | |
${getConfidenceText(suggestion.confidence)} | |
</span> | |
</div> | |
<p style="color: var(--text-secondary); margin-bottom: 0.5rem; font-size: 14px;"> | |
پیشنهاد دستهبندی: <strong>${suggestion.predicted_category || 'نامشخص'}</strong> | |
</p> | |
<div style="display: flex; gap: 0.5rem;"> | |
<button class="btn btn-primary" type="button" onclick="approveSuggestion('${suggestion.id}')" style="font-size: 12px; padding: 0.25rem 0.5rem;"> | |
<i class="fas fa-check"></i> | |
تایید | |
</button> | |
<button class="btn" type="button" onclick="rejectSuggestion('${suggestion.id}')" style="font-size: 12px; padding: 0.25rem 0.5rem;"> | |
<i class="fas fa-times"></i> | |
رد | |
</button> | |
</div> | |
</div> | |
`).join(''); | |
} | |
// Trigger scraping function | |
async function triggerScraping() { | |
try { | |
showToast('در حال شروع جمعآوری اسناد...', 'info'); | |
const response = await fetch(API_ENDPOINTS.scrapeTrigger, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ manual_trigger: true }) | |
}); | |
if (!response.ok) { | |
throw new Error(`Scraping API error: ${response.status}`); | |
} | |
const result = await response.json(); | |
showToast('جمعآوری اسناد شروع شد', 'success'); | |
// Reload data after a delay to show new documents | |
setTimeout(() => { | |
loadDashboardData(); | |
}, 5000); | |
} catch (error) { | |
console.error('Error triggering scraping:', error); | |
showToast('خطا در شروع جمعآوری: ' + error.message, 'error'); | |
} | |
} | |
// Helper functions | |
function getStatusText(status) { | |
const statusMap = { | |
'published': 'منتشر شده', | |
'pending': 'در حال بررسی', | |
'error': 'نیاز به اصلاح', | |
'processing': 'در حال پردازش', | |
'completed': 'تکمیل شده' | |
}; | |
return statusMap[status] || status || 'نامشخص'; | |
} | |
function getConfidenceClass(confidence) { | |
if (confidence >= 8) return 'confidence-high'; | |
if (confidence >= 5) return 'confidence-medium'; | |
return 'confidence-low'; | |
} | |
function getConfidenceText(confidence) { | |
if (confidence >= 8) return 'عالی'; | |
if (confidence >= 5) return 'متوسط'; | |
return 'ضعیف'; | |
} | |
// Modal functions | |
function viewDocument(documentId) { | |
const document = currentData.documents.find(doc => doc.id === documentId); | |
if (!document) { | |
showToast('سند یافت نشد', 'error'); | |
return; | |
} | |
const modalBody = document.getElementById('modalBody'); | |
modalBody.innerHTML = ` | |
<div style="margin-bottom: 1rem;"> | |
<h4 style="color: var(--text-primary); margin-bottom: 0.5rem;">${document.title || 'بدون عنوان'}</h4> | |
<p style="color: var(--text-secondary); font-size: 14px;">${document.document_number || 'شماره سند موجود نیست'}</p> | |
</div> | |
<div style="margin-bottom: 1rem;"> | |
<h5 style="color: var(--text-primary); margin-bottom: 0.5rem;">جزئیات سند</h5> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; font-size: 14px;"> | |
<div><strong>منبع:</strong> ${document.source || 'نامشخص'}</div> | |
<div><strong>دستهبندی:</strong> ${document.category || 'نامشخص'}</div> | |
<div><strong>امتیاز کیفیت:</strong> ${document.final_score?.toFixed(1) || 'N/A'}</div> | |
<div><strong>وضعیت:</strong> <span class="status-badge ${document.status || 'pending'}">${getStatusText(document.status)}</span></div> | |
</div> | |
</div> | |
<div style="margin-bottom: 1rem;"> | |
<h5 style="color: var(--text-primary); margin-bottom: 0.5rem;">متن سند</h5> | |
<div style="background: var(--surface-variant); padding: 1rem; border-radius: var(--radius-md); max-height: 200px; overflow-y: auto; font-size: 14px; line-height: 1.6;"> | |
${document.full_text || document.content || 'متن سند موجود نیست'} | |
</div> | |
</div> | |
`; | |
document.getElementById('documentModal').style.display = 'flex'; | |
} | |
function closeDocumentModal() { | |
document.getElementById('documentModal').style.display = 'none'; | |
} | |
function approveDocument() { | |
// Implementation for document approval | |
showToast('سند تایید شد', 'success'); | |
closeDocumentModal(); | |
} | |
function rejectDocument() { | |
// Implementation for document rejection | |
showToast('سند رد شد', 'warning'); | |
closeDocumentModal(); | |
} | |
// AI suggestion functions with better error handling | |
async function approveSuggestion(suggestionId) { | |
try { | |
const response = await fetch(API_ENDPOINTS.trainAI, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ | |
document_id: suggestionId, | |
feedback_type: 'approved', | |
feedback_score: 10, | |
feedback_text: 'تایید شده' | |
}) | |
}); | |
if (!response.ok) { | |
throw new Error(`Training API error: ${response.status}`); | |
} | |
showToast('پیشنهاد تایید شد', 'success'); | |
loadAISuggestions(); | |
} catch (error) { | |
console.error('Error approving suggestion:', error); | |
showToast('خطا در تایید پیشنهاد: ' + error.message, 'error'); | |
} | |
} | |
async function rejectSuggestion(suggestionId) { | |
try { | |
const response = await fetch(API_ENDPOINTS.trainAI, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ | |
document_id: suggestionId, | |
feedback_type: 'rejected', | |
feedback_score: 0, | |
feedback_text: 'رد شده' | |
}) | |
}); | |
if (!response.ok) { | |
throw new Error(`Training API error: ${response.status}`); | |
} | |
showToast('پیشنهاد رد شد', 'warning'); | |
loadAISuggestions(); | |
} catch (error) { | |
console.error('Error rejecting suggestion:', error); | |
showToast('خطا در رد پیشنهاد: ' + error.message, 'error'); | |
} | |
} | |
// Toast notification function | |
function showToast(message, type = 'info') { | |
const toastContainer = document.getElementById('toastContainer'); | |
const toastId = 'toast-' + Date.now(); | |
const toast = document.createElement('div'); | |
toast.className = `toast ${type}`; | |
toast.id = toastId; | |
toast.innerHTML = ` | |
<div class="toast-header"> | |
<div class="toast-title">${type === 'success' ? 'موفقیت' : type === 'error' ? 'خطا' : type === 'warning' ? 'هشدار' : 'اطلاعات'}</div> | |
<button class="toast-close" type="button" onclick="removeToast('${toastId}')" aria-label="بستن"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div class="toast-message">${message}</div> | |
`; | |
toastContainer.appendChild(toast); | |
// Auto remove after 5 seconds | |
setTimeout(() => removeToast(toastId), 5000); | |
} | |
function removeToast(toastId) { | |
const toast = document.getElementById(toastId); | |
if (toast) { | |
toast.style.animation = 'slideOut 0.3s ease'; | |
setTimeout(() => toast.remove(), 300); | |
} | |
} | |
// Chart initialization | |
function initializeCharts(chartsData) { | |
// This will be implemented when Chart.js is loaded | |
console.log('Charts data:', chartsData); | |
} | |
// Search functionality | |
function setupSearch() { | |
const searchInput = document.getElementById('searchInput'); | |
let searchTimeout; | |
searchInput.addEventListener('input', (e) => { | |
clearTimeout(searchTimeout); | |
searchTimeout = setTimeout(() => { | |
const term = e.target.value.toLowerCase().trim(); | |
if (term) { | |
loadDocuments(1, { search: term }); | |
} else { | |
loadDocuments(1); | |
} | |
}, 300); | |
}); | |
} | |
// Initialize dashboard | |
document.addEventListener('DOMContentLoaded', function() { | |
// Show loading screen | |
setTimeout(() => { | |
document.getElementById('loadingScreen').classList.add('hidden'); | |
document.getElementById('dashboard').classList.add('loaded'); | |
// Initialize components | |
setTimeout(() => { | |
connectWebSocket(); | |
loadDashboardData(); | |
setupSearch(); | |
}, 500); | |
}, 1500); | |
}); | |
</script> | |
</body> | |
</html> | |