Really-amin commited on
Commit
9a8b6da
·
verified ·
1 Parent(s): db2e8d3

Upload 2 files

Browse files
Files changed (2) hide show
  1. admin_dashboard_filemanager.py +123 -342
  2. admin_dashboard_sub.py +218 -0
admin_dashboard_filemanager.py CHANGED
@@ -1,364 +1,145 @@
1
- import gradio as gr
2
- import json
3
- import os
4
- import shutil
5
  from pathlib import Path
6
  from datetime import datetime
 
7
  import zipfile
 
 
8
 
9
- # CSS سفارشی با طراحی گلس مورفیسم و نئومورفیسم
10
- CUSTOM_CSS = """
11
- .file-manager {
12
- border-radius: 20px;
13
- box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.15), -4px -4px 10px rgba(255, 255, 255, 0.7);
14
- backdrop-filter: blur(10px);
15
- background-color: rgba(255, 255, 255, 0.2);
16
- padding: 20px;
17
- }
18
-
19
- .custom-header {
20
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
21
- color: white !important;
22
- padding: 20px !important;
23
- border-radius: 20px;
24
- margin: -20px -20px 20px -20px !important;
25
- }
26
 
27
- .file-operations {
28
- display: flex;
29
- gap: 10px;
30
- margin: 15px 0;
31
- background: rgba(240, 248, 255, 0.7);
32
- padding: 15px;
33
- border-radius: 20px;
34
- box-shadow: inset 4px 4px 6px rgba(0, 0, 0, 0.1), inset -4px -4px 6px rgba(255, 255, 255, 0.5);
35
  }
36
-
37
- .file-preview {
38
- border-radius: 20px;
39
- margin-top: 15px;
40
  padding: 20px;
41
- box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.1), -4px -4px 8px rgba(255, 255, 255, 0.5);
42
- backdrop-filter: blur(5px);
43
- background: rgba(255, 255, 255, 0.7);
44
- }
45
-
46
- .breadcrumb {
47
- background: rgba(241, 245, 249, 0.9);
48
- padding: 12px 20px;
49
- border-radius: 20px;
50
- margin-bottom: 20px;
51
- display: flex;
52
- align-items: center;
53
- gap: 10px;
54
  }
55
-
56
- .search-box {
57
- margin: 10px 0;
58
- padding: 12px;
59
  border-radius: 20px;
60
- border: 2px solid rgba(102, 126, 234, 0.5);
61
- width: 100%;
62
- backdrop-filter: blur(5px);
63
  transition: all 0.3s ease;
64
  }
65
-
66
- .search-box:focus {
67
- border-color: #667eea;
68
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.3);
69
- }
70
-
71
- .file-item, .folder-item {
72
- transition: all 0.3s ease;
73
- padding: 12px 15px;
74
- margin: 5px 0;
75
- border-radius: 20px;
76
- display: flex;
77
- justify-content: space-between;
78
- align-items: center;
79
  cursor: pointer;
80
- backdrop-filter: blur(5px);
81
- box-shadow: inset 3px 3px 5px rgba(0, 0, 0, 0.1), inset -3px -3px 5px rgba(255, 255, 255, 0.5);
82
- }
83
-
84
- .file-item:hover, .folder-item:hover {
85
- background-color: rgba(255, 255, 255, 0.8);
86
- transform: translateX(5px);
87
- }
88
-
89
- .status-message {
90
- margin-top: 15px;
91
- padding: 12px;
92
  border-radius: 20px;
93
- animation: slideIn 0.3s ease;
94
- background: rgba(255, 255, 255, 0.8);
95
- }
96
-
97
- .operation-button {
98
- transition: all 0.3s ease !important;
99
- border-radius: 20px !important;
100
- padding: 8px 16px !important;
101
- box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.1), -4px -4px 6px rgba(255, 255, 255, 0.7);
102
- }
103
-
104
- .operation-button:hover {
105
- transform: translateY(-2px);
106
  }
107
-
108
- .preview-title {
109
- color: #4a5568;
110
- font-size: 1.1em;
111
- margin-bottom: 10px;
112
- border-bottom: 2px solid rgba(226, 232, 240, 0.8);
113
- padding-bottom: 8px;
114
  }
115
-
116
- .context-menu {
117
- position: absolute;
118
- background: rgba(255, 255, 255, 0.95);
119
- border-radius: 20px;
120
- box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.1), -4px -4px 6px rgba(255, 255, 255, 0.7);
121
- padding: 5px 0;
122
- z-index: 1000;
123
  }
 
124
  """
 
