Skip to content
This repository was archived by the owner on Jul 28, 2026. It is now read-only.

Commit db69d9f

Browse files
committed
feat: enhanced notification toasts with icons, types (info/warning), click-to-dismiss, and admin support
1 parent 9e87c8c commit db69d9f

3 files changed

Lines changed: 59 additions & 2 deletions

File tree

public/css/style.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,31 @@ tbody tr:hover {
12251225
color: var(--accent-red);
12261226
}
12271227

1228+
.toast-info {
1229+
background: rgba(6, 182, 212, 0.15);
1230+
border: 1px solid rgba(6, 182, 212, 0.3);
1231+
color: var(--accent-cyan);
1232+
}
1233+
1234+
.toast-warning {
1235+
background: rgba(245, 158, 11, 0.15);
1236+
border: 1px solid rgba(245, 158, 11, 0.3);
1237+
color: var(--accent-orange);
1238+
}
1239+
1240+
.toast-icon {
1241+
width: 18px;
1242+
height: 18px;
1243+
flex-shrink: 0;
1244+
}
1245+
1246+
.toast {
1247+
display: flex;
1248+
align-items: center;
1249+
gap: 10px;
1250+
cursor: pointer;
1251+
}
1252+
12281253
@keyframes slideUp {
12291254
from { opacity: 0; transform: translateY(20px); }
12301255
to { opacity: 1; transform: translateY(0); }

public/js/admin.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,30 @@ async function adminApi(path, options = {}) {
4444

4545
function $a(sel) { return document.querySelector(sel); }
4646

47+
const ADMIN_TOAST_ICONS = {
48+
success: '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>',
49+
error: '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M15 9l-6 6M9 9l6 6"/></svg>',
50+
info: '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>',
51+
warning: '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>',
52+
};
53+
54+
function showAdminToast(message, type = 'success') {
55+
const container = $a('#admin-toast-container') || (() => {
56+
const el = document.createElement('div');
57+
el.id = 'admin-toast-container';
58+
el.className = 'toast-container';
59+
document.body.appendChild(el);
60+
return el;
61+
})();
62+
63+
const toast = document.createElement('div');
64+
toast.className = `toast toast-${type}`;
65+
toast.innerHTML = (ADMIN_TOAST_ICONS[type] || ADMIN_TOAST_ICONS.success) + `<span>${message}</span>`;
66+
toast.addEventListener('click', () => toast.remove());
67+
container.appendChild(toast);
68+
setTimeout(() => { if (toast.parentNode) toast.remove(); }, 4000);
69+
}
70+
4771
function adminNavigateTo(page) {
4872
const parts = page.split('/');
4973
const basePage = parts[0] || 'servers';

public/js/app.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,13 @@ async function api(path, options = {}) {
174174
return data;
175175
}
176176

177+
const TOAST_ICONS = {
178+
success: '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>',
179+
error: '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M15 9l-6 6M9 9l6 6"/></svg>',
180+
info: '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>',
181+
warning: '<svg class="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>',
182+
};
183+
177184
function showToast(message, type = 'success') {
178185
const container = $('#toast-container') || (() => {
179186
const el = document.createElement('div');
@@ -185,9 +192,10 @@ function showToast(message, type = 'success') {
185192

186193
const toast = document.createElement('div');
187194
toast.className = `toast toast-${type}`;
188-
toast.textContent = message;
195+
toast.innerHTML = (TOAST_ICONS[type] || TOAST_ICONS.success) + `<span>${message}</span>`;
196+
toast.addEventListener('click', () => toast.remove());
189197
container.appendChild(toast);
190-
setTimeout(() => toast.remove(), 4000);
198+
setTimeout(() => { if (toast.parentNode) toast.remove(); }, 4000);
191199
}
192200

193201
function showError(form, message) {

0 commit comments

Comments
 (0)