system notification

This commit is contained in:
Thanakorn S
2026-05-26 13:26:57 +07:00
parent d93abb0b81
commit 5687b562fb
3 changed files with 162 additions and 0 deletions
+81
View File
@@ -208,6 +208,25 @@ $_usage_full = $_usage_max_pct >= 100;
</li>
<?php endif; ?>
<!-- Bell notifications -->
<li class="dropdown">
<a class="position-relative btn-icon btn-sm btn-light btn rounded-circle" href="#" role="button"
data-bs-toggle="dropdown" aria-expanded="false" onclick="clear_notif_badge()">
<i class="ti ti-bell" style="font-size:18px;"></i>
<span id="notif_badge" class="badge bg-danger position-absolute top-0 start-100 translate-middle rounded-pill d-none"
style="font-size:9px;min-width:16px;padding:2px 4px;"></span>
</a>
<div class="dropdown-menu dropdown-menu-end p-0" style="min-width:300px;">
<div class="px-3 py-2 border-bottom d-flex align-items-center justify-content-between">
<span class="fw-semibold small">Notifications</span>
<a href="javascript:;" class="small text-muted" onclick="clear_all_notifs()">Clear all</a>
</div>
<div id="notif_list" style="max-height:360px;overflow-y:auto;">
<div class="text-center text-muted small py-4">No notifications</div>
</div>
</div>
</li>
<!-- User dropdown -->
<li class="ms-3 dropdown">
<a class="position-relative btn-icon btn-sm btn-primary btn rounded-circle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
@@ -258,6 +277,68 @@ var prop_limit = '<?php echo $prop['limit']; ?>';
var user_role = document.getElementById('session-context')?.dataset.role || 'viewer';
var company_id = parseInt(document.getElementById('session-context')?.dataset.companyId || '0', 10);
// ── Bell notifications ────────────────────────────────────────────────────────
var _notif_items = [];
var _notif_unread = 0;
var _notif_icon_map = {
success: 'ti-circle-check text-success',
info: 'ti-info-circle text-info',
warning: 'ti-alert-triangle text-warning',
danger: 'ti-circle-x text-danger',
};
function add_notification(title, message, type) {
var time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
_notif_items.unshift({ title: title, message: message, type: type || 'info', time: time });
if (_notif_items.length > 20) _notif_items.pop();
_notif_unread++;
_render_notif_badge();
_render_notif_list();
}
function clear_notif_badge() {
_notif_unread = 0;
_render_notif_badge();
}
function clear_all_notifs() {
_notif_items = [];
_notif_unread = 0;
_render_notif_badge();
_render_notif_list();
}
function _render_notif_badge() {
var badge = document.getElementById('notif_badge');
if (!badge) return;
if (_notif_unread > 0) {
badge.textContent = _notif_unread > 99 ? '99+' : _notif_unread;
badge.classList.remove('d-none');
} else {
badge.classList.add('d-none');
}
}
function _render_notif_list() {
var el = document.getElementById('notif_list');
if (!el) return;
if (_notif_items.length === 0) {
el.innerHTML = '<div class="text-center text-muted small py-4">No notifications</div>';
return;
}
el.innerHTML = _notif_items.map(function(n) {
var icon = _notif_icon_map[n.type] || _notif_icon_map.info;
return '<div class="d-flex align-items-start gap-2 px-3 py-2 border-bottom">' +
'<i class="ti ' + icon + ' fs-5 flex-shrink-0 mt-1"></i>' +
'<div class="flex-grow-1 overflow-hidden">' +
(n.title ? '<div class="fw-semibold small text-truncate">' + n.title + '</div>' : '') +
(n.message ? '<div class="small text-muted text-truncate">' + n.message + '</div>' : '') +
'<div class="small text-muted opacity-75 mt-1">' + n.time + '</div>' +
'</div></div>';
}).join('');
}
// ── Log out ───────────────────────────────────────────────────────────────────
function log_out() {
return ajax_request({