noho-platform 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,41 +3,91 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>NOHO - Dashboard</title>
6
+ <title>NOHO Platform - لوحة التحكم</title>
7
7
  <style>
8
8
  * { margin: 0; padding: 0; box-sizing: border-box; }
9
9
 
10
10
  :root {
11
11
  --primary: #6366f1;
12
12
  --primary-dark: #4f46e5;
13
+ --primary-light: #818cf8;
13
14
  --bg: #0f172a;
14
15
  --surface: #1e293b;
16
+ --surface-hover: #334155;
15
17
  --text: #f8fafc;
16
18
  --text-secondary: #94a3b8;
17
- --border: #334155;
19
+ --border: #475569;
18
20
  --success: #10b981;
19
21
  --error: #ef4444;
20
22
  --warning: #f59e0b;
23
+ --info: #3b82f6;
21
24
  }
22
25
 
23
26
  body {
24
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
27
+ font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
25
28
  background: var(--bg);
26
29
  color: var(--text);
27
30
  line-height: 1.6;
31
+ overflow-x: hidden;
28
32
  }
29
33
 
30
- .container {
31
- max-width: 1200px;
32
- margin: 0 auto;
33
- padding: 20px;
34
+ /* Loading Animation */
35
+ .loader {
36
+ position: fixed;
37
+ top: 0; left: 0; width: 100%; height: 100%;
38
+ background: var(--bg);
39
+ display: flex; justify-content: center; align-items: center;
40
+ z-index: 9999;
41
+ transition: opacity 0.5s;
42
+ }
43
+ .loader.hidden { opacity: 0; pointer-events: none; }
44
+ .loader-spinner {
45
+ width: 50px; height: 50px;
46
+ border: 3px solid var(--surface);
47
+ border-top-color: var(--primary);
48
+ border-radius: 50%;
49
+ animation: spin 1s linear infinite;
50
+ }
51
+ @keyframes spin { to { transform: rotate(360deg); } }
52
+
53
+ /* Connection Status */
54
+ .connection-status {
55
+ position: fixed;
56
+ top: 20px; left: 20px;
57
+ padding: 8px 16px;
58
+ border-radius: 20px;
59
+ font-size: 12px;
60
+ font-weight: bold;
61
+ z-index: 1000;
62
+ display: flex; align-items: center; gap: 8px;
63
+ transition: all 0.3s;
64
+ }
65
+ .connection-status.online {
66
+ background: rgba(16, 185, 129, 0.2);
67
+ color: var(--success);
68
+ border: 1px solid var(--success);
69
+ }
70
+ .connection-status.offline {
71
+ background: rgba(239, 68, 68, 0.2);
72
+ color: var(--error);
73
+ border: 1px solid var(--error);
74
+ }
75
+ .status-dot {
76
+ width: 8px; height: 8px;
77
+ border-radius: 50%;
78
+ background: currentColor;
79
+ animation: pulse 2s infinite;
34
80
  }
81
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
35
82
 
36
83
  header {
37
- background: var(--surface);
84
+ background: rgba(30, 41, 59, 0.8);
85
+ backdrop-filter: blur(10px);
38
86
  border-bottom: 1px solid var(--border);
39
87
  padding: 20px 0;
40
- margin-bottom: 30px;
88
+ position: sticky;
89
+ top: 0;
90
+ z-index: 100;
41
91
  }
42
92
 
43
93
  .header-content {
@@ -55,14 +105,18 @@
55
105
  background: linear-gradient(135deg, var(--primary), #a855f7);
56
106
  -webkit-background-clip: text;
57
107
  -webkit-text-fill-color: transparent;
108
+ display: flex; align-items: center; gap: 10px;
109
+ }
110
+ .logo::before {
111
+ content: "◈";
112
+ font-size: 32px;
58
113
  }
59
114
 
60
- .auth-section {
61
- display: flex;
62
- gap: 10px;
115
+ .user-menu {
116
+ display: flex; align-items: center; gap: 15px;
63
117
  }
64
118
 
65
- button {
119
+ .btn {
66
120
  background: var(--primary);
67
121
  color: white;
68
122
  border: none;
@@ -71,265 +125,430 @@
71
125
  cursor: pointer;
72
126
  font-size: 14px;
73
127
  transition: all 0.3s;
128
+ display: inline-flex; align-items: center; gap: 8px;
74
129
  }
75
-
76
- button:hover {
130
+ .btn:hover {
77
131
  background: var(--primary-dark);
78
132
  transform: translateY(-2px);
133
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
79
134
  }
80
-
81
- button.secondary {
135
+ .btn.secondary {
82
136
  background: transparent;
83
137
  border: 1px solid var(--border);
84
138
  }
139
+ .btn.secondary:hover {
140
+ background: var(--surface);
141
+ border-color: var(--primary);
142
+ }
143
+ .btn.danger { background: var(--error); }
144
+ .btn.success { background: var(--success); }
145
+ .btn.small { padding: 6px 12px; font-size: 12px; }
85
146
 
86
- button.danger {
87
- background: var(--error);
147
+ .container {
148
+ max-width: 1200px;
149
+ margin: 0 auto;
150
+ padding: 30px 20px;
88
151
  }
89
152
 
90
- .grid {
153
+ /* Stats Cards */
154
+ .stats-grid {
91
155
  display: grid;
92
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
156
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
93
157
  gap: 20px;
94
158
  margin-bottom: 30px;
95
159
  }
96
160
 
161
+ .stat-card {
162
+ background: var(--surface);
163
+ border: 1px solid var(--border);
164
+ border-radius: 16px;
165
+ padding: 24px;
166
+ position: relative;
167
+ overflow: hidden;
168
+ transition: transform 0.3s;
169
+ }
170
+ .stat-card:hover {
171
+ transform: translateY(-5px);
172
+ border-color: var(--primary);
173
+ }
174
+ .stat-card::before {
175
+ content: "";
176
+ position: absolute;
177
+ top: 0; left: 0; right: 0; height: 3px;
178
+ background: linear-gradient(90deg, var(--primary), var(--primary-light));
179
+ transform: scaleX(0);
180
+ transition: transform 0.3s;
181
+ }
182
+ .stat-card:hover::before { transform: scaleX(1); }
183
+
184
+ .stat-icon {
185
+ font-size: 32px;
186
+ margin-bottom: 10px;
187
+ }
188
+ .stat-value {
189
+ font-size: 36px;
190
+ font-weight: bold;
191
+ color: var(--primary);
192
+ line-height: 1;
193
+ }
194
+ .stat-label {
195
+ color: var(--text-secondary);
196
+ font-size: 14px;
197
+ margin-top: 5px;
198
+ }
199
+
200
+ /* Cards */
97
201
  .card {
98
202
  background: var(--surface);
99
203
  border: 1px solid var(--border);
100
- border-radius: 12px;
204
+ border-radius: 16px;
101
205
  padding: 24px;
206
+ margin-bottom: 24px;
102
207
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
208
+ animation: slideUp 0.5s ease-out;
209
+ }
210
+ @keyframes slideUp {
211
+ from { opacity: 0; transform: translateY(20px); }
212
+ to { opacity: 1; transform: translateY(0); }
103
213
  }
104
214
 
105
- .card h2 {
106
- margin-bottom: 16px;
107
- color: var(--text);
215
+ .card-header {
216
+ display: flex;
217
+ justify-content: space-between;
218
+ align-items: center;
219
+ margin-bottom: 20px;
220
+ padding-bottom: 15px;
221
+ border-bottom: 1px solid var(--border);
222
+ }
223
+ .card-title {
108
224
  font-size: 20px;
225
+ font-weight: bold;
226
+ display: flex; align-items: center; gap: 10px;
109
227
  }
110
228
 
229
+ /* Form Elements */
111
230
  .form-group {
112
- margin-bottom: 16px;
231
+ margin-bottom: 20px;
113
232
  }
114
-
115
233
  label {
116
234
  display: block;
117
235
  margin-bottom: 8px;
118
236
  color: var(--text-secondary);
119
237
  font-size: 14px;
238
+ font-weight: 500;
120
239
  }
121
-
122
240
  input, textarea, select {
123
241
  width: 100%;
124
- padding: 12px;
242
+ padding: 12px 16px;
125
243
  background: var(--bg);
126
244
  border: 1px solid var(--border);
127
245
  border-radius: 8px;
128
246
  color: var(--text);
129
247
  font-size: 14px;
130
- font-family: inherit;
248
+ transition: all 0.3s;
249
+ }
250
+ input:focus, textarea:focus, select:focus {
251
+ outline: none;
252
+ border-color: var(--primary);
253
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
131
254
  }
132
-
133
255
  textarea {
134
256
  min-height: 200px;
135
- font-family: 'Courier New', monospace;
257
+ font-family: 'Fira Code', 'Courier New', monospace;
136
258
  direction: ltr;
259
+ resize: vertical;
137
260
  }
138
261
 
139
- .stats {
140
- display: grid;
141
- grid-template-columns: repeat(3, 1fr);
142
- gap: 15px;
143
- margin-bottom: 20px;
262
+ /* Code Editor Style */
263
+ .code-editor {
264
+ position: relative;
265
+ background: #0d1117;
266
+ border: 1px solid #30363d;
267
+ border-radius: 8px;
268
+ overflow: hidden;
269
+ }
270
+ .code-header {
271
+ background: #161b22;
272
+ padding: 8px 16px;
273
+ display: flex;
274
+ gap: 8px;
275
+ align-items: center;
276
+ border-bottom: 1px solid #30363d;
144
277
  }
278
+ .code-dot {
279
+ width: 12px; height: 12px;
280
+ border-radius: 50%;
281
+ }
282
+ .code-dot.red { background: #ff5f56; }
283
+ .code-dot.yellow { background: #ffbd2e; }
284
+ .code-dot.green { background: #27c93f; }
145
285
 
146
- .stat-box {
286
+ /* Tabs */
287
+ .tabs {
288
+ display: flex;
289
+ gap: 5px;
290
+ margin-bottom: 20px;
147
291
  background: var(--bg);
148
- padding: 20px;
292
+ padding: 5px;
149
293
  border-radius: 8px;
150
- text-align: center;
151
- border: 1px solid var(--border);
294
+ width: fit-content;
152
295
  }
153
-
154
- .stat-value {
155
- font-size: 32px;
156
- font-weight: bold;
157
- color: var(--primary);
296
+ .tab {
297
+ padding: 10px 20px;
298
+ cursor: pointer;
299
+ border-radius: 6px;
300
+ transition: all 0.3s;
301
+ border: none;
302
+ background: transparent;
303
+ color: var(--text-secondary);
304
+ font-weight: 500;
305
+ }
306
+ .tab.active {
307
+ background: var(--primary);
308
+ color: white;
309
+ }
310
+ .tab:hover:not(.active) {
311
+ color: var(--text);
312
+ background: var(--surface-hover);
158
313
  }
159
314
 
160
- .stat-label {
161
- color: var(--text-secondary);
162
- font-size: 14px;
315
+ .tab-content {
316
+ display: none;
317
+ animation: fadeIn 0.3s;
163
318
  }
319
+ .tab-content.active { display: block; }
320
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
164
321
 
165
- .api-key-display {
166
- background: var(--bg);
167
- padding: 16px;
168
- border-radius: 8px;
322
+ /* API Key Display */
323
+ .api-key-box {
324
+ background: linear-gradient(135deg, #1e293b, #0f172a);
169
325
  border: 1px solid var(--border);
170
- font-family: monospace;
326
+ border-radius: 12px;
327
+ padding: 20px;
171
328
  display: flex;
172
329
  justify-content: space-between;
173
330
  align-items: center;
174
- margin-bottom: 16px;
331
+ gap: 20px;
332
+ font-family: 'Fira Code', monospace;
175
333
  word-break: break-all;
176
334
  }
177
-
178
- .modal {
179
- display: none;
180
- position: fixed;
181
- top: 0;
182
- left: 0;
183
- width: 100%;
184
- height: 100%;
185
- background: rgba(0,0,0,0.8);
186
- z-index: 1000;
187
- justify-content: center;
188
- align-items: center;
189
- }
190
-
191
- .modal.active {
192
- display: flex;
335
+ .api-key-text {
336
+ font-size: 16px;
337
+ letter-spacing: 1px;
193
338
  }
194
-
195
- .modal-content {
196
- background: var(--surface);
197
- padding: 30px;
198
- border-radius: 16px;
199
- max-width: 500px;
200
- width: 90%;
201
- max-height: 90vh;
202
- overflow-y: auto;
339
+ .api-key-hidden {
340
+ filter: blur(4px);
341
+ user-select: none;
203
342
  }
204
343
 
344
+ /* Pages List */
205
345
  .pages-list {
206
346
  list-style: none;
347
+ display: grid;
348
+ gap: 12px;
207
349
  }
208
-
209
350
  .page-item {
210
351
  background: var(--bg);
211
352
  border: 1px solid var(--border);
212
- border-radius: 8px;
353
+ border-radius: 12px;
213
354
  padding: 16px;
214
- margin-bottom: 12px;
215
355
  display: flex;
216
356
  justify-content: space-between;
217
357
  align-items: center;
358
+ transition: all 0.3s;
359
+ animation: slideIn 0.3s ease-out;
360
+ }
361
+ @keyframes slideIn {
362
+ from { opacity: 0; transform: translateX(-20px); }
363
+ to { opacity: 1; transform: translateX(0); }
364
+ }
365
+ .page-item:hover {
366
+ border-color: var(--primary);
367
+ transform: translateX(5px);
368
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
218
369
  }
219
-
220
370
  .page-info h3 {
221
- margin-bottom: 4px;
222
371
  font-size: 16px;
372
+ margin-bottom: 4px;
373
+ color: var(--primary-light);
223
374
  }
224
-
225
375
  .page-meta {
226
376
  font-size: 12px;
227
377
  color: var(--text-secondary);
378
+ display: flex; gap: 10px; align-items: center;
228
379
  }
229
-
230
380
  .badge {
231
381
  display: inline-block;
232
382
  padding: 4px 8px;
233
383
  border-radius: 4px;
234
- font-size: 12px;
235
- background: var(--primary);
236
- color: white;
237
- }
238
-
239
- .badge.warning {
240
- background: var(--warning);
384
+ font-size: 11px;
385
+ font-weight: bold;
386
+ text-transform: uppercase;
241
387
  }
388
+ .badge.public { background: rgba(16, 185, 129, 0.2); color: var(--success); }
389
+ .badge.private { background: rgba(239, 68, 68, 0.2); color: var(--error); }
242
390
 
243
- .analysis-result {
391
+ /* Analysis Result */
392
+ .analysis-box {
244
393
  background: var(--bg);
245
- border-radius: 8px;
246
- padding: 16px;
247
- margin-top: 16px;
394
+ border-radius: 12px;
395
+ padding: 20px;
396
+ margin-top: 20px;
248
397
  border-right: 4px solid var(--success);
249
398
  }
250
-
251
- .analysis-result.has-warnings {
399
+ .analysis-box.has-warnings {
252
400
  border-right-color: var(--warning);
253
401
  }
254
-
255
- .warning-item {
256
- color: var(--warning);
257
- margin: 4px 0;
258
- font-size: 14px;
402
+ .analysis-box.error {
403
+ border-right-color: var(--error);
259
404
  }
260
405
 
261
- .toast {
406
+ /* Toast Notifications */
407
+ .toast-container {
262
408
  position: fixed;
263
409
  bottom: 20px;
264
410
  left: 20px;
411
+ z-index: 2000;
412
+ display: flex;
413
+ flex-direction: column;
414
+ gap: 10px;
415
+ }
416
+ .toast {
265
417
  background: var(--surface);
266
418
  border: 1px solid var(--border);
267
419
  padding: 16px 24px;
268
- border-radius: 8px;
420
+ border-radius: 12px;
269
421
  box-shadow: 0 10px 15px -3px rgba(0,0,0,0.5);
270
- transform: translateY(100px);
271
- opacity: 0;
272
- transition: all 0.3s;
273
- z-index: 2000;
422
+ display: flex;
423
+ align-items: center;
424
+ gap: 12px;
425
+ min-width: 300px;
426
+ animation: slideInLeft 0.3s ease-out;
427
+ border-right: 4px solid var(--info);
274
428
  }
275
-
276
- .toast.show {
277
- transform: translateY(0);
278
- opacity: 1;
429
+ @keyframes slideInLeft {
430
+ from { transform: translateX(-100%); opacity: 0; }
431
+ to { transform: translateX(0); opacity: 1; }
279
432
  }
433
+ .toast.success { border-right-color: var(--success); }
434
+ .toast.error { border-right-color: var(--error); }
435
+ .toast.warning { border-right-color: var(--warning); }
280
436
 
281
- .toast.success { border-right: 4px solid var(--success); }
282
- .toast.error { border-right: 4px solid var(--error); }
283
-
284
- .hidden { display: none !important; }
285
-
286
- .tabs {
437
+ /* Live Activity */
438
+ .activity-feed {
439
+ max-height: 300px;
440
+ overflow-y: auto;
441
+ }
442
+ .activity-item {
287
443
  display: flex;
288
- gap: 10px;
289
- margin-bottom: 20px;
444
+ align-items: center;
445
+ gap: 12px;
446
+ padding: 12px;
290
447
  border-bottom: 1px solid var(--border);
291
- padding-bottom: 10px;
292
- }
293
-
294
- .tab {
295
- padding: 8px 16px;
296
- cursor: pointer;
297
- border-radius: 6px;
298
- transition: all 0.2s;
448
+ font-size: 14px;
449
+ animation: fadeIn 0.3s;
299
450
  }
300
-
301
- .tab.active {
302
- background: var(--primary);
303
- color: white;
451
+ .activity-item:last-child { border-bottom: none; }
452
+ .activity-time {
453
+ color: var(--text-secondary);
454
+ font-size: 12px;
455
+ margin-right: auto;
304
456
  }
305
457
 
306
- .tab-content {
458
+ /* Modal */
459
+ .modal-overlay {
307
460
  display: none;
461
+ position: fixed;
462
+ top: 0; left: 0; width: 100%; height: 100%;
463
+ background: rgba(0,0,0,0.8);
464
+ z-index: 1000;
465
+ justify-content: center;
466
+ align-items: center;
467
+ backdrop-filter: blur(5px);
468
+ }
469
+ .modal-overlay.active { display: flex; animation: fadeIn 0.3s; }
470
+ .modal-content {
471
+ background: var(--surface);
472
+ border: 1px solid var(--border);
473
+ border-radius: 20px;
474
+ padding: 30px;
475
+ max-width: 500px;
476
+ width: 90%;
477
+ max-height: 90vh;
478
+ overflow-y: auto;
479
+ animation: scaleIn 0.3s;
480
+ }
481
+ @keyframes scaleIn {
482
+ from { transform: scale(0.9); opacity: 0; }
483
+ to { transform: scale(1); opacity: 1; }
308
484
  }
309
485
 
310
- .tab-content.active {
311
- display: block;
486
+ /* Welcome Screen */
487
+ .hero {
488
+ text-align: center;
489
+ padding: 80px 20px;
490
+ background: radial-gradient(circle at center, rgba(99,102,241,0.1) 0%, transparent 70%);
491
+ border-radius: 24px;
492
+ margin-bottom: 40px;
493
+ }
494
+ .hero h1 {
495
+ font-size: 48px;
496
+ margin-bottom: 20px;
497
+ background: linear-gradient(135deg, var(--primary), #a855f7);
498
+ -webkit-background-clip: text;
499
+ -webkit-text-fill-color: transparent;
500
+ }
501
+ .hero p {
502
+ color: var(--text-secondary);
503
+ font-size: 18px;
504
+ max-width: 600px;
505
+ margin: 0 auto 30px;
312
506
  }
313
507
 
508
+ /* Responsive */
314
509
  @media (max-width: 768px) {
315
- .grid { grid-template-columns: 1fr; }
316
- .stats { grid-template-columns: 1fr; }
317
- .header-content { flex-direction: column; gap: 10px; }
510
+ .stats-grid { grid-template-columns: 1fr; }
511
+ .header-content { flex-direction: column; gap: 15px; }
512
+ .hero h1 { font-size: 36px; }
318
513
  }
514
+
515
+ .hidden { display: none !important; }
319
516
  </style>
320
517
  </head>
321
518
  <body>
322
519
 
520
+ <!-- Loading Screen -->
521
+ <div class="loader" id="loader">
522
+ <div class="loader-spinner"></div>
523
+ </div>
524
+
525
+ <!-- Connection Status -->
526
+ <div class="connection-status offline" id="connectionStatus">
527
+ <span class="status-dot"></span>
528
+ <span id="connectionText">غير متصل</span>
529
+ </div>
530
+
531
+ <!-- Toast Container -->
532
+ <div class="toast-container" id="toastContainer"></div>
533
+
323
534
  <header>
324
535
  <div class="header-content">
325
536
  <div class="logo">NOHO Platform</div>
326
- <div class="auth-section" id="authSection">
327
- <button onclick="showModal('loginModal')">تسجيل الدخول</button>
328
- <button class="secondary" onclick="showModal('registerModal')">إنشاء حساب</button>
537
+
538
+ <div class="user-menu" id="guestMenu">
539
+ <button class="btn secondary" onclick="showModal('loginModal')">
540
+ <span>🔑</span> تسجيل الدخول
541
+ </button>
542
+ <button class="btn" onclick="showModal('registerModal')">
543
+ <span>✨</span> إنشاء حساب
544
+ </button>
329
545
  </div>
330
- <div class="auth-section hidden" id="userSection">
331
- <span id="usernameDisplay"></span>
332
- <button class="secondary" onclick="logout()">خروج</button>
546
+
547
+ <div class="user-menu hidden" id="userMenu">
548
+ <span id="usernameDisplay" style="font-weight: bold;"></span>
549
+ <button class="btn secondary small" onclick="logout()">
550
+ <span>🚪</span> خروج
551
+ </button>
333
552
  </div>
334
553
  </div>
335
554
  </header>
@@ -337,173 +556,296 @@
337
556
  <div class="container">
338
557
  <!-- Guest View -->
339
558
  <div id="guestView">
340
- <div class="grid">
341
- <div class="card" style="text-align: center; padding: 60px 20px;">
342
- <h1 style="font-size: 48px; margin-bottom: 20px;">🚀</h1>
343
- <h2>ابنِ صفحاتك الديناميكية</h2>
344
- <p style="color: var(--text-secondary); margin: 20px 0;">
345
- منصة متكاملة لإنشاء صفحات ويب ذكية مع AI، ربط API، وتحليل تلقائي للكود
346
- </p>
347
- <button onclick="showModal('registerModal')" style="font-size: 18px; padding: 15px 30px;">
348
- ابدأ الآن مجاناً
349
- </button>
559
+ <div class="hero">
560
+ <h1>🚀 NOHO Platform</h1>
561
+ <p>منصة متكاملة لإنشاء صفحات ويب ذكية مع تحليل AI، ربط API، واستضافة فورية. بناء وتشغيل صفحاتك الديناميكية في ثوانٍ!</p>
562
+ <button class="btn" style="font-size: 18px; padding: 15px 40px;" onclick="showModal('registerModal')">
563
+ ابدأ الآن مجاناً
564
+ </button>
565
+ </div>
566
+
567
+ <div class="stats-grid">
568
+ <div class="stat-card">
569
+ <div class="stat-icon">⚡</div>
570
+ <div class="stat-value" id="publicPages">0</div>
571
+ <div class="stat-label">صفحة عامة</div>
572
+ </div>
573
+ <div class="stat-card">
574
+ <div class="stat-icon">👥</div>
575
+ <div class="stat-value" id="publicUsers">0</div>
576
+ <div class="stat-label">مستخدم نشط</div>
577
+ </div>
578
+ <div class="stat-card">
579
+ <div class="stat-icon">🤖</div>
580
+ <div class="stat-value">GPT-4</div>
581
+ <div class="stat-label">ذكاء اصطناعي</div>
350
582
  </div>
351
583
  </div>
352
584
  </div>
353
585
 
354
586
  <!-- Dashboard View -->
355
587
  <div id="dashboardView" class="hidden">
356
- <div class="stats">
357
- <div class="stat-box">
588
+ <div class="stats-grid">
589
+ <div class="stat-card">
590
+ <div class="stat-icon">📄</div>
358
591
  <div class="stat-value" id="statPages">0</div>
359
- <div class="stat-label">الصفحات</div>
592
+ <div class="stat-label">صفحاتي</div>
360
593
  </div>
361
- <div class="stat-box">
594
+ <div class="stat-card">
595
+ <div class="stat-icon">👁️</div>
362
596
  <div class="stat-value" id="statViews">0</div>
363
- <div class="stat-label">المشاهدات</div>
597
+ <div class="stat-label">إجمالي المشاهدات</div>
364
598
  </div>
365
- <div class="stat-box">
599
+ <div class="stat-card">
600
+ <div class="stat-icon">📊</div>
366
601
  <div class="stat-value" id="statRequests">0</div>
367
602
  <div class="stat-label">الطلبات</div>
368
603
  </div>
369
604
  </div>
370
605
 
371
- <div class="grid">
372
- <!-- API Key Section -->
373
- <div class="card" style="grid-column: 1 / -1;">
374
- <h2>🔑 مفتاح API</h2>
375
- <p style="color: var(--text-secondary); margin-bottom: 16px;">
376
- استخدم هذا المفتاح للوصول إلى API من أي لغة برمجة
377
- </p>
378
- <div class="api-key-display">
379
- <span id="apiKeyDisplay">********</span>
380
- <div>
381
- <button class="secondary" onclick="toggleApiKey()" style="margin-left: 8px;">إظهار</button>
382
- <button onclick="copyApiKey()">نسخ</button>
383
- <button class="danger" onclick="regenerateKey()" style="margin-right: 8px;">تجديد</button>
384
- </div>
385
- </div>
386
- <div style="background: var(--bg); padding: 16px; border-radius: 8px; direction: ltr; text-align: left; overflow-x: auto;">
387
- <code style="color: var(--text-secondary);">
388
- curl -X POST http://localhost:5000/api/pages \<br>
389
- -H "X-API-Key: <span class="key-placeholder">YOUR_KEY</span>" \<br>
390
- -d '{"route":"/hello","code":"res.send(\"Hello\")"}'
391
- </code>
606
+ <!-- API Key Section -->
607
+ <div class="card">
608
+ <div class="card-header">
609
+ <div class="card-title">🔑 مفتاح API</div>
610
+ <button class="btn small secondary" onclick="regenerateKey()">تجديد</button>
611
+ </div>
612
+ <p style="color: var(--text-secondary); margin-bottom: 16px;">
613
+ استخدم هذا المفتاح للوصول إلى API من أي لغة برمجة
614
+ </p>
615
+ <div class="api-key-box">
616
+ <span class="api-key-text api-key-hidden" id="apiKeyDisplay">••••••••••••••••••••••••••</span>
617
+ <div style="display: flex; gap: 8px;">
618
+ <button class="btn small secondary" onclick="toggleApiKey()">👁️ إظهار</button>
619
+ <button class="btn small" onclick="copyApiKey()">📋 نسخ</button>
392
620
  </div>
393
621
  </div>
622
+ <div style="margin-top: 16px; background: var(--bg); padding: 16px; border-radius: 8px; direction: ltr; font-family: monospace; font-size: 13px; overflow-x: auto;">
623
+ <code style="color: var(--text-secondary);">
624
+ curl -X POST http://localhost:5000/api/pages \<br>
625
+ -H "X-API-Key: <span style="color: var(--primary);">YOUR_KEY_HERE</span>" \<br>
626
+ -d '{"route":"/hello","code":"res.send(\"Hello World\")"}'
627
+ </code>
628
+ </div>
629
+ </div>
394
630
 
395
- <!-- Create Page -->
396
- <div class="card" style="grid-column: 1 / -1;">
397
- <h2>✨ إنشاء صفحة جديدة</h2>
398
-
399
- <div class="tabs">
400
- <div class="tab active" onclick="switchTab('codeTab', this)">كود مخصص</div>
401
- <div class="tab" onclick="switchTab('aiTab', this)">توليد بالذكاء الاصطناعي</div>
402
- </div>
631
+ <!-- Create Page -->
632
+ <div class="card">
633
+ <div class="card-header">
634
+ <div class="card-title">✨ إنشاء صفحة جديدة</div>
635
+ </div>
636
+
637
+ <div class="tabs">
638
+ <button class="tab active" onclick="switchTab('codeTab', this)">📝 كود مخصص</button>
639
+ <button class="tab" onclick="switchTab('aiTab', this)">🤖 توليد بالAI</button>
640
+ </div>
403
641
 
404
- <div id="codeTab" class="tab-content active">
405
- <div class="form-group">
406
- <label>مسار الصفحة (Route)</label>
407
- <input type="text" id="pageRoute" placeholder="/my-page" dir="ltr">
408
- </div>
409
-
410
- <div class="form-group">
411
- <label>الكود (JavaScript)</label>
412
- <textarea id="pageCode" placeholder="// مثال:
642
+ <div id="codeTab" class="tab-content active">
643
+ <div class="form-group">
644
+ <label>📍 مسار الصفحة (Route)</label>
645
+ <input type="text" id="pageRoute" placeholder="/my-awesome-page" dir="ltr">
646
+ </div>
647
+
648
+ <div class="form-group">
649
+ <label>💻 كود JavaScript</label>
650
+ <div class="code-editor">
651
+ <div class="code-header">
652
+ <div class="code-dot red"></div>
653
+ <div class="code-dot yellow"></div>
654
+ <div class="code-dot green"></div>
655
+ <span style="color: var(--text-secondary); font-size: 12px; margin-right: 10px;">main.js</span>
656
+ </div>
657
+ <textarea id="pageCode" placeholder="// اكتب كودك هنا...
413
658
  res.send(`
414
- <h1>مرحباً بك في NOHO</h1>
415
- <p>الوقت: ${new Date()}</p>
659
+ <!DOCTYPE html>
660
+ <html>
661
+ <head><title>صفحتي</title></head>
662
+ <body>
663
+ <h1>مرحباً بك في NOHO!</h1>
664
+ <p>الوقت: ${new Date().toLocaleString('ar-EG')}</p>
665
+ </body>
666
+ </html>
416
667
  `);" dir="ltr"></textarea>
417
668
  </div>
669
+ </div>
418
670
 
419
- <button onclick="analyzeCode()">🔍 تحليل الكود</button>
420
- <button onclick="createPage()" style="margin-right: 10px;">نشر الصفحة</button>
421
-
422
- <div id="analysisResult"></div>
671
+ <div style="display: flex; gap: 10px;">
672
+ <button class="btn secondary" onclick="analyzeCode()">🔍 تحليل الكود</button>
673
+ <button class="btn success" onclick="createPage()">🚀 نشر الصفحة</button>
423
674
  </div>
675
+
676
+ <div id="analysisResult"></div>
677
+ </div>
424
678
 
425
- <div id="aiTab" class="tab-content">
426
- <div class="form-group">
427
- <label>وصف الصفحة المطلوبة</label>
428
- <input type="text" id="aiPrompt" placeholder="صفحة عرض منتجات مع تصميم عصري">
429
- </div>
430
- <button onclick="generateWithAI()">✨ توليد الكود</button>
431
- <div id="aiResult" style="margin-top: 20px;"></div>
679
+ <div id="aiTab" class="tab-content">
680
+ <div class="form-group">
681
+ <label>📝 وصف الصفحة المطلوبة</label>
682
+ <input type="text" id="aiPrompt" placeholder="مثال: صفحة عرض منتجات عصرية مع صور وأسعار">
432
683
  </div>
684
+ <button class="btn" onclick="generateWithAI()">✨ توليد الكود الآن</button>
685
+ <div id="aiResult" style="margin-top: 20px;"></div>
433
686
  </div>
687
+ </div>
434
688
 
689
+ <div class="grid" style="display: grid; grid-template-columns: 2fr 1fr; gap: 24px;">
435
690
  <!-- My Pages -->
436
- <div class="card" style="grid-column: 1 / -1;">
437
- <h2>📄 صفحاتي</h2>
691
+ <div class="card">
692
+ <div class="card-header">
693
+ <div class="card-title">📄 صفحاتي</div>
694
+ <button class="btn small secondary" onclick="loadPages()">🔄 تحديث</button>
695
+ </div>
438
696
  <ul class="pages-list" id="pagesList">
439
697
  <li style="text-align: center; color: var(--text-secondary); padding: 40px;">
440
698
  لا توجد صفحات بعد
441
699
  </li>
442
700
  </ul>
443
701
  </div>
702
+
703
+ <!-- Live Activity -->
704
+ <div class="card">
705
+ <div class="card-header">
706
+ <div class="card-title">📡 النشاط المباشر</div>
707
+ </div>
708
+ <div class="activity-feed" id="activityFeed">
709
+ <div class="activity-item">
710
+ <span>🟢</span>
711
+ <span>متصل بالسيرفر</span>
712
+ <span class="activity-time">الآن</span>
713
+ </div>
714
+ </div>
715
+ </div>
444
716
  </div>
445
717
  </div>
446
718
  </div>
447
719
 
448
720
  <!-- Login Modal -->
449
- <div class="modal" id="loginModal">
721
+ <div class="modal-overlay" id="loginModal">
450
722
  <div class="modal-content">
451
- <h2 style="margin-bottom: 20px;">تسجيل الدخول</h2>
723
+ <h2 style="margin-bottom: 20px;">🔑 تسجيل الدخول</h2>
452
724
  <div class="form-group">
453
- <label>البريد الإلكتروني</label>
454
- <input type="email" id="loginEmail" dir="ltr">
725
+ <label>📧 البريد الإلكتروني</label>
726
+ <input type="email" id="loginEmail" dir="ltr" placeholder="example@mail.com">
455
727
  </div>
456
728
  <div class="form-group">
457
- <label>كلمة المرور</label>
458
- <input type="password" id="loginPassword">
729
+ <label>🔒 كلمة المرور</label>
730
+ <input type="password" id="loginPassword" placeholder="********">
459
731
  </div>
460
- <button onclick="login()" style="width: 100%;">دخول</button>
461
- <button class="secondary" onclick="hideModal('loginModal')" style="width: 100%; margin-top: 10px;">إلغاء</button>
732
+ <button class="btn" style="width: 100%;" onclick="login()">دخول</button>
733
+ <button class="btn secondary" style="width: 100%; margin-top: 10px;" onclick="hideModal('loginModal')">إلغاء</button>
462
734
  </div>
463
735
  </div>
464
736
 
465
737
  <!-- Register Modal -->
466
- <div class="modal" id="registerModal">
738
+ <div class="modal-overlay" id="registerModal">
467
739
  <div class="modal-content">
468
- <h2 style="margin-bottom: 20px;">إنشاء حساب جديد</h2>
740
+ <h2 style="margin-bottom: 20px;">✨ إنشاء حساب جديد</h2>
469
741
  <div class="form-group">
470
- <label>اسم المستخدم (سيظهر في الرابط)</label>
471
- <input type="text" id="regUsername" placeholder="your-name" dir="ltr">
742
+ <label>👤 اسم المستخدم (سيظهر في الرابط)</label>
743
+ <input type="text" id="regUsername" dir="ltr" placeholder="your-name">
472
744
  </div>
473
745
  <div class="form-group">
474
- <label>البريد الإلكتروني</label>
475
- <input type="email" id="regEmail" dir="ltr">
746
+ <label>📧 البريد الإلكتروني</label>
747
+ <input type="email" id="regEmail" dir="ltr" placeholder="example@mail.com">
476
748
  </div>
477
749
  <div class="form-group">
478
- <label>كلمة المرور</label>
479
- <input type="password" id="regPassword">
750
+ <label>🔒 كلمة المرور (8 أحرف على الأقل)</label>
751
+ <input type="password" id="regPassword" placeholder="********">
480
752
  </div>
481
- <button onclick="register()" style="width: 100%;">إنشاء الحساب</button>
482
- <button class="secondary" onclick="hideModal('registerModal')" style="width: 100%; margin-top: 10px;">إلغاء</button>
753
+ <button class="btn" style="width: 100%;" onclick="register()">إنشاء الحساب</button>
754
+ <button class="btn secondary" style="width: 100%; margin-top: 10px;" onclick="hideModal('registerModal')">إلغاء</button>
483
755
  </div>
484
756
  </div>
485
757
 
486
- <!-- API Key Display Modal (After Register) -->
487
- <div class="modal" id="apiKeyModal">
758
+ <!-- API Key Modal -->
759
+ <div class="modal-overlay" id="apiKeyModal">
488
760
  <div class="modal-content">
489
761
  <h2 style="margin-bottom: 20px; color: var(--success);">✅ تم إنشاء الحساب!</h2>
490
762
  <p style="margin-bottom: 16px;">احفظ مفتاح API هذا جيداً، لن يُعرض مرة أخرى:</p>
491
- <div class="api-key-display" style="background: var(--bg); margin-bottom: 16px;">
492
- <span id="newApiKey" style="font-size: 16px;"></span>
763
+ <div class="api-key-box" style="background: rgba(16, 185, 129, 0.1); border-color: var(--success);">
764
+ <span id="newApiKey" style="font-size: 16px; font-weight: bold;"></span>
493
765
  </div>
494
- <button onclick="copyNewApiKey()" style="width: 100%; margin-bottom: 10px;">نسخ المفتاح</button>
495
- <button onclick="closeApiKeyModal()" style="width: 100%;">متابعة للوحة التحكم</button>
766
+ <button class="btn" style="width: 100%; margin-bottom: 10px;" onclick="copyNewApiKey()">📋 نسخ المفتاح</button>
767
+ <button class="btn secondary" style="width: 100%;" onclick="closeApiKeyModal()">متابعة للوحة التحكم</button>
496
768
  </div>
497
769
  </div>
498
770
 
499
- <div class="toast" id="toast"></div>
500
-
501
771
  <script>
502
- // API URL - يعمل تلقائياً على نفس السيرفر
503
772
  const API_URL = window.location.origin;
504
773
  let currentUser = null;
505
774
  let apiKeyVisible = false;
506
775
  let storedApiKey = '';
776
+ let ws = null;
777
+
778
+ // إخفاء اللودر
779
+ window.addEventListener('load', () => {
780
+ setTimeout(() => {
781
+ document.getElementById('loader').classList.add('hidden');
782
+ }, 500);
783
+ });
784
+
785
+ // WebSocket Connection
786
+ function connectWebSocket() {
787
+ const wsUrl = `ws://${window.location.host}`;
788
+ ws = new WebSocket(wsUrl);
789
+
790
+ ws.onopen = () => {
791
+ updateConnectionStatus(true);
792
+ showToast('متصل بالسيرفر', 'success');
793
+ };
794
+
795
+ ws.onclose = () => {
796
+ updateConnectionStatus(false);
797
+ setTimeout(connectWebSocket, 3000); // إعادة المحاولة
798
+ };
799
+
800
+ ws.onmessage = (event) => {
801
+ const data = JSON.parse(event.data);
802
+ handleWebSocketMessage(data);
803
+ };
804
+
805
+ ws.onerror = (error) => {
806
+ console.error('WebSocket Error:', error);
807
+ updateConnectionStatus(false);
808
+ };
809
+ }
810
+
811
+ function updateConnectionStatus(connected) {
812
+ const status = document.getElementById('connectionStatus');
813
+ const text = document.getElementById('connectionText');
814
+
815
+ if (connected) {
816
+ status.className = 'connection-status online';
817
+ text.textContent = 'متصل';
818
+ } else {
819
+ status.className = 'connection-status offline';
820
+ text.textContent = 'غير متصل';
821
+ }
822
+ }
823
+
824
+ function handleWebSocketMessage(data) {
825
+ if (data.type === 'new_page') {
826
+ addActivity(`🆕 صفحة جديدة: ${data.route} بواسطة ${data.username}`);
827
+ if (currentUser && data.username !== currentUser.username) {
828
+ showToast(`🆕 ${data.username} أنشأ صفحة جديدة!`, 'info');
829
+ }
830
+ } else if (data.type === 'user_online') {
831
+ addActivity(`🟢 ${data.username} دخل الآن`);
832
+ } else if (data.type === 'user_offline') {
833
+ addActivity(`🔴 ${data.username} خرج`);
834
+ }
835
+ }
836
+
837
+ function addActivity(message) {
838
+ const feed = document.getElementById('activityFeed');
839
+ const time = new Date().toLocaleTimeString('ar-EG', {hour: '2-digit', minute: '2-digit'});
840
+ const item = document.createElement('div');
841
+ item.className = 'activity-item';
842
+ item.innerHTML = `
843
+ <span>${message}</span>
844
+ <span class="activity-time">${time}</span>
845
+ `;
846
+ feed.insertBefore(item, feed.firstChild);
847
+ if (feed.children.length > 20) feed.removeChild(feed.lastChild);
848
+ }
507
849
 
508
850
  // Auth Functions
509
851
  async function register() {
@@ -512,12 +854,12 @@ async function register() {
512
854
  const password = document.getElementById('regPassword').value;
513
855
 
514
856
  if (!username || !email || !password) {
515
- showToast('أكمل جميع الحقول', 'error');
857
+ showToast('أكمل جميع الحقول', 'error');
516
858
  return;
517
859
  }
518
860
 
519
861
  if (password.length < 8) {
520
- showToast('كلمة المرور يجب أن تكون 8 أحرف على الأقل', 'error');
862
+ showToast('كلمة المرور يجب أن تكون 8 أحرف على الأقل', 'error');
521
863
  return;
522
864
  }
523
865
 
@@ -535,11 +877,10 @@ async function register() {
535
877
  hideModal('registerModal');
536
878
  showModal('apiKeyModal');
537
879
  } else {
538
- showToast(data.error || 'خطأ في التسجيل', 'error');
880
+ showToast(data.error || 'خطأ في التسجيل', 'error');
539
881
  }
540
882
  } catch (e) {
541
- showToast('خطأ في الاتصال بالسيرفر', 'error');
542
- console.error(e);
883
+ showToast('خطأ في الاتصال', 'error');
543
884
  }
544
885
  }
545
886
 
@@ -548,7 +889,7 @@ async function login() {
548
889
  const password = document.getElementById('loginPassword').value;
549
890
 
550
891
  if (!email || !password) {
551
- showToast('أدخل البريد وكلمة المرور', 'error');
892
+ showToast('أدخل البريد وكلمة المرور', 'error');
552
893
  return;
553
894
  }
554
895
 
@@ -565,25 +906,29 @@ async function login() {
565
906
  currentUser = data.user;
566
907
  hideModal('loginModal');
567
908
  loadDashboard();
568
- showToast('تم تسجيل الدخول', 'success');
909
+ showToast(`✅ أهلاً ${data.user.username}!`, 'success');
910
+
911
+ // WebSocket Auth
912
+ if (ws && ws.readyState === 1) {
913
+ ws.send(JSON.stringify({ type: 'auth', token: data.token }));
914
+ }
569
915
  } else {
570
- showToast(data.error || 'بيانات غير صحيحة', 'error');
916
+ showToast(data.error || 'بيانات غير صحيحة', 'error');
571
917
  }
572
918
  } catch (e) {
573
- showToast('خطأ في الاتصال', 'error');
574
- console.error(e);
919
+ showToast('خطأ في الاتصال', 'error');
575
920
  }
576
921
  }
577
922
 
578
923
  function logout() {
579
924
  localStorage.removeItem('noho_token');
580
925
  currentUser = null;
581
- location.reload();
926
+ showToast('👋 تم تسجيل الخروج', 'info');
927
+ setTimeout(() => location.reload(), 500);
582
928
  }
583
929
 
584
930
  function closeApiKeyModal() {
585
931
  hideModal('apiKeyModal');
586
- // تسجيل الدخول تلقائياً بعد التسجيل
587
932
  const email = document.getElementById('regEmail').value;
588
933
  const password = document.getElementById('regPassword').value;
589
934
  document.getElementById('loginEmail').value = email;
@@ -605,28 +950,25 @@ async function loadDashboard() {
605
950
  if (data.success) {
606
951
  currentUser = data.user;
607
952
 
608
- // تحديث الواجهة
609
953
  document.getElementById('guestView').classList.add('hidden');
610
954
  document.getElementById('dashboardView').classList.remove('hidden');
611
- document.getElementById('authSection').classList.add('hidden');
612
- document.getElementById('userSection').classList.remove('hidden');
613
- document.getElementById('usernameDisplay').textContent = data.user.username;
955
+ document.getElementById('guestMenu').classList.add('hidden');
956
+ document.getElementById('userMenu').classList.remove('hidden');
957
+ document.getElementById('usernameDisplay').textContent = `👤 ${data.user.username}`;
614
958
 
615
- // تحديث الإحصائيات
616
959
  document.getElementById('statPages').textContent = data.stats.totalPages;
617
- document.getElementById('statViews').textContent = data.stats.pages.reduce((a,b) => a + b.views, 0);
960
+ document.getElementById('statViews').textContent = data.stats.pages.reduce((a,b) => a + (b.views || 0), 0);
618
961
  document.getElementById('statRequests').textContent = data.stats.requests;
619
962
 
620
- // حفظ مفتاح API
621
963
  document.getElementById('apiKeyDisplay').dataset.fullKey = data.user.apiKey;
964
+ document.getElementById('apiKeyDisplay').textContent = '•'.repeat(30);
622
965
 
623
- // تحميل الصفحات
624
966
  loadPages();
625
967
  } else {
626
968
  localStorage.removeItem('noho_token');
627
969
  }
628
970
  } catch (e) {
629
- console.error('Load dashboard error:', e);
971
+ console.error('Error:', e);
630
972
  }
631
973
  }
632
974
 
@@ -643,41 +985,41 @@ async function loadPages() {
643
985
 
644
986
  const list = document.getElementById('pagesList');
645
987
  if (!data.pages || data.pages.length === 0) {
646
- list.innerHTML = '<li style="text-align: center; color: var(--text-secondary); padding: 40px;">لا توجد صفحات بعد</li>';
988
+ list.innerHTML = '<li style="text-align: center; color: var(--text-secondary); padding: 40px;">📭 لا توجد صفحات بعد</li>';
647
989
  return;
648
990
  }
649
991
 
650
992
  list.innerHTML = data.pages.map(page => `
651
993
  <li class="page-item">
652
994
  <div class="page-info">
653
- <h3>${page.shortRoute || page.route}</h3>
995
+ <h3>🔗 ${page.route}</h3>
654
996
  <div class="page-meta">
655
- المشاهدات: ${page.views} |
656
- التاريخ: ${new Date(page.createdAt).toLocaleDateString('ar-SA')}
657
- ${page.public ? '<span class="badge">عام</span>' : '<span class="badge warning">خاص</span>'}
997
+ 👁️ ${page.views || 0} مشاهدة | 📅 ${new Date(page.createdAt).toLocaleDateString('ar-SA')}
998
+ ${page.public ? '<span class="badge public">عام</span>' : '<span class="badge private">خاص</span>'}
658
999
  </div>
659
1000
  </div>
660
- <div>
1001
+ <div style="display: flex; gap: 8px;">
661
1002
  <a href="${API_URL}${page.route}" target="_blank">
662
- <button class="secondary">زيارة</button>
1003
+ <button class="btn small secondary">🌐 زيارة</button>
663
1004
  </a>
664
- <button class="danger" onclick="deletePage('${page.id}')">حذف</button>
1005
+ <button class="btn small danger" onclick="deletePage('${page.id}')">🗑️ حذف</button>
665
1006
  </div>
666
1007
  </li>
667
1008
  `).join('');
668
1009
  } catch (e) {
669
- console.error('Load pages error:', e);
1010
+ showToast(' خطأ في تحميل الصفحات', 'error');
670
1011
  }
671
1012
  }
672
1013
 
673
- // Page Functions
674
1014
  async function analyzeCode() {
675
1015
  const code = document.getElementById('pageCode').value;
676
- if (!code) return showToast('أدخل الكود أولاً', 'error');
1016
+ if (!code) return showToast('أدخل الكود أولاً', 'error');
677
1017
 
678
1018
  const token = localStorage.getItem('noho_token');
679
- if (!token) return showToast('سجل دخول أولاً', 'error');
1019
+ if (!token) return showToast('سجل دخول أولاً', 'error');
680
1020
 
1021
+ showToast('🔍 جاري التحليل...', 'info');
1022
+
681
1023
  try {
682
1024
  const res = await fetch(`${API_URL}/api/ai/analyze`, {
683
1025
  method: 'POST',
@@ -692,17 +1034,19 @@ async function analyzeCode() {
692
1034
  const resultDiv = document.getElementById('analysisResult');
693
1035
 
694
1036
  if (data.success) {
1037
+ const hasWarnings = data.warnings && data.warnings.length > 0;
695
1038
  resultDiv.innerHTML = `
696
- <div class="analysis-result ${data.warnings && data.warnings.length > 0 ? 'has-warnings' : ''}">
697
- <h4>نتيجة التحليل</h4>
698
- ${data.warnings ? data.warnings.map(w => `<div class="warning-item">⚠️ ${w}</div>`).join('') : ''}
699
- ${data.changes ? data.changes.map(c => `<div style="color: var(--success); margin: 4px 0;">✓ ${c}</div>`).join('') : ''}
700
- ${(!data.warnings || data.warnings.length === 0) ? '<div style="color: var(--success);">✓ الكود آمن</div>' : ''}
1039
+ <div class="analysis-box ${hasWarnings ? 'has-warnings' : ''}">
1040
+ <h4 style="margin-bottom: 10px;">🔍 نتيجة التحليل</h4>
1041
+ ${data.warnings ? data.warnings.map(w => `<div style="color: var(--warning); margin: 4px 0;">⚠️ ${w}</div>`).join('') : ''}
1042
+ ${data.changes ? data.changes.map(c => `<div style="color: var(--success); margin: 4px 0;">✅ ${c}</div>`).join('') : ''}
1043
+ ${(!data.warnings || data.warnings.length === 0) ? '<div style="color: var(--success);">✅ الكود آمن ولا يوجد تحذيرات</div>' : ''}
701
1044
  </div>
702
1045
  `;
1046
+ showToast('✅ تم التحليل', 'success');
703
1047
  }
704
1048
  } catch (e) {
705
- showToast('خطأ في التحليل', 'error');
1049
+ showToast('خطأ في التحليل', 'error');
706
1050
  }
707
1051
  }
708
1052
 
@@ -711,8 +1055,8 @@ async function createPage() {
711
1055
  const code = document.getElementById('pageCode').value;
712
1056
  const token = localStorage.getItem('noho_token');
713
1057
 
714
- if (!route || !code) return showToast('أكمل جميع الحقول', 'error');
715
- if (!token) return showToast('سجل دخول أولاً', 'error');
1058
+ if (!route || !code) return showToast('أكمل جميع الحقول', 'error');
1059
+ if (!token) return showToast('سجل دخول أولاً', 'error');
716
1060
 
717
1061
  try {
718
1062
  const res = await fetch(`${API_URL}/api/pages`, {
@@ -726,28 +1070,36 @@ async function createPage() {
726
1070
 
727
1071
  const data = await res.json();
728
1072
  if (data.success) {
729
- showToast('تم إنشاء الصفحة!', 'success');
1073
+ showToast('تم إنشاء الصفحة بنجاح!', 'success');
730
1074
  document.getElementById('pageRoute').value = '';
731
1075
  document.getElementById('pageCode').value = '';
732
1076
  document.getElementById('analysisResult').innerHTML = '';
733
1077
  loadPages();
1078
+
1079
+ // WebSocket broadcast
1080
+ if (ws && ws.readyState === 1) {
1081
+ ws.send(JSON.stringify({
1082
+ type: 'page_created',
1083
+ route: data.page.route,
1084
+ username: currentUser.username
1085
+ }));
1086
+ }
734
1087
  } else {
735
- showToast(data.error || 'خطأ في الإنشاء', 'error');
1088
+ showToast(data.error || 'خطأ في الإنشاء', 'error');
736
1089
  }
737
1090
  } catch (e) {
738
- showToast('خطأ في الاتصال', 'error');
739
- console.error(e);
1091
+ showToast('خطأ في الاتصال', 'error');
740
1092
  }
741
1093
  }
742
1094
 
743
1095
  async function generateWithAI() {
744
1096
  const prompt = document.getElementById('aiPrompt').value.trim();
745
- if (!prompt) return showToast('أدخل وصفاً', 'error');
1097
+ if (!prompt) return showToast('أدخل وصفاً', 'error');
746
1098
 
747
1099
  const token = localStorage.getItem('noho_token');
748
- if (!token) return showToast('سجل دخول أولاً', 'error');
1100
+ if (!token) return showToast('سجل دخول أولاً', 'error');
749
1101
 
750
- showToast('جاري التوليد...', 'success');
1102
+ showToast('🤖 جاري التوليد... قد يستغرق 10-20 ثانية', 'info');
751
1103
 
752
1104
  try {
753
1105
  const res = await fetch(`${API_URL}/api/ai/generate`, {
@@ -762,18 +1114,18 @@ async function generateWithAI() {
762
1114
  const data = await res.json();
763
1115
  if (data.success) {
764
1116
  document.getElementById('pageCode').value = data.code;
765
- document.getElementById('aiResult').innerHTML = '<div style="color: var(--success);">✓ تم توليد الكود! انتقل لتبويب "كود مخصص" لمراجعته</div>';
1117
+ showToast(' تم توليد الكود! راجعه في تبويب "كود مخصص"', 'success');
766
1118
  switchTab('codeTab', document.querySelectorAll('.tab')[0]);
767
1119
  } else {
768
- showToast(data.error || 'فشل التوليد', 'error');
1120
+ showToast(data.error || 'فشل التوليد', 'error');
769
1121
  }
770
1122
  } catch (e) {
771
- showToast('خطأ في الاتصال', 'error');
1123
+ showToast('خطأ في الاتصال', 'error');
772
1124
  }
773
1125
  }
774
1126
 
775
1127
  async function deletePage(pageId) {
776
- if (!confirm('هل أنت متأكد من الحذف؟')) return;
1128
+ if (!confirm('⚠️ هل أنت متأكد من حذف هذه الصفحة؟')) return;
777
1129
 
778
1130
  const token = localStorage.getItem('noho_token');
779
1131
  if (!token) return;
@@ -785,21 +1137,20 @@ async function deletePage(pageId) {
785
1137
  });
786
1138
 
787
1139
  if (res.ok) {
788
- showToast('تم الحذف', 'success');
1140
+ showToast('🗑️ تم الحذف', 'success');
789
1141
  loadPages();
790
1142
  }
791
1143
  } catch (e) {
792
- showToast('خطأ في الحذف', 'error');
1144
+ showToast('خطأ في الحذف', 'error');
793
1145
  }
794
1146
  }
795
1147
 
796
- // API Key Functions
797
1148
  function toggleApiKey() {
798
1149
  const display = document.getElementById('apiKeyDisplay');
799
1150
  const fullKey = display.dataset.fullKey;
800
1151
 
801
1152
  if (apiKeyVisible) {
802
- display.textContent = '••••••••••••';
1153
+ display.textContent = ''.repeat(30);
803
1154
  apiKeyVisible = false;
804
1155
  } else {
805
1156
  display.textContent = fullKey || 'غير متوفر';
@@ -809,21 +1160,21 @@ function toggleApiKey() {
809
1160
 
810
1161
  function copyApiKey() {
811
1162
  const key = document.getElementById('apiKeyDisplay').dataset.fullKey;
812
- if (!key) return showToast('لا يوجد مفتاح', 'error');
1163
+ if (!key) return showToast('لا يوجد مفتاح', 'error');
813
1164
 
814
1165
  navigator.clipboard.writeText(key).then(() => {
815
- showToast('تم النسخ!', 'success');
1166
+ showToast('📋 تم النسخ!', 'success');
816
1167
  });
817
1168
  }
818
1169
 
819
1170
  function copyNewApiKey() {
820
1171
  navigator.clipboard.writeText(storedApiKey).then(() => {
821
- showToast('تم النسخ!', 'success');
1172
+ showToast('📋 تم النسخ!', 'success');
822
1173
  });
823
1174
  }
824
1175
 
825
1176
  async function regenerateKey() {
826
- if (!confirm('سيتم إنشاء مفتاح جديد والقديم سيتوقف؟')) return;
1177
+ if (!confirm('⚠️ سيتم إنشاء مفتاح جديد والقديم سيتوقف؟')) return;
827
1178
 
828
1179
  const token = localStorage.getItem('noho_token');
829
1180
  if (!token) return;
@@ -836,11 +1187,11 @@ async function regenerateKey() {
836
1187
 
837
1188
  const data = await res.json();
838
1189
  if (data.success) {
839
- showToast('تم تجديد المفتاح', 'success');
1190
+ showToast('تم تجديد المفتاح', 'success');
840
1191
  loadDashboard();
841
1192
  }
842
1193
  } catch (e) {
843
- showToast('خطأ في التجديد', 'error');
1194
+ showToast('خطأ في التجديد', 'error');
844
1195
  }
845
1196
  }
846
1197
 
@@ -854,25 +1205,30 @@ function hideModal(id) {
854
1205
  }
855
1206
 
856
1207
  function switchTab(tabId, tabElement) {
857
- // إزالة active من جميع التبويبات
858
1208
  document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
859
1209
  document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
860
1210
 
861
- // إضافة active للمحدد
862
1211
  if (tabElement) tabElement.classList.add('active');
863
1212
  document.getElementById(tabId).classList.add('active');
864
1213
  }
865
1214
 
866
- function showToast(message, type = 'success') {
867
- const toast = document.getElementById('toast');
868
- toast.textContent = message;
869
- toast.className = `toast show ${type}`;
870
- setTimeout(() => toast.classList.remove('show'), 3000);
1215
+ function showToast(message, type = 'info') {
1216
+ const container = document.getElementById('toastContainer');
1217
+ const toast = document.createElement('div');
1218
+ toast.className = `toast ${type}`;
1219
+ toast.innerHTML = `<span>${message}</span>`;
1220
+ container.appendChild(toast);
1221
+
1222
+ setTimeout(() => {
1223
+ toast.style.animation = 'slideInLeft 0.3s reverse';
1224
+ setTimeout(() => toast.remove(), 300);
1225
+ }, 3000);
871
1226
  }
872
1227
 
873
1228
  // Init
874
1229
  window.onload = () => {
875
- // التحقق من تسجيل الدخول تلقائياً
1230
+ connectWebSocket();
1231
+
876
1232
  const token = localStorage.getItem('noho_token');
877
1233
  if (token) {
878
1234
  loadDashboard();
@@ -881,7 +1237,7 @@ window.onload = () => {
881
1237
 
882
1238
  // Close modals on outside click
883
1239
  window.onclick = (e) => {
884
- if (e.target.classList.contains('modal')) {
1240
+ if (e.target.classList.contains('modal-overlay')) {
885
1241
  e.target.classList.remove('active');
886
1242
  }
887
1243
  };