Files
4WDCSA.co.za/assets/js/notifications_panel.js
2025-12-16 22:40:24 +02:00

104 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* notifications.js - small admin notification panel
Requires jQuery. */
(function($){
function timeAgo(ts){
var seconds = Math.floor((Date.now() - (new Date(ts)).getTime())/1000);
if (seconds < 60) return seconds + 's ago';
var minutes = Math.floor(seconds/60);
if (minutes < 60) return minutes + 'm ago';
var hours = Math.floor(minutes/60);
if (hours < 24) return hours + 'h ago';
var days = Math.floor(hours/24);
return days + 'd ago';
}
function renderNotifications(list){
var $panel = $('#notif-panel');
$panel.empty();
if (!list || list.length === 0) {
$panel.append('<div class="notif-empty">No notifications</div>');
return;
}
list.forEach(function(n){
var actorAvatar = (n.data && n.data.actor_avatar) ? n.data.actor_avatar : 'assets/images/icons/user.png';
// Prefer the notification payload title (n.data.title). Do NOT fall back to the event string.
var title = (n.data && n.data.title) ? n.data.title : 'Notification';
var time = n.time_created || new Date().toISOString();
var read = (n.read_by && Array.isArray(n.read_by) && n.read_by.length>0);
var $item = $('<div class="notif-item" data-id="'+n.id+'">');
$item.append('<img class="notif-item-avatar" src="'+actorAvatar+'" alt="avatar">');
var $body = $('<div class="notif-item-body">');
$body.append('<div class="notif-item-title">'+escapeHtml(title)+'</div>');
$body.append('<div class="notif-item-meta">'+timeAgo(time)+'</div>');
$item.append($body);
$item.append('<button class="notif-close" title="Mark read">×</button>');
$panel.append($item);
});
}
function escapeHtml(str) {
if (!str) return '';
return String(str).replace(/[&<>"'`]/g, function(s){ return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":"&#39;",'`':'&#96;'}[s]; });
}
function fetchAndRender(adminId){
$.getJSON('/src/api/notifications.php', { action: 'fetch' }, function(resp){
if (resp && resp.success) {
renderNotifications(resp.notifications);
if (resp.unread_count && resp.unread_count > 0) {
$('#notif-badge').text(resp.unread_count).show();
} else {
$('#notif-badge').hide();
}
}
});
}
// Fetch only unread count (used on page load so badge shows without opening panel)
function fetchUnreadCount(){
$.getJSON('/src/api/notifications.php', { action: 'fetch' }, function(resp){
if (resp && resp.success) {
if (resp.unread_count && resp.unread_count > 0) {
$('#notif-badge').text(resp.unread_count).show();
} else {
$('#notif-badge').hide();
}
}
});
}
$(function(){
var $container = $('.notif-avatar-container');
if (!$container.length) return;
var adminId = $container.data('admin-id');
// ensure badge is populated on page load
fetchUnreadCount();
$container.on('click', function(e){
e.preventDefault();
$('#notif-panel').toggle();
if ($('#notif-panel').is(':visible')) fetchAndRender(adminId);
});
$(document).on('click', '.notif-close', function(e){
e.stopPropagation();
var $it = $(this).closest('.notif-item');
var id = $it.data('id');
if (!id) return;
$.post('/src/api/notifications.php', { action: 'mark_read', id: id }, function(resp){
if (resp && resp.success) {
$it.remove();
// refresh count
fetchAndRender(adminId);
}
}, 'json');
});
// click outside to close
$(document).on('click', function(e){
if (!$(e.target).closest('#notif-panel, .notif-avatar-container').length) {
$('#notif-panel').hide();
}
});
});
})(jQuery);