Hoghoghi / app /frontend /scraping.html
Really-amin's picture
Upload 25 files
89ba751 verified
raw
history blame
69.9 kB
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>سامانه اسکراپینگ حقوقی | داشبورد هوشمند</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- Load API Client and Core System -->
<script src="js/api-client.js"></script>
<script src="js/core.js"></script>
<script src="js/notifications.js"></script>
<script src="js/scraping-control.js"></script>
<style>
:root {
/* رنگ‌بندی مدرن و هارمونیک - منطبق با index */
--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%);
--purple-gradient: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
/* سایه‌های ملایم */
--shadow-xs: 0 1px 3px rgba(0, 0, 0, 0.05);
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08);
--shadow-md: 0 4px 15px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 8px 25px rgba(0, 0, 0, 0.12);
--shadow-glow-primary: 0 0 20px rgba(59, 130, 246, 0.15);
--shadow-glow-success: 0 0 20px rgba(16, 185, 129, 0.15);
--shadow-glow-warning: 0 0 20px rgba(245, 158, 11, 0.15);
--shadow-glow-purple: 0 0 20px rgba(139, 92, 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-height: 100vh;
width: 100%;
}
/* سایدبار کامپکت - منطبق با index */
.sidebar {
width: var(--sidebar-width);
background: linear-gradient(135deg,
rgba(248, 250, 252, 0.98) 0%,
rgba(241, 245, 249, 0.95) 25%,
rgba(226, 232, 240, 0.98) 50%,
rgba(203, 213, 225, 0.95) 75%,
rgba(148, 163, 184, 0.1) 100%);
backdrop-filter: blur(25px);
padding: 1rem 0;
position: fixed;
height: 100vh;
right: 0;
top: 0;
z-index: 1000;
overflow-y: auto;
box-shadow: -8px 0 32px rgba(59, 130, 246, 0.12);
border-left: 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-bottom: 1px solid rgba(59, 130, 246, 0.12);
margin-bottom: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.4) 0%,
rgba(248, 250, 252, 0.2) 100%);
margin: 0 0.5rem 1rem;
border-radius: var(--border-radius);
backdrop-filter: blur(10px);
}
.logo {
display: flex;
align-items: center;
gap: 0.6rem;
color: var(--text-primary);
text-decoration: none;
}
.logo-icon {
width: 2rem;
height: 2rem;
background: var(--primary-gradient);
border-radius: var(--border-radius-sm);
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
color: white;
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;
-webkit-text-fill-color: transparent;
}
.nav-section {
margin-bottom: 1rem;
}
.nav-title {
padding: 0 1rem 0.4rem;
font-size: var(--font-size-xs);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-secondary);
}
.nav-menu {
list-style: none;
}
.nav-item {
margin: 0.15rem 0.5rem;
}
.nav-link {
display: flex;
align-items: center;
padding: 0.6rem 0.8rem;
color: var(--text-primary);
text-decoration: none;
border-radius: var(--border-radius-sm);
transition: var(--transition-smooth);
font-weight: 500;
font-size: var(--font-size-sm);
cursor: pointer;
border: 1px solid transparent;
}
.nav-link:hover {
color: var(--text-primary);
transform: translateX(-2px);
border-color: rgba(59, 130, 246, 0.15);
background: rgba(59, 130, 246, 0.05);
}
.nav-link.active {
background: var(--primary-gradient);
color: var(--text-light);
box-shadow: var(--shadow-md);
}
.nav-icon {
margin-left: 0.6rem;
width: 1rem;
text-align: center;
font-size: 0.9rem;
}
.nav-badge {
background: var(--danger-gradient);
color: white;
padding: 0.15rem 0.4rem;
border-radius: 10px;
font-size: var(--font-size-xs);
font-weight: 600;
margin-right: auto;
min-width: 1.2rem;
text-align: center;
}
/* محتوای اصلی */
.main-content {
flex: 1;
margin-right: var(--sidebar-width);
padding: 1rem;
min-height: 100vh;
width: calc(100% - var(--sidebar-width));
}
/* هدر کامپکت - منطبق با index */
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.2rem;
padding: 0.8rem 0;
}
.dashboard-title {
font-size: var(--font-size-2xl);
font-weight: 800;
background: var(--primary-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: center;
gap: 0.6rem;
}
.header-actions {
display: flex;
align-items: center;
gap: 0.8rem;
}
/* آمار کلی - منطبق با index */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
margin-bottom: 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-height: 130px;
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 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.purple::before { background: var(--purple-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-bottom: 0.8rem;
}
.stat-icon {
width: 2.2rem;
height: 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-icon.purple { background: var(--purple-gradient); color: white; }
.stat-content {
flex: 1;
}
.stat-title {
font-size: var(--font-size-xs);
color: var(--text-secondary);
font-weight: 600;
margin-bottom: 0.3rem;
}
.stat-value {
font-size: var(--font-size-xl);
font-weight: 800;
color: var(--text-primary);
line-height: 1;
margin-bottom: 0.3rem;
}
.stat-value.small {
font-size: var(--font-size-base);
}
.stat-extra {
font-size: var(--font-size-xs);
color: var(--text-muted);
margin-bottom: 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; }
/* کارت‌های عملیات اصلی */
.control-section {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.control-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);
}
.control-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
}
.control-header {
display: flex;
justify-content: between;
align-items: center;
margin-bottom: 1.2rem;
}
.control-title {
font-size: var(--font-size-lg);
font-weight: 700;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 0.6rem;
}
/* مراجع آماده */
.sources-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
max-height: 350px;
overflow-y: auto;
padding-left: 0.5rem;
}
.source-item {
background: var(--glass-bg);
border: 2px solid var(--glass-border);
border-radius: var(--border-radius-sm);
padding: 1rem;
cursor: pointer;
transition: var(--transition-smooth);
position: relative;
}
.source-item:hover {
border-color: rgba(59, 130, 246, 0.3);
background: rgba(59, 130, 246, 0.03);
transform: translateY(-2px);
box-shadow: var(--shadow-sm);
}
.source-item.selected {
border-color: #3b82f6;
background: rgba(59, 130, 246, 0.05);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.source-checkbox {
position: absolute;
top: 0.8rem;
left: 0.8rem;
width: 1.2rem;
height: 1.2rem;
cursor: pointer;
}
.source-info {
margin-right: 2rem;
}
.source-name {
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.3rem;
font-size: var(--font-size-sm);
}
.source-url {
font-size: var(--font-size-xs);
color: var(--text-muted);
margin-bottom: 0.5rem;
direction: ltr;
text-align: left;
}
.source-meta {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.source-tag {
background: rgba(59, 130, 246, 0.1);
color: #3b82f6;
padding: 0.15rem 0.5rem;
border-radius: 10px;
font-size: var(--font-size-xs);
font-weight: 500;
}
.source-status {
width: 8px;
height: 8px;
border-radius: 50%;
margin-top: 0.4rem;
position: absolute;
top: 0.8rem;
right: 0.8rem;
}
.source-status.online {
background: #10b981;
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
}
.source-status.offline {
background: #ef4444;
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2);
}
/* دکمه‌های عمل - منطبق با index */
.action-buttons {
display: flex;
gap: 0.8rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
}
.btn {
padding: 0.6rem 1.2rem;
border: none;
border-radius: var(--border-radius-sm);
font-family: inherit;
font-weight: 600;
cursor: pointer;
transition: var(--transition-smooth);
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
font-size: var(--font-size-sm);
min-width: 120px;
justify-content: center;
}
.btn-primary {
background: var(--primary-gradient);
color: white;
box-shadow: var(--shadow-sm);
}
.btn-primary:hover {
box-shadow: var(--shadow-md), var(--shadow-glow-primary);
transform: translateY(-2px);
}
.btn-success {
background: var(--success-gradient);
color: white;
}
.btn-success:hover {
box-shadow: var(--shadow-md), var(--shadow-glow-success);
transform: translateY(-2px);
}
.btn-warning {
background: var(--warning-gradient);
color: white;
}
.btn-warning:hover {
box-shadow: var(--shadow-md), var(--shadow-glow-warning);
transform: translateY(-2px);
}
.btn-secondary {
background: var(--glass-bg);
color: var(--text-primary);
border: 1px solid var(--glass-border);
}
.btn-secondary:hover {
background: rgba(59, 130, 246, 0.05);
border-color: rgba(59, 130, 246, 0.3);
transform: translateY(-2px);
}
.btn-auto {
background: var(--purple-gradient);
color: white;
font-size: var(--font-size-base);
padding: 0.8rem 1.5rem;
position: relative;
overflow: hidden;
animation: pulse-auto 3s ease-in-out infinite;
}
@keyframes pulse-auto {
0%, 100% { box-shadow: var(--shadow-sm); }
50% { box-shadow: var(--shadow-lg), var(--shadow-glow-purple); }
}
.btn-auto:hover {
box-shadow: var(--shadow-xl), var(--shadow-glow-purple);
transform: translateY(-3px) scale(1.02);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none !important;
box-shadow: var(--shadow-sm) !important;
}
.btn-sm {
padding: 0.4rem 0.8rem;
font-size: var(--font-size-xs);
min-width: auto;
}
/* تنظیمات سریع */
.quick-settings {
background: rgba(59, 130, 246, 0.03);
border: 1px solid rgba(59, 130, 246, 0.1);
border-radius: var(--border-radius-sm);
padding: 1rem;
margin-top: 1rem;
}
.settings-title {
font-size: var(--font-size-sm);
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.8rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.settings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.setting-item label {
display: block;
font-size: var(--font-size-xs);
font-weight: 600;
color: var(--text-secondary);
margin-bottom: 0.4rem;
}
.setting-item select {
width: 100%;
padding: 0.5rem;
border: 1px solid var(--glass-border);
border-radius: var(--border-radius-sm);
background: var(--glass-bg);
color: var(--text-primary);
font-family: inherit;
font-size: var(--font-size-xs);
}
/* وضعیت فعالیت */
.activity-status {
background: var(--card-bg);
border-radius: var(--border-radius);
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: var(--shadow-md);
border: 1px solid rgba(255, 255, 255, 0.3);
display: none;
}
.activity-status.show {
display: block;
}
.status-content {
display: flex;
align-items: center;
gap: 1rem;
}
.status-icon {
width: 3rem;
height: 3rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
flex-shrink: 0;
}
.status-loading {
background: rgba(59, 130, 246, 0.1);
color: #3b82f6;
border: 2px solid rgba(59, 130, 246, 0.2);
}
.status-success {
background: rgba(16, 185, 129, 0.1);
color: #10b981;
border: 2px solid rgba(16, 185, 129, 0.2);
}
.status-error {
background: rgba(239, 68, 68, 0.1);
color: #ef4444;
border: 2px solid rgba(239, 68, 68, 0.2);
}
.status-details {
flex: 1;
}
.status-title {
font-size: var(--font-size-lg);
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.3rem;
}
.status-message {
font-size: var(--font-size-sm);
color: var(--text-secondary);
margin-bottom: 0.8rem;
}
.status-progress {
width: 100%;
height: 6px;
background: rgba(0, 0, 0, 0.08);
border-radius: 3px;
overflow: hidden;
}
.status-progress-bar {
height: 100%;
background: var(--primary-gradient);
border-radius: 3px;
transition: width 0.3s ease;
position: relative;
}
.status-progress-bar::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
/* نتایج */
.results-section {
background: var(--card-bg);
border-radius: var(--border-radius);
box-shadow: var(--shadow-md);
margin-bottom: 1.5rem;
border: 1px solid rgba(255, 255, 255, 0.3);
overflow: hidden;
display: none;
}
.results-section.show {
display: block;
}
.results-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
background: linear-gradient(135deg, rgba(16, 185, 129, 0.02), rgba(255, 255, 255, 0.05));
}
.results-title {
font-size: var(--font-size-lg);
font-weight: 700;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 0.6rem;
}
.results-actions {
display: flex;
gap: 0.5rem;
}
.results-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
padding: 1.5rem;
}
.result-card {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: var(--border-radius-sm);
padding: 1.5rem;
transition: var(--transition-smooth);
}
.result-card:hover {
border-color: rgba(16, 185, 129, 0.3);
transform: translateY(-2px);
box-shadow: var(--shadow-sm);
}
.result-source {
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.5rem;
font-size: var(--font-size-sm);
}
.result-meta {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
font-size: var(--font-size-xs);
color: var(--text-muted);
flex-wrap: wrap;
}
.result-meta span {
display: flex;
align-items: center;
gap: 0.3rem;
}
.result-content {
font-size: var(--font-size-sm);
color: var(--text-secondary);
line-height: 1.5;
max-height: 100px;
overflow: hidden;
margin-bottom: 1rem;
}
.result-actions {
display: flex;
gap: 0.5rem;
padding-top: 1rem;
border-top: 1px solid rgba(0, 0, 0, 0.05);
}
/* Toast Notifications - منطبق با index */
.toast-container {
position: fixed;
top: 1rem;
left: 1rem;
z-index: 10001;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.toast {
background: var(--card-bg);
border-radius: var(--border-radius-sm);
padding: 1rem 1.5rem;
box-shadow: var(--shadow-lg);
border-left: 4px solid;
display: flex;
align-items: center;
gap: 0.8rem;
min-width: 300px;
transform: translateX(-100%);
transition: all 0.3s ease;
}
.toast.show {
transform: translateX(0);
}
.toast.success { border-left-color: #10b981; }
.toast.error { border-left-color: #ef4444; }
.toast.warning { border-left-color: #f59e0b; }
.toast.info { border-left-color: #3b82f6; }
.toast-icon {
font-size: 1.2rem;
}
.toast.success .toast-icon { color: #10b981; }
.toast.error .toast-icon { color: #ef4444; }
.toast.warning .toast-icon { color: #f59e0b; }
.toast.info .toast-icon { color: #3b82f6; }
.toast-content {
flex: 1;
}
.toast-title {
font-weight: 600;
font-size: var(--font-size-sm);
margin-bottom: 0.2rem;
}
.toast-message {
font-size: var(--font-size-xs);
color: var(--text-secondary);
}
.toast-close {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
font-size: 1rem;
transition: var(--transition-fast);
}
.toast-close:hover {
color: var(--text-primary);
}
/* دکمه منوی موبایل */
.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-width: 992px) {
.mobile-menu-toggle {
display: block;
}
.sidebar {
transform: translateX(100%);
position: fixed;
z-index: 10000;
}
.sidebar.open {
transform: translateX(0);
}
.main-content {
margin-right: 0;
width: 100%;
padding: 1rem;
}
.dashboard-header {
flex-direction: column;
align-items: flex-start;
gap: 0.8rem;
}
.header-actions {
width: 100%;
justify-content: space-between;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
.control-section {
grid-template-columns: 1fr;
}
.sources-grid {
grid-template-columns: 1fr;
}
.settings-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.main-content {
padding: 0.8rem;
}
.stats-grid {
grid-template-columns: 1fr;
gap: 0.6rem;
}
.stat-card {
min-height: 100px;
padding: 0.8rem;
}
.stat-value {
font-size: var(--font-size-lg);
}
.action-buttons {
flex-direction: column;
}
.btn {
justify-content: center;
width: 100%;
}
.results-grid {
grid-template-columns: 1fr;
}
}
</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-spider"></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">
<i class="fas fa-chart-pie nav-icon"></i>
<span>نمای کلی</span>
</a>
</li>
<li class="nav-item">
<a href="scraping_dashboard.html" class="nav-link active">
<i class="fas fa-spider 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">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="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="#" class="nav-link">
<i class="fas fa-sign-out-alt nav-icon"></i>
<span>خروج</span>
</a>
</li>
</ul>
</div>
</nav>
</aside>
<!-- محتوای اصلی -->
<main class="main-content">
<!-- هدر صفحه -->
<header class="dashboard-header">
<div>
<h1 class="dashboard-title">
<i class="fas fa-spider"></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>
</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="totalSources">10</div>
<div class="stat-extra">مراجع آماده</div>
<div class="stat-change positive">
<i class="fas fa-arrow-up"></i>
<span>+100%</span>
</div>
</div>
<div class="stat-icon primary">
<i class="fas fa-globe"></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="selectedSources">0</div>
<div class="stat-extra">آماده اسکراپینگ</div>
<div class="stat-change positive">
<i class="fas fa-arrow-up"></i>
<span>+0%</span>
</div>
</div>
<div class="stat-icon success">
<i class="fas fa-check-circle"></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="scrapedItems">0</div>
<div class="stat-extra">آیتم استخراج شده</div>
<div class="stat-change positive">
<i class="fas fa-arrow-up"></i>
<span>+0%</span>
</div>
</div>
<div class="stat-icon warning">
<i class="fas fa-download"></i>
</div>
</div>
</div>
<div class="stat-card purple">
<div class="stat-header">
<div class="stat-content">
<div class="stat-title">آخرین بروزرسانی</div>
<div class="stat-value small" id="lastUpdate">--</div>
<div class="stat-extra">زمان آخرین فعالیت</div>
<div class="stat-change positive">
<i class="fas fa-clock"></i>
<span>جدید</span>
</div>
</div>
<div class="stat-icon purple">
<i class="fas fa-clock"></i>
</div>
</div>
</div>
</div>
</section>
<!-- کنترل عملیات -->
<section class="control-section">
<!-- مراجع آماده -->
<div class="control-card">
<div class="control-header">
<h2 class="control-title">
<i class="fas fa-list"></i>
مراجع حقوقی آماده
</h2>
</div>
<div class="sources-grid" id="sourcesGrid">
<!-- مراجع اینجا لود می‌شوند -->
</div>
</div>
<!-- کنترل عملیات -->
<div class="control-card">
<div class="control-header">
<h2 class="control-title">
<i class="fas fa-cogs"></i>
کنترل عملیات
</h2>
</div>
<div class="action-buttons">
<button type="button" class="btn btn-auto" onclick="startAutoScraping()" id="autoScrapingBtn">
<i class="fas fa-magic"></i>
اسکراپینگ خودکار
</button>
<button type="button" class="btn btn-primary" onclick="startSelectedScraping()" id="selectedScrapingBtn">
<i class="fas fa-play"></i>
اسکراپینگ انتخابی
</button>
<button type="button" class="btn btn-warning" onclick="stopScraping()" id="stopScrapingBtn" disabled>
<i class="fas fa-stop"></i>
توقف
</button>
<button type="button" class="btn btn-secondary" onclick="refreshData()">
<i class="fas fa-sync-alt"></i>
بروزرسانی
</button>
</div>
<div class="action-buttons">
<button type="button" class="btn btn-secondary" onclick="selectAllSources()">
<i class="fas fa-check-double"></i>
انتخاب همه
</button>
<button type="button" class="btn btn-secondary" onclick="clearAllSources()">
<i class="fas fa-times"></i>
پاک کردن انتخاب
</button>
<button type="button" class="btn btn-secondary" onclick="checkSourcesStatus()">
<i class="fas fa-wifi"></i>
بررسی وضعیت
</button>
</div>
<div class="quick-settings">
<h4 class="settings-title">
<i class="fas fa-sliders-h"></i>
تنظیمات سریع
</h4>
<div class="settings-grid">
<div class="setting-item">
<label>عمق اسکراپینگ</label>
<select id="scrapingDepth">
<option value="1">سطح ۱ (صفحه اصلی)</option>
<option value="2" selected>سطح ۲ (یک سطح عمق)</option>
<option value="3">سطح ۳ (دو سطح عمق)</option>
</select>
</div>
<div class="setting-item">
<label>تاخیر بین درخواست‌ها</label>
<select id="scrapingDelay">
<option value="1">۱ ثانیه</option>
<option value="2" selected>۲ ثانیه</option>
<option value="3">۳ ثانیه</option>
<option value="5">۵ ثانیه</option>
</select>
</div>
</div>
</div>
</div>
</section>
<!-- وضعیت فعالیت -->
<div class="activity-status" id="activityStatus">
<div class="status-content">
<div class="status-icon status-loading" id="statusIcon">
<i class="fas fa-spinner fa-spin"></i>
</div>
<div class="status-details">
<div class="status-title" id="statusTitle">در حال اسکراپینگ...</div>
<div class="status-message" id="statusMessage">در حال اتصال به مراجع...</div>
<div class="status-progress">
<div class="status-progress-bar" id="statusProgressBar"></div>
</div>
</div>
</div>
</div>
<!-- نتایج -->
<div class="results-section" id="resultsSection">
<div class="results-header">
<h3 class="results-title">
<i class="fas fa-check-circle"></i>
نتایج اسکراپینگ
</h3>
<div class="results-actions">
<button type="button" class="btn btn-success btn-sm" onclick="exportResults()">
<i class="fas fa-download"></i>
دانلود JSON
</button>
<button type="button" class="btn btn-primary btn-sm" onclick="saveToDatabase()">
<i class="fas fa-save"></i>
ذخیره
</button>
</div>
</div>
<div class="results-grid" id="resultsGrid">
<!-- نتایج اینجا نمایش داده می‌شوند -->
</div>
</div>
</main>
</div>
<!-- Toast Container -->
<div class="toast-container" id="toastContainer"></div>
<script>
// داده‌های مراجع حقوقی
const legalSources = [
{
id: 'divan',
name: 'دیوان عدالت اداری',
url: 'https://www.divan.gov.ir',
category: 'قضایی',
tags: ['دیوان', 'عدالت اداری', 'آرا'],
status: 'online',
priority: 1
},
{
id: 'dadiran',
name: 'پورتال ملی خدمات قضایی',
url: 'https://www.dadiran.ir',
category: 'خدمات قضایی',
tags: ['دادگستری', 'خدمات', 'پورتال ملی'],
status: 'online',
priority: 1
},
{
id: 'judiciary',
name: 'قوه قضائیه',
url: 'https://www.judiciary.ir',
category: 'قضایی',
tags: ['قوه قضائیه', 'اخبار', 'اطلاعیه'],
status: 'online',
priority: 1
},
{
id: 'lawcenter',
name: 'مرکز مطالعات و تحقیقات قوانین',
url: 'https://www.lawcenter.gov.ir',
category: 'تحقیقات',
tags: ['قوانین', 'تحقیقات', 'مطالعات'],
status: 'online',
priority: 2
},
{
id: 'mohaseabat',
name: 'دیوان محاسبات کشور',
url: 'https://www.mohaseabat.gov.ir',
category: 'مالی',
tags: ['محاسبات', 'مالی', 'گزارش'],
status: 'online',
priority: 2
},
{
id: 'majlis',
name: 'مجلس شورای اسلامی',
url: 'https://www.majlis.ir',
category: 'قانونگذاری',
tags: ['مجلس', 'قوانین', 'لوایح'],
status: 'online',
priority: 1
},
{
id: 'shora-negahban',
name: 'شورای نگهبان',
url: 'https://www.shora-rc.ir',
category: 'نظارتی',
tags: ['شورای نگهبان', 'نظارت', 'تایید'],
status: 'online',
priority: 2
},
{
id: 'maslahat',
name: 'مجمع تشخیص مصلحت نظام',
url: 'https://www.maslahat.ir',
category: 'مشاوره‌ای',
tags: ['مجمع', 'مصلحت', 'مشاوره'],
status: 'online',
priority: 2
},
{
id: 'dadgostary-tehran',
name: 'دادگستری استان تهران',
url: 'https://www.tehran-justice.ir',
category: 'قضایی محلی',
tags: ['دادگستری', 'تهران', 'محلی'],
status: 'online',
priority: 3
},
{
id: 'kanoon-vokala',
name: 'کانون وکلای دادگستری',
url: 'https://www.iranbar.org',
category: 'صنفی',
tags: ['وکلا', 'کانون', 'صنفی'],
status: 'online',
priority: 3
}
];
// متغیرهای عمومی
let selectedSources = [];
let scrapingActive = false;
let scrapingResults = [];
let scrapingProgress = 0;
// شروع برنامه
document.addEventListener('DOMContentLoaded', function() {
initializeDashboard();
loadSources();
updateStatistics();
setupEventListeners();
showToast('سامانه اسکراپینگ حقوقی آماده است', 'success', 'آماده');
});
// مقداردهی اولیه
function initializeDashboard() {
console.log('🕷️ Legal Scraping Dashboard Initializing...');
// شبیه‌سازی داده‌های موجود
const mockData = {
scrapedItems: Math.floor(Math.random() * 1000) + 500,
lastUpdate: new Date().toLocaleDateString('fa-IR')
};
document.getElementById('scrapedItems').textContent = mockData.scrapedItems;
document.getElementById('lastUpdate').textContent = mockData.lastUpdate;
}
// 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');
}
});
}
}
// بارگذاری مراجع
function loadSources() {
const sourcesGrid = document.getElementById('sourcesGrid');
let html = '';
legalSources.forEach(source => {
html += `
<div class="source-item" onclick="toggleSource('${source.id}')" id="source-${source.id}">
<input type="checkbox" class="source-checkbox" id="checkbox-${source.id}" onchange="toggleSource('${source.id}')">
<div class="source-status ${source.status}"></div>
<div class="source-info">
<div class="source-name">${source.name}</div>
<div class="source-url">${source.url}</div>
<div class="source-meta">
<span class="source-tag">${source.category}</span>
${source.tags.slice(0, 2).map(tag => `<span class="source-tag">${tag}</span>`).join('')}
</div>
</div>
</div>
`;
});
sourcesGrid.innerHTML = html;
}
// تغییر وضعیت انتخاب مرجع
function toggleSource(sourceId) {
const sourceElement = document.getElementById(`source-${sourceId}`);
const checkbox = document.getElementById(`checkbox-${sourceId}`);
if (selectedSources.includes(sourceId)) {
selectedSources = selectedSources.filter(id => id !== sourceId);
sourceElement.classList.remove('selected');
checkbox.checked = false;
} else {
selectedSources.push(sourceId);
sourceElement.classList.add('selected');
checkbox.checked = true;
}
updateStatistics();
}
// انتخاب همه مراجع
function selectAllSources() {
selectedSources = legalSources.map(source => source.id);
legalSources.forEach(source => {
const sourceElement = document.getElementById(`source-${source.id}`);
const checkbox = document.getElementById(`checkbox-${source.id}`);
sourceElement.classList.add('selected');
checkbox.checked = true;
});
updateStatistics();
showToast('همه مراجع انتخاب شدند', 'success', 'انتخاب');
}
// پاک کردن انتخاب همه مراجع
function clearAllSources() {
selectedSources = [];
legalSources.forEach(source => {
const sourceElement = document.getElementById(`source-${source.id}`);
const checkbox = document.getElementById(`checkbox-${source.id}`);
sourceElement.classList.remove('selected');
checkbox.checked = false;
});
updateStatistics();
showToast('انتخاب همه مراجع پاک شد', 'info', 'پاک سازی');
}
// بروزرسانی آمار
function updateStatistics() {
document.getElementById('selectedSources').textContent = selectedSources.length;
}
// شروع اسکراپینگ خودکار
function startAutoScraping() {
if (scrapingActive) {
showToast('اسکراپینگ در حال انجام است', 'warning', 'هشدار');
return;
}
// انتخاب خودکار مراجع بر اساس اولویت
const autoSelectedSources = legalSources
.filter(source => source.priority <= 2)
.map(source => source.id);
if (autoSelectedSources.length === 0) {
showToast('هیچ مرجع مناسبی برای اسکراپینگ خودکار یافت نشد', 'error', 'خطا');
return;
}
// بروزرسانی انتخاب‌ها
clearAllSources();
autoSelectedSources.forEach(sourceId => {
toggleSource(sourceId);
});
showToast(`${autoSelectedSources.length} مرجع به صورت خودکار انتخاب شد`, 'info', 'انتخاب خودکار');
setTimeout(() => {
startScraping(autoSelectedSources);
}, 1000);
}
// شروع اسکراپینگ مراجع انتخابی
function startSelectedScraping() {
if (scrapingActive) {
showToast('اسکراپینگ در حال انجام است', 'warning', 'هشدار');
return;
}
if (selectedSources.length === 0) {
showToast('لطفاً حداقل یک مرجع انتخاب کنید', 'warning', 'هشدار');
return;
}
startScraping(selectedSources);
}
// شروع فرآیند اسکراپینگ
function startScraping(sources) {
scrapingActive = true;
scrapingResults = [];
scrapingProgress = 0;
// بروزرسانی UI
document.getElementById('autoScrapingBtn').disabled = true;
document.getElementById('selectedScrapingBtn').disabled = true;
document.getElementById('stopScrapingBtn').disabled = false;
// نمایش وضعیت
const activityStatus = document.getElementById('activityStatus');
activityStatus.classList.add('show');
updateScrapingStatus('loading', 'شروع اسکراپینگ...', 'در حال آماده‌سازی...', 0);
showToast(`شروع اسکراپینگ ${sources.length} مرجع`, 'info', 'شروع');
// شبیه‌سازی فرآیند اسکراپینگ
performScraping(sources);
}
// انجام اسکراپینگ (شبیه‌سازی)
async function performScraping(sources) {
const totalSteps = sources.length * 5; // 5 مرحله برای هر مرجع
let currentStep = 0;
for (let i = 0; i < sources.length; i++) {
const sourceId = sources[i];
const source = legalSources.find(s => s.id === sourceId);
if (!source) continue;
// مراحل اسکراپینگ برای هر مرجع
const steps = [
{ title: `اتصال به ${source.name}`, message: 'در حال برقراری ارتباط...' },
{ title: `دریافت صفحه ${source.name}`, message: 'در حال دانلود HTML...' },
{ title: `تجزیه محتوا ${source.name}`, message: 'در حال استخراج اطلاعات...' },
{ title: `پردازش داده‌ها ${source.name}`, message: 'در حال تمیز کردن متن...' },
{ title: `ذخیره نتایج ${source.name}`, message: 'در حال ذخیره در پایگاه داده...' }
];
for (let j = 0; j < steps.length; j++) {
currentStep++;
const progress = (currentStep / totalSteps) * 100;
updateScrapingStatus('loading', steps[j].title, steps[j].message, progress);
// شبیه‌سازی تاخیر
await new Promise(resolve => setTimeout(resolve, 800));
if (!scrapingActive) {
return; // متوقف شده
}
}
// تولید نتیجه تصادفی برای این مرجع
const result = generateMockResult(source);
scrapingResults.push(result);
}
// تکمیل اسکراپینگ
completeScrapingSuccess();
}
// تولید نتیجه تصادفی
function generateMockResult(source) {
const sampleContents = [
'قانون جدید در زمینه حقوق شهروندی تصویب شد...',
'دیوان عدالت اداری رای مهمی در خصوص اختیارات دولتی صادر کرد...',
'آخرین تغییرات در قوانین مدنی و تجارت اعلام شد...',
'گزارش جدید از عملکرد دستگاه‌های قضایی منتشر شد...',
'رویه‌های جدید برای تسریع در روند قضایی ابلاغ شد...'
];
const randomContent = sampleContents[Math.floor(Math.random() * sampleContents.length)];
return {
source: source.name,
url: source.url,
title: `آخرین اطلاعات از ${source.name}`,
content: randomContent,
wordCount: Math.floor(Math.random() * 500) + 100,
scrapedAt: new Date().toISOString(),
links: Math.floor(Math.random() * 10) + 5,
status: 'success'
};
}
// تکمیل موفق اسکراپینگ
function completeScrapingSuccess() {
updateScrapingStatus('success', 'اسکراپینگ تکمیل شد', `${scrapingResults.length} مرجع با موفقیت پردازش شد`, 100);
setTimeout(() => {
document.getElementById('activityStatus').classList.remove('show');
displayResults();
resetScrapingState();
showToast(`اسکراپینگ تکمیل شد - ${scrapingResults.length} نتیجه`, 'success', 'تکمیل');
// بروزرسانی آمار
const currentScraped = parseInt(document.getElementById('scrapedItems').textContent);
document.getElementById('scrapedItems').textContent = currentScraped + scrapingResults.length;
document.getElementById('lastUpdate').textContent = new Date().toLocaleDateString('fa-IR');
}, 2000);
}
// نمایش نتایج
function displayResults() {
const resultsSection = document.getElementById('resultsSection');
const resultsGrid = document.getElementById('resultsGrid');
let html = '';
scrapingResults.forEach((result, index) => {
html += `
<div class="result-card">
<div class="result-source">${result.source}</div>
<div class="result-meta">
<span><i class="fas fa-file-text"></i> ${result.wordCount} کلمه</span>
<span><i class="fas fa-link"></i> ${result.links} لینک</span>
<span><i class="fas fa-clock"></i> ${new Date(result.scrapedAt).toLocaleTimeString('fa-IR')}</span>
</div>
<div class="result-content">${result.content}</div>
<div class="result-actions">
<button class="btn btn-primary btn-sm" onclick="viewFullResult(${index})">
<i class="fas fa-eye"></i>
مشاهده کامل
</button>
<button class="btn btn-success btn-sm" onclick="saveResult(${index})">
<i class="fas fa-save"></i>
ذخیره
</button>
</div>
</div>
`;
});
resultsGrid.innerHTML = html;
resultsSection.classList.add('show');
}
// بروزرسانی وضعیت اسکراپینگ
function updateScrapingStatus(type, title, message, progress) {
const statusIcon = document.getElementById('statusIcon');
const statusTitle = document.getElementById('statusTitle');
const statusMessage = document.getElementById('statusMessage');
const progressBar = document.getElementById('statusProgressBar');
statusIcon.className = `status-icon status-${type}`;
statusTitle.textContent = title;
statusMessage.textContent = message;
progressBar.style.width = `${progress}%`;
const icons = {
loading: 'spinner fa-spin',
success: 'check-circle',
error: 'exclamation-triangle'
};
statusIcon.innerHTML = `<i class="fas fa-${icons[type]}"></i>`;
}
// توقف اسکراپینگ
function stopScraping() {
if (!scrapingActive) return;
scrapingActive = false;
updateScrapingStatus('error', 'اسکراپینگ متوقف شد', 'فرآیند توسط کاربر متوقف شد', scrapingProgress);
setTimeout(() => {
document.getElementById('activityStatus').classList.remove('show');
resetScrapingState();
}, 2000);
showToast('اسکراپینگ متوقف شد', 'warning', 'توقف');
}
// بازنشانی وضعیت اسکراپینگ
function resetScrapingState() {
scrapingActive = false;
scrapingProgress = 0;
document.getElementById('autoScrapingBtn').disabled = false;
document.getElementById('selectedScrapingBtn').disabled = false;
document.getElementById('stopScrapingBtn').disabled = true;
}
// بررسی وضعیت مراجع
function checkSourcesStatus() {
showToast('در حال بررسی وضعیت مراجع...', 'info', 'بررسی');
// شبیه‌سازی بررسی
setTimeout(() => {
const onlineCount = legalSources.filter(s => s.status === 'online').length;
showToast(`${onlineCount} از ${legalSources.length} مرجع آنلاین هستند`, 'success', 'وضعیت مراجع');
}, 1500);
}
// بروزرسانی داده‌ها
function refreshData() {
showToast('در حال بروزرسانی...', 'info', 'بروزرسانی');
setTimeout(() => {
updateStatistics();
showToast('داده‌ها بروزرسانی شد', 'success', 'بروزرسانی');
}, 1000);
}
// صادرات نتایج
function exportResults() {
if (scrapingResults.length === 0) {
showToast('نتیجه‌ای برای صادرات وجود ندارد', 'warning', 'هشدار');
return;
}
const dataStr = JSON.stringify(scrapingResults, null, 2);
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
const exportFileDefaultName = `legal-scraping-results-${new Date().toISOString().split('T')[0]}.json`;
const linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
showToast('فایل JSON آماده دانلود است', 'success', 'صادرات');
}
// ذخیره در پایگاه داده
function saveToDatabase() {
if (scrapingResults.length === 0) {
showToast('نتیجه‌ای برای ذخیره وجود ندارد', 'warning', 'هشدار');
return;
}
// شبیه‌سازی ذخیره
showToast('در حال ذخیره در پایگاه داده...', 'info', 'ذخیره');
setTimeout(() => {
showToast(`${scrapingResults.length} نتیجه در پایگاه داده ذخیره شد`, 'success', 'ذخیره موفق');
}, 2000);
}
// مشاهده نتیجه کامل
function viewFullResult(index) {
const result = scrapingResults[index];
if (!result) return;
showToast(`مشاهده نتیجه کامل: ${result.source}`, 'info', 'نمایش');
}
// ذخیره نتیجه منفرد
function saveResult(index) {
const result = scrapingResults[index];
if (!result) return;
showToast(`نتیجه ${result.source} ذخیره شد`, 'success', 'ذخیره');
}
// نمایش Toast
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);
setTimeout(() => toast.classList.add('show'), 100);
setTimeout(() => {
if (toast.parentElement) {
toast.classList.remove('show');
setTimeout(() => {
if (toast.parentElement) {
toast.remove();
}
}, 300);
}
}, 5000);
}
console.log('🕷️ Legal Scraping Dashboard Ready!');
</script>
</body>
</html>