125
 
126
- class EnhancedFileManager:
127
  def __init__(self):
128
- self.root_path = Path('data')
129
  self.root_path.mkdir(exist_ok=True)
130
- self.clipboard = None
131
- self.history = []
132
- self.current_path = str(self.root_path)
133
-
134
- def list_files(self, search_query=""):
135
- path = Path(self.current_path)
136
- files = []
137
- folders = []
138
-
139
- try:
140
- for item in path.iterdir():
141
- if search_query and search_query.lower() not in item.name.lower():
142
- continue
143
-
144
- modified_time = datetime.fromtimestamp(item.stat().st_mtime).strftime("%Y-%m-%d %H:%M")
145
-
146
- if item.is_file():
147
- size = item.stat().st_size
148
- size_str = f"{size/1024/1024:.2f} MB" if size > 1024*1024 else f"{size/1024:.1f} KB"
149
- icon = self._get_file_icon(item.suffix.lower())
150
-
151
- files.append({
152
- "icon": icon,
153
- "name": item.name,
154
- "type": "file",
155
- "path": str(item),
156
- "size": size_str,
157
- "modified": modified_time
158
- })
159
- else:
160
- items_count = len(list(item.iterdir()))
161
- folders.append({
162
- "icon": "📁",
163
- "name": item.name,
164
- "type": "folder",
165
- "path": str(item),
166
- "modified": modified_time,
167
- "items_count": items_count
168
- })
169
-
170
- return {
171
- "folders": sorted(folders, key=lambda x: x["name"].lower()),
172
- "files": sorted(files, key=lambda x: x["name"].lower())
173
- }
174
-
175
- except Exception as e:
176
- return {"folders": [], "files": [], "error": str(e)}
177
-
178
- def _get_file_icon(self, extension):
179
- icons = {
180
- '.pdf': '📕',
181
- '.txt': '📄',
182
- '.doc': '📝',
183
- '.docx': '📝',
184
- '.xls': '📊',
185
- '.xlsx': '📊',
186
- '.jpg': '🖼️',
187
- '.jpeg': '🖼️',
188
- '.png': '🖼️',
189
- '.gif': '🖼️',
190
- '.zip': '🗜️',
191
- '.rar': '🗜️',
192
- '.mp3': '🎵',
193
- '.mp4': '🎥',
194
- '.py': '🐍',
195
- '.js': '📜',
196
- '.html': '🌐',
197
- '.css': '🎨'
198
- }
199
- return icons.get(extension, '📄')
200
-
201
- def create_folder(self, name):
202
- if not name:
203
- return False, self._create_status("لطفاً نام پوشه را وارد کنید", "error")
204
-
205
- try:
206
- new_folder = Path(self.current_path) / name
207
- if new_folder.exists():
208
- return False, self._create_status("پوشه‌ای با این نام قبلاً وجود دارد", "error")
209
-
210
- new_folder.mkdir(parents=True)
211
- return True, self._create_status("پوشه با موفقیت ایجاد شد", "success")
212
- except Exception as e:
213
- return False, self._create_status(f"خطا در ایجاد پوشه: {str(e)}", "error")
214
-
215
  def upload_file(self, file):
