Akira-Kurusu commited on
Commit
deeb3ee
·
verified ·
1 Parent(s): 1570eec

Update web/users.html

Browse files
Files changed (1) hide show
  1. web/users.html +101 -17
web/users.html CHANGED
@@ -20,6 +20,70 @@
20
  );
21
  })();
22
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  </head>
24
 
25
  <body class="vertical light">
@@ -392,27 +456,47 @@
392
  }
393
 
394
  function showAlert(type, message) {
395
- const alertClass = {
396
- 'success': 'alert-success',
397
- 'error': 'alert-danger',
398
- 'warning': 'alert-warning',
399
- 'info': 'alert-info'
400
- }[type] || 'alert-info';
401
-
402
- const alertHtml = `
403
- <div class="alert ${alertClass} alert-dismissible fade show" role="alert">
404
- ${message}
405
- <button type="button" class="close" data-dismiss="alert">
406
- <span aria-hidden="true">&times;</span>
407
- </button>
 
 
 
 
 
 
 
 
 
 
408
  </div>
 
 
409
  `;
 
410
 
411
- $('.container-fluid').prepend(alertHtml);
 
 
 
 
412
 
413
- setTimeout(() => {
414
- $('.alert').first().alert('close');
415
- }, 5000);
 
 
 
416
  }
417
  </script>
418
 
 
20
  );
21
  })();
22
  </script>
23
+ <style>
24
+ /* ── TOAST NOTIFICATIONS ─────────────────────────── */
25
+ #toast-container {
26
+ position: fixed;
27
+ bottom: 1.5rem;
28
+ right: 1.5rem;
29
+ z-index: 99999;
30
+ display: flex;
31
+ flex-direction: column;
32
+ gap: 0.6rem;
33
+ pointer-events: none;
34
+ }
35
+ .toast-notify {
36
+ display: flex;
37
+ align-items: flex-start;
38
+ gap: 12px;
39
+ min-width: 300px;
40
+ max-width: 380px;
41
+ padding: 14px 16px;
42
+ border-radius: 10px;
43
+ box-shadow: 0 8px 24px rgba(0,0,0,0.15);
44
+ background: #fff;
45
+ border-left: 5px solid #ccc;
46
+ pointer-events: all;
47
+ opacity: 0;
48
+ transform: translateX(60px);
49
+ transition: opacity 0.3s ease, transform 0.3s ease;
50
+ position: relative;
51
+ overflow: hidden;
52
+ }
53
+ .toast-notify.show { opacity: 1; transform: translateX(0); }
54
+ .toast-notify.hide { opacity: 0; transform: translateX(60px); }
55
+ .toast-notify.toast-success { border-left-color: #28a745; }
56
+ .toast-notify.toast-error { border-left-color: #dc3545; }
57
+ .toast-notify.toast-warning { border-left-color: #ffc107; }
58
+ .toast-notify.toast-info { border-left-color: #17a2b8; }
59
+ .toast-icon { flex-shrink: 0; margin-top: 1px; }
60
+ .toast-success .toast-icon svg { stroke: #28a745; }
61
+ .toast-error .toast-icon svg { stroke: #dc3545; }
62
+ .toast-warning .toast-icon svg { stroke: #ffc107; }
63
+ .toast-info .toast-icon svg { stroke: #17a2b8; }
64
+ .toast-body { flex: 1; }
65
+ .toast-title { font-weight: 600; font-size: 0.875rem; margin-bottom: 2px; }
66
+ .toast-success .toast-title { color: #155724; }
67
+ .toast-error .toast-title { color: #721c24; }
68
+ .toast-warning .toast-title { color: #856404; }
69
+ .toast-info .toast-title { color: #0c5460; }
70
+ .toast-msg { font-size: 0.825rem; color: #555; line-height: 1.4; }
71
+ .toast-close {
72
+ background: none; border: none; font-size: 1rem;
73
+ line-height: 1; color: #aaa; cursor: pointer; padding: 0; flex-shrink: 0;
74
+ }
75
+ .toast-close:hover { color: #333; }
76
+ .toast-progress {
77
+ position: absolute;
78
+ bottom: 0; left: 0;
79
+ height: 3px;
80
+ border-radius: 0 0 0 10px;
81
+ }
82
+ .toast-success .toast-progress { background: #28a745; }
83
+ .toast-error .toast-progress { background: #dc3545; }
84
+ .toast-warning .toast-progress { background: #ffc107; }
85
+ .toast-info .toast-progress { background: #17a2b8; }
86
+ </style>
87
  </head>
88
 
89
  <body class="vertical light">
 
456
  }
457
 
458
  function showAlert(type, message) {
459
+ if (!document.getElementById('toast-container')) {
460
+ const container = document.createElement('div');
461
+ container.id = 'toast-container';
462
+ document.body.appendChild(container);
463
+ }
464
+ const container = document.getElementById('toast-container');
465
+
466
+ const icons = {
467
+ success: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>',
468
+ error: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>',
469
+ warning: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
470
+ info: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>'
471
+ };
472
+ const titles = { success: 'Éxito', error: 'Error', warning: 'Advertencia', info: 'Información' };
473
+ const duration = type === 'error' ? 6000 : 4000;
474
+
475
+ const toast = document.createElement('div');
476
+ toast.className = `toast-notify toast-${type}`;
477
+ toast.innerHTML = `
478
+ <div class="toast-icon">${icons[type] || icons.info}</div>
479
+ <div class="toast-body">
480
+ <div class="toast-title">${titles[type] || 'Aviso'}</div>
481
+ <div class="toast-msg">${message}</div>
482
  </div>
483
+ <button class="toast-close" onclick="this.closest('.toast-notify').remove()">&times;</button>
484
+ <div class="toast-progress" style="width:100%;transition:width ${duration}ms linear;"></div>
485
  `;
486
+ container.appendChild(toast);
487
 
488
+ requestAnimationFrame(() => requestAnimationFrame(() => {
489
+ toast.classList.add('show');
490
+ const bar = toast.querySelector('.toast-progress');
491
+ if (bar) requestAnimationFrame(() => { bar.style.width = '0%'; });
492
+ }));
493
 
494
+ const timer = setTimeout(() => {
495
+ toast.classList.add('hide');
496
+ toast.addEventListener('transitionend', () => toast.remove(), { once: true });
497
+ }, duration);
498
+
499
+ toast.querySelector('.toast-close').addEventListener('click', () => clearTimeout(timer));
500
  }
501
  </script>
502