Update web/patients.html
Browse files- web/patients.html +100 -18
web/patients.html
CHANGED
|
@@ -186,6 +186,69 @@
|
|
| 186 |
margin-bottom: 1rem;
|
| 187 |
opacity: 0.5;
|
| 188 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
</style>
|
| 190 |
</head>
|
| 191 |
<body class="vertical light">
|
|
@@ -1100,28 +1163,47 @@ function createPatientCard(patient) {
|
|
| 1100 |
}
|
| 1101 |
|
| 1102 |
function showAlert(type, message) {
|
| 1103 |
-
|
| 1104 |
-
|
| 1105 |
-
|
| 1106 |
-
|
| 1107 |
-
|
| 1108 |
-
|
| 1109 |
-
|
| 1110 |
-
const
|
| 1111 |
-
<
|
| 1112 |
-
|
| 1113 |
-
|
| 1114 |
-
|
| 1115 |
-
|
| 1116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1117 |
</div>
|
|
|
|
|
|
|
| 1118 |
`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1119 |
|
| 1120 |
-
|
|
|
|
|
|
|
|
|
|
| 1121 |
|
| 1122 |
-
|
| 1123 |
-
$('.alert').first().alert('close');
|
| 1124 |
-
}, 5000);
|
| 1125 |
}
|
| 1126 |
|
| 1127 |
function handleLogout() {
|
|
|
|
| 186 |
margin-bottom: 1rem;
|
| 187 |
opacity: 0.5;
|
| 188 |
}
|
| 189 |
+
|
| 190 |
+
/* ── TOAST NOTIFICATIONS ─────────────────────────── */
|
| 191 |
+
#toast-container {
|
| 192 |
+
position: fixed;
|
| 193 |
+
bottom: 1.5rem;
|
| 194 |
+
right: 1.5rem;
|
| 195 |
+
z-index: 99999;
|
| 196 |
+
display: flex;
|
| 197 |
+
flex-direction: column;
|
| 198 |
+
gap: 0.6rem;
|
| 199 |
+
pointer-events: none;
|
| 200 |
+
}
|
| 201 |
+
.toast-notify {
|
| 202 |
+
display: flex;
|
| 203 |
+
align-items: flex-start;
|
| 204 |
+
gap: 12px;
|
| 205 |
+
min-width: 300px;
|
| 206 |
+
max-width: 380px;
|
| 207 |
+
padding: 14px 16px;
|
| 208 |
+
border-radius: 10px;
|
| 209 |
+
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
|
| 210 |
+
background: #fff;
|
| 211 |
+
border-left: 5px solid #ccc;
|
| 212 |
+
pointer-events: all;
|
| 213 |
+
opacity: 0;
|
| 214 |
+
transform: translateX(60px);
|
| 215 |
+
transition: opacity 0.3s ease, transform 0.3s ease;
|
| 216 |
+
position: relative;
|
| 217 |
+
overflow: hidden;
|
| 218 |
+
}
|
| 219 |
+
.toast-notify.show { opacity: 1; transform: translateX(0); }
|
| 220 |
+
.toast-notify.hide { opacity: 0; transform: translateX(60px); }
|
| 221 |
+
.toast-notify.toast-success { border-left-color: #28a745; }
|
| 222 |
+
.toast-notify.toast-error { border-left-color: #dc3545; }
|
| 223 |
+
.toast-notify.toast-warning { border-left-color: #ffc107; }
|
| 224 |
+
.toast-notify.toast-info { border-left-color: #17a2b8; }
|
| 225 |
+
.toast-icon { flex-shrink: 0; margin-top: 1px; }
|
| 226 |
+
.toast-success .toast-icon svg { stroke: #28a745; }
|
| 227 |
+
.toast-error .toast-icon svg { stroke: #dc3545; }
|
| 228 |
+
.toast-warning .toast-icon svg { stroke: #ffc107; }
|
| 229 |
+
.toast-info .toast-icon svg { stroke: #17a2b8; }
|
| 230 |
+
.toast-body { flex: 1; }
|
| 231 |
+
.toast-title { font-weight: 600; font-size: 0.875rem; margin-bottom: 2px; }
|
| 232 |
+
.toast-success .toast-title { color: #155724; }
|
| 233 |
+
.toast-error .toast-title { color: #721c24; }
|
| 234 |
+
.toast-warning .toast-title { color: #856404; }
|
| 235 |
+
.toast-info .toast-title { color: #0c5460; }
|
| 236 |
+
.toast-msg { font-size: 0.825rem; color: #555; line-height: 1.4; }
|
| 237 |
+
.toast-close {
|
| 238 |
+
background: none; border: none; font-size: 1rem;
|
| 239 |
+
line-height: 1; color: #aaa; cursor: pointer; padding: 0; flex-shrink: 0;
|
| 240 |
+
}
|
| 241 |
+
.toast-close:hover { color: #333; }
|
| 242 |
+
.toast-progress {
|
| 243 |
+
position: absolute;
|
| 244 |
+
bottom: 0; left: 0;
|
| 245 |
+
height: 3px;
|
| 246 |
+
border-radius: 0 0 0 10px;
|
| 247 |
+
}
|
| 248 |
+
.toast-success .toast-progress { background: #28a745; }
|
| 249 |
+
.toast-error .toast-progress { background: #dc3545; }
|
| 250 |
+
.toast-warning .toast-progress { background: #ffc107; }
|
| 251 |
+
.toast-info .toast-progress { background: #17a2b8; }
|
| 252 |
</style>
|
| 253 |
</head>
|
| 254 |
<body class="vertical light">
|
|
|
|
| 1163 |
}
|
| 1164 |
|
| 1165 |
function showAlert(type, message) {
|
| 1166 |
+
if (!document.getElementById('toast-container')) {
|
| 1167 |
+
const container = document.createElement('div');
|
| 1168 |
+
container.id = 'toast-container';
|
| 1169 |
+
document.body.appendChild(container);
|
| 1170 |
+
}
|
| 1171 |
+
const container = document.getElementById('toast-container');
|
| 1172 |
+
|
| 1173 |
+
const icons = {
|
| 1174 |
+
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>',
|
| 1175 |
+
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>',
|
| 1176 |
+
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>',
|
| 1177 |
+
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>'
|
| 1178 |
+
};
|
| 1179 |
+
const titles = { success: 'Éxito', error: 'Error', warning: 'Advertencia', info: 'Información' };
|
| 1180 |
+
const duration = type === 'error' ? 6000 : 4000;
|
| 1181 |
+
|
| 1182 |
+
const toast = document.createElement('div');
|
| 1183 |
+
toast.className = `toast-notify toast-${type}`;
|
| 1184 |
+
toast.innerHTML = `
|
| 1185 |
+
<div class="toast-icon">${icons[type] || icons.info}</div>
|
| 1186 |
+
<div class="toast-body">
|
| 1187 |
+
<div class="toast-title">${titles[type] || 'Aviso'}</div>
|
| 1188 |
+
<div class="toast-msg">${message}</div>
|
| 1189 |
</div>
|
| 1190 |
+
<button class="toast-close" onclick="this.closest('.toast-notify').remove()">×</button>
|
| 1191 |
+
<div class="toast-progress" style="width:100%;transition:width ${duration}ms linear;"></div>
|
| 1192 |
`;
|
| 1193 |
+
container.appendChild(toast);
|
| 1194 |
+
|
| 1195 |
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
| 1196 |
+
toast.classList.add('show');
|
| 1197 |
+
const bar = toast.querySelector('.toast-progress');
|
| 1198 |
+
if (bar) requestAnimationFrame(() => { bar.style.width = '0%'; });
|
| 1199 |
+
}));
|
| 1200 |
|
| 1201 |
+
const timer = setTimeout(() => {
|
| 1202 |
+
toast.classList.add('hide');
|
| 1203 |
+
toast.addEventListener('transitionend', () => toast.remove(), { once: true });
|
| 1204 |
+
}, duration);
|
| 1205 |
|
| 1206 |
+
toast.querySelector('.toast-close').addEventListener('click', () => clearTimeout(timer));
|
|
|
|
|
|
|
| 1207 |
}
|
| 1208 |
|
| 1209 |
function handleLogout() {
|