216
- if file is None:
217
- return False, self._create_status("لطفاً یک فایل انتخاب کنید", "error")
218
-
219
- try:
220
- dest_path = Path(self.current_path) / file.name
221
- if dest_path.exists():
222
- return False, self._create_status("فایلی با این نام قبلاً وجود دارد", "error")
223
-
224
- shutil.copy2(file.name, dest_path)
225
- return True, self._create_status("فایل با موفقیت آپلود شد", "success")
226
- except Exception as e:
227
- return False, self._create_status(f"خطا در آپلود: {str(e)}", "error")
228
-
229
- def delete_item(self, path):
230
- try:
231
- path = Path(path)
232
- if path.is_file():
233
- path.unlink()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  else:
235
- shutil.rmtree(path)
236
- return True, self._create_status("آیتم با موفقیت حذف شد", "success")
237
- except Exception as e:
238
- return False, self._create_status(f"خطا در حذف: {str(e)}", "error")
239
-
240
- def compress_items(self, items):
241
- try:
242
- if not items:
243
- return False, self._create_status("لطفاً حداقل یک آیتم انتخاب کنید", "error")
244
-
245
- archive_name = f"archive_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
246
- archive_path = Path(self.current_path) / f"{archive_name}.zip"
247
-
248
- with zipfile.ZipFile(archive_path, 'w') as zipf:
249
- for item in items:
250
- item_path = Path(item)
251
- if item_path.is_file():
252
- zipf.write(item_path, item_path.name)
253
- else:
254
- for root, dirs, files in os.walk(item_path):
255
- for file in files:
256
- file_path = Path(root) / file
257
- zipf.write(file_path, file_path.relative_to(item_path.parent))
258
-
259
- return True, self._create_status("فایل فشرده با موفقیت ایجاد شد", "success")
260
- except Exception as e:
261
- return False, self._create_status(f"خطا در فشرده‌سازی: {str(e)}", "error")
262
-
263
- def _create_status(self, message, status="info"):
264
- color_map = {
265
- "success": "#48bb78",
266
- "error": "#f56565",
267
- "info": "#4299e1",
268
- "warning": "#ecc94b"
269
- }
270
- color = color_map.get(status, color_map["info"])
271
-
272
- return f"""
273
- <div class="status-message" style="
274
- background-color: {color}10;
275
- border-right: 4px solid {color};
276
- color: {color};
277
- ">
278
- {message}
279
- </div>
280
- """
281
-
282
- def create_interface():
283
- manager = EnhancedFileManager()
284
-
285
- with gr.Blocks(css=CUSTOM_CSS) as interface:
286
- with gr.Column(elem_classes="file-manager"):
287
- gr.Markdown("# 🗂️ مدیریت پیشرفته فایل‌ها", elem_classes="custom-header")
288
-
289
- # نوار مسیر
290
- with gr.Row(elem_classes="breadcrumb"):
291
- current_path = gr.Textbox(value=manager.current_path, label="", interactive=False)
292
- home_btn = gr.Button("🏠", elem_classes="operation-button")
293
- up_btn = gr.Button("⬆️", elem_classes="operation-button")
294
- refresh_btn = gr.Button("🔄", elem_classes="operation-button")
295
-
296
- # جستجو
297
- search_box = gr.Textbox(
298
- placeholder="🔍 جستجو در فایل‌ها...",
299
- elem_classes="search-box"
300
- )
301
-
302
- # عملیات اصلی
303
- with gr.Row(elem_classes="file-operations"):
304
- upload_btn = gr.UploadButton(
305
- "📤 آپلود فایل",
306
- elem_classes="operation-button"
307
- )
308
- new_folder_btn = gr.Button(
309
- "📁 پوشه جدید",
310
- elem_classes="operation-button"
311
- )
312
- compress_btn = gr.Button(
313
- "🗜️ فشرده‌سازی",
314
- elem_classes="operation-button"
315
- )
316
-
317
- # لیست فایل‌ها و پیش‌نمایش
318
- with gr.Row():
319
- with gr.Column(scale=2):
320
- file_list = gr.HTML()
321
- with gr.Column(scale=1):
322
- with gr.Group(elem_classes="file-preview"):
323
- gr.Markdown("### 👁️ پیش‌نمایش", elem_classes="preview-title")
324
- preview = gr.HTML()
325
-
326
- # پیام‌های وضعیت
327
- status = gr.HTML()
328
-
329
- # عملکردها
330
- def update_file_list(search=""):
331
- files_data = manager.list_files(search)
332
- return manager._format_file_list(files_data)
333
-
334
- def handle_upload(file):
335
- success, message = manager.upload_file(file)
336
- return message, update_file_list()
337
-
338
- def handle_new_folder():
339
- name = gr.Textbox(label="نام پوشه جدید").value
340
- success, message = manager.create_folder(name)
341
- return message, update_file_list()
342
-
343
- # اتصال رویدادها
344
- search_box.change(update_file_list, inputs=[search_box], outputs=[file_list])
345
- upload_btn.upload(handle_upload, inputs=[upload_btn], outputs=[status, file_list])
346
- new_folder_btn.click(handle_new_folder, outputs=[status, file_list])
347
- refresh_btn.click(update_file_list, outputs=[file_list])
348
-
349
- home_btn.click(
350
- lambda: (str(manager.root_path), update_file_list()),
351
- outputs=[current_path, file_list]
352
- )
353
-
354
- up_btn.click(
355
- lambda p: (str(Path(p).parent), update_file_list()),
356
- inputs=[current_path],
357
- outputs=[current_path, file_list]
358
- )
359
-
360
- return interface
361
-
362
- if __name__ == "__main__":
363
- demo = create_interface()
364
- demo.launch()
 
1
+ import streamlit as st
 
 
 
2
  from pathlib import Path
3
  from datetime import datetime
4
+ import shutil
5
  import zipfile
6
+ from PIL import Image
7
+ import time
8
 
9
+ # تنظیمات صفحه
10
+ st.set_page_config(page_title="مدیریت فایل‌ها", layout="centered", page_icon="📁")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # CSS برای طراحی گلس مورفیسم و نئومورفیسم
13
+ CUSTOM_CSS = """
14
+ <style>
15
+ body {
16
+ font-family: 'Vazir', sans-serif;
17
+ background: linear-gradient(135deg, #ece9e6, #ffffff);
 
 
18
  }
19
+ .dashboard-container {
20
+ max-width: 800px;
21
+ margin: auto;
 
22
  padding: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
+ .glass-card, .glass-button, .drag-drop {
25
+ backdrop-filter: blur(10px);
26
+ background: rgba(255, 255, 255, 0.15);
 
27
  border-radius: 20px;
28
+ box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.15), -4px -4px 10px rgba(255, 255, 255, 0.7);
29
+ padding: 20px;
30
+ margin: 15px 0;
31
  transition: all 0.3s ease;
32
  }
33
+ .glass-button {
34
+ color: #ffffff;
35
+ background: linear-gradient(135deg, #4a90e2, #50e3c2);
36
+ border: none;
37
+ font-size: 18px;
 
 
 
 
 
 
 
 
 
38
  cursor: pointer;
39
+ width: 100%;
40
+ padding: 10px 20px;
41
+ margin-top: 10px;
 
 
 
 
 
 
 
 
 
42
  border-radius: 20px;
43
+ transition: all 0.3s ease;
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
+ .glass-button:hover {
46
+ background: linear-gradient(135deg, #50e3c2, #4a90e2);
 
 
 
 
 
47
  }
48
+ .drag-drop {
49
+ border: 2px dashed #4a90e2;
50
+ padding: 40px;
51
+ text-align: center;
52
+ cursor: pointer;
 
 
 
53
  }
54
+ </style>
55
  """
56
+ st.markdown(CUSTOM_CSS, unsafe_allow_html=True)
57
 
58
+ class FileManager:
59
  def __init__(self):
60
+ self.root_path = Path('uploads')
61
  self.root_path.mkdir(exist_ok=True)
62
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  def upload_file(self, file):
64
+ """آپلود فایل با نوار پیشرفت"""
65
+ dest_path = self.root_path / file.name
66
+ with open(dest_path, "wb") as f:
67
+ total_size = file.size
68
+ chunk_size = 1024
69
+ bytes_read = 0
70
+ while bytes_read < total_size:
71
+ data = file.read(chunk_size)
72
+ f.write(data)
73
+ bytes_read += len(data)
74
+ st.progress(bytes_read / total_size)
75
+ return f"فایل '{file.name}' با موفقیت آپلود شد."
76
+
77
+ def list_files(self, file_type=None):
78
+ """لیست فایل‌ها با فیلتر نوع فایل"""
79
+ files = [f.name for f in self.root_path.iterdir() if f.is_file() and (not file_type or f.suffix == file_type)]
80
+ return files
81
+
82
+ def delete_file(self, filename):
83
+ """حذف فایل"""
84
+ path = self.root_path / filename
85
+ if path.exists():
86
+ path.unlink()
87
+ return f"فایل '{filename}' حذف شد."
88
+ return f"فایل '{filename}' یافت نشد."
89
+
90
+ def preview_file(self, filename):
91
+ """پیش‌نمایش فایل"""
92
+ path = self.root_path / filename
93
+ if path.suffix in ['.jpg', '.jpeg', '.png']:
94
+ return Image.open(path)
95
+ elif path.suffix == '.txt':
96
+ with open(path, "r", encoding="utf-8") as f:
97
+ return f.read(300)
98
+ return "پیش‌نمایش در دسترس نیست."
99
+
100
+ def compress_files(self, files):
101
+ """فشرده‌سازی فایل‌ها"""
102
+ zip_name = f"compressed_{datetime.now().strftime('%Y%m%d%H%M%S')}.zip"
103
+ zip_path = self.root_path / zip_name
104
+ with zipfile.ZipFile(zip_path, 'w') as zipf:
105
+ for file in files:
106
+ file_path = self.root_path / file
107
+ zipf.write(file_path, file)
108
+ st.progress((files.index(file) + 1) / len(files))
109
+ return f"فایل‌ها با نام '{zip_name}' فشرده شدند."
110
+
111
+ file_manager = FileManager()
112
+
113
+ # آپلود فایل
114
+ st.title("📁 مدیریت فایل‌ها")
115
+ uploaded_file = st.file_uploader("فایل خود را آپلود کنید", type=["jpg", "jpeg", "png", "txt"], accept_multiple_files=False)
116
+
117
+ if uploaded_file:
118
+ st.write(file_manager.upload_file(uploaded_file))
119
+
120
+ # لیست فایل‌ها و فیلتر نوع فایل
121
+ st.subheader("فایل‌های آپلود شده")
122
+ file_type = st.selectbox("نمایش فایل‌های:", ["همه", ".jpg", ".txt", ".png"], index=0)
123
+ files = file_manager.list_files(None if file_type == "همه" else file_type)
124
+
125
+ # نمایش منوی زمینه و عملیات‌ها
126
+ for file in files:
127
+ col1, col2, col3, col4 = st.columns([3, 1, 1, 1])
128
+ with col1:
129
+ if st.button(f"پیش‌نمایش {file}"):
130
+ preview = file_manager.preview_file(file)
131
+ if isinstance(preview, str):
132
+ st.text(preview)
133
  else:
134
+ st.image(preview)
135
+ with col2:
136
+ if st.button(f"حذف {file}"):
137
+ st.write(file_manager.delete_file(file))
138
+ with col3:
139
+ st.download_button(label=f"دانلود {file}", data=open(file_manager.root_path / file, 'rb').read(), file_name=file)
140
+
141
+ # فشرده‌سازی فایل‌ها
142
+ st.subheader("فشرده‌سازی فایل‌ها")
143
+ selected_files = st.multiselect("فایل‌های موردنظر را انتخاب کنید", files)
144
+ if st.button("فشرده‌سازی فایل‌ها"):
145
+ st.write(file_manager.compress_files(selected_files))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin_dashboard_sub.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from 'react';
2
+ import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts';
3
+ import { Upload, RefreshCw, Database, FileText, Settings } from 'lucide-react';
4
+ import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
5
+ import { Alert, AlertDescription } from '@/components/ui/alert';
6
+ import { Button } from '@/components/ui/button';
7
+
8
+ const AdminDashboard = () => {
9
+ // Sample data for learning chart
10
+ const [learningData, setLearningData] = useState([]);
11
+ const [isUpdating, setIsUpdating] = useState(false);
12
+ const [chatHistory, setChatHistory] = useState([]);
13
+ const [message, setMessage] = useState('');
14
+ const [stats, setStats] = useState({});
15
+ const [plotData, setPlotData] = useState([]);
16
+
17
+ const handleKnowledgeUpdate = () => {
18
+ setIsUpdating(true);
19
+ // Simulate update
20
+ setTimeout(() => setIsUpdating(false), 2000);
21
+ };
22
+
23
+ const handleChatSubmit = async () => {
24
+ const response = await fetch('http://localhost:7860/api/chat', {
25
+ method: 'POST',
26
+ headers: {
27
+ 'Content-Type': 'application/json'
28
+ },
29
+ body: JSON.stringify({ message, history: chatHistory })
30
+ });
31
+ const data = await response.json();
32
+ setChatHistory(data.history);
33
+ setMessage('');
34
+ };
35
+
36
+ const handleFileUpload = async (file) => {
37
+ const formData = new FormData();
38
+ formData.append('file', file);
39
+ const response = await fetch('http://localhost:7860/api/upload', {
40
+ method: 'POST',
41
+ body: formData
42
+ });
43
+ const data = await response.json();
44
+ alert(data.message);
45
+ };
46
+
47
+ const handleShowStats = async () => {
48
+ const response = await fetch('http://localhost:7860/api/stats');
49
+ const data = await response.json();
50
+ setStats(data.stats);
51
+ setPlotData(data.plot);
52
+ };
53
+
54
+ useEffect(() => {
55
+ // Fetch initial learning data
56
+ const fetchLearningData = async () => {
57
+ const response = await fetch('http://localhost:7860/api/learning_data');
58
+ const data = await response.json();
59
+ setLearningData(data);
60
+ };
61
+ fetchLearningData();
62
+ }, []);
63
+
64
+ return (
65
+ <div className="p-6 max-w-7xl mx-auto space-y-6">
66
+ {/* Header */}
67
+ <div className="flex justify-between items-center">
68
+ <h1 className="text-3xl font-bold">Admin Dashboard</h1>
69
+ <Button
70
+ onClick={handleKnowledgeUpdate}
71
+ className="bg-blue-600 hover:bg-blue-700"
72
+ disabled={isUpdating}
73
+ >
74
+ <RefreshCw className={`w-4 h-4 mr-2 ${isUpdating ? 'animate-spin' : ''}`} />
75
+ Update Knowledge Base
76
+ </Button>
77
+ </div>
78
+
79
+ {/* Statistics Cards */}
80
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
81
+ <Card>
82
+ <CardContent className="pt-6">
83
+ <div className="text-2xl font-bold">{stats.average_response_time || '89%'}</div>
84
+ <div className="text-sm text-gray-500">Current Learning Rate</div>
85
+ </CardContent>
86
+ </Card>
87
+
88
+ <Card>
89
+ <CardContent className="pt-6">
90
+ <div className="text-2xl font-bold">{stats.total_interactions || '1,234'}</div>
91
+ <div className="text-sm text-gray-500">Number of Answered Questions</div>
92
+ </CardContent>
93
+ </Card>
94
+
95
+ <Card>
96
+ <CardContent className="pt-6">
97
+ <div className="text-2xl font-bold">{stats.user_satisfaction || '95%'}</div>
98
+ <div className="text-sm text-gray-500">User Satisfaction</div>
99
+ </CardContent>
100
+ </Card>
101
+ </div>
102
+
103
+ {/* Learning Chart */}
104
+ <Card>
105
+ <CardHeader>
106
+ <CardTitle>Learning Rate Trend</CardTitle>
107
+ </CardHeader>
108
+ <CardContent>
109
+ <div className="h-[400px] w-full">
110
+ <LineChart
111
+ width={800}
112
+ height={400}
113
+ data={plotData}
114
+ margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
115
+ >
116
+ <CartesianGrid strokeDasharray="3 3" />
117
+ <XAxis dataKey="date" />
118
+ <YAxis />
119
+ <Tooltip />
120
+ <Legend />
121
+ <Line
122
+ type="monotone"
123
+ dataKey="rate"
124
+ stroke="#2563eb"
125
+ name="Learning Rate"
126
+ strokeWidth={2}
127
+ />
128
+ </LineChart>
129
+ </div>
130
+ </CardContent>
131
+ </Card>
132
+
133
+ {/* Operation Buttons */}
134
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
135
+ <Button className="h-24 text-lg justify-start p-6" variant="outline">
136
+ <FileText className="w-6 h-6 mr-4" />
137
+ Manage Files
138
+ </Button>
139
+
140
+ <Button className="h-24 text-lg justify-start p-6" variant="outline">
141
+ <Settings className="w-6 h-6 mr-4" />
142
+ Learning Settings
143
+ </Button>
144
+ </div>
145
+
146
+ {/* Alerts and Announcements */}
147
+ <Alert>
148
+ <AlertDescription>
149
+ Last knowledge base update: Yesterday at 15:30
150
+ </AlertDescription>
151
+ </Alert>
152
+
153
+ {/* Chat */}
154
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
155
+ <Card>
156
+ <CardHeader>
157
+ <CardTitle>Chat</CardTitle>
158
+ </CardHeader>
159
+ <CardContent>
160
+ <div className="h-[400px] w-full overflow-y-auto">
161
+ {chatHistory.map((msg, index) => (
162
+ <div key={index} className="p-2 border-b">
163
+ {msg}
164
+ </div>
165
+ ))}
166
+ </div>
167
+ <div className="flex mt-4">
168
+ <input
169
+ type="text"
170
+ className="flex-grow p-2 border rounded-l"
171
+ value={message}
172
+ onChange={(e) => setMessage(e.target.value)}
173
+ onKeyPress={(e) => e.key === 'Enter' && handleChatSubmit()}
174
+ />
175
+ <button
176
+ className="p-2 bg-blue-600 text-white rounded-r"
177
+ onClick={handleChatSubmit}
178
+ >
179
+ Send
180
+ </button>
181
+ </div>
182
+ </CardContent>
183
+ </Card>
184
+ </div>
185
+
186
+ {/* Upload Documents */}
187
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
188
+ <Card>
189
+ <CardHeader>
190
+ <CardTitle>Upload Documents</CardTitle>
191
+ </CardHeader>
192
+ <CardContent>
193
+ <input type="file" onChange={(e) => handleFileUpload(e.target.files[0])} />
194
+ </CardContent>
195
+ </Card>
196
+ </div>
197
+
198
+ {/* Statistics */}
199
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
200
+ <Card>
201
+ <CardHeader>
202
+ <CardTitle>Statistics</CardTitle>
203
+ </CardHeader>
204
+ <CardContent>
205
+ <button className="p-2 bg-blue-600 text-white rounded" onClick={handleShowStats}>
206
+ Show Stats
207
+ </button>
208
+ <div className="mt-4">
209
+ <pre>{JSON.stringify(stats, null, 2)}</pre>
210
+ </div>
211
+ </CardContent>
212
+ </Card>
213
+ </div>
214
+ </div>
215
+ );
216
+ };
217
+
218
+ export default AdminDashboard;