Files
orderPRO/public/assets/js/modules/notifications.js
Jacek Pyziak 360eef128d feat(121+122): smsplanet conversation, notifications, default footer
Phase 121 — SMSPLANET Conversation + Notifications:
- migration 20260512_000110 adds smsplanet conversation + notifications tables
- src/Modules/Sms (SmsConversationService, SmsMessageRepository, SmsplanetWebhookController)
- src/Modules/Notifications (Repository, Controller, ApiController)
- order SMS tab, notification center, sender mode, inbound webhook
- public notifications.js + layouts/app.php integration

Phase 122 — SMSPLANET Default SMS Footer:
- migration 20260512_000111 adds smsplanet_integration_settings.default_footer
- footer appended to test SMS and order SMS, validated against 918 char limit
- settings textarea + compact order SMS note when footer configured

Bundled (could not split per-phase without hunk staging):
- routes/web.php (also carries Phase 118 fakturownia redirects)
- DOCS/{ARCHITECTURE,DB_SCHEMA,TECH_CHANGELOG}.md (118 + 121 + 122 entries)
- .paul/codebase/{architecture,db_schema,tech_changelog}.md (118 + 121 + 122)
- .paul/STATE.md, ROADMAP.md, changelog/2026-05-12.md (UNIFY closure)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-12 20:37:41 +02:00

82 lines
2.4 KiB
JavaScript

(function () {
var badge = document.getElementById('js-notification-badge');
var button = document.getElementById('js-notification-button');
if (!badge || !button || typeof fetch !== 'function') return;
var seenKey = 'orderproSeenNotificationIds';
var seenIds = loadSeenIds();
var permissionAsked = false;
function loadSeenIds() {
try {
return JSON.parse(localStorage.getItem(seenKey) || '[]').map(String);
} catch (e) {
return [];
}
}
function saveSeenIds() {
try {
localStorage.setItem(seenKey, JSON.stringify(seenIds.slice(-100)));
} catch (e) {}
}
function updateBadge(count) {
var value = Math.max(0, Number(count) || 0);
badge.textContent = value > 99 ? '99+' : String(value);
badge.hidden = value === 0;
}
function requestPermission() {
if (permissionAsked || !('Notification' in window) || Notification.permission !== 'default') {
return Promise.resolve();
}
permissionAsked = true;
return Notification.requestPermission().catch(function () {});
}
function showBrowserNotification(item) {
var id = String(item.id || '');
if (id === '' || seenIds.indexOf(id) !== -1) return;
seenIds.push(id);
saveSeenIds();
if (!('Notification' in window) || Notification.permission !== 'granted') return;
var nativeNotification = new Notification(item.title || 'orderPRO', {
body: item.body || '',
tag: 'orderpro-notification-' + id
});
nativeNotification.onclick = function () {
window.focus();
if (item.target_url) {
window.location.href = item.target_url;
}
};
}
function poll() {
fetch('/api/notifications/unread', { credentials: 'same-origin' })
.then(function (response) { return response.ok ? response.json() : null; })
.then(function (data) {
if (!data || !data.ok) return;
updateBadge(data.count);
if (Array.isArray(data.items)) {
data.items.slice().reverse().forEach(showBrowserNotification);
}
})
.catch(function () {});
}
button.addEventListener('click', function (event) {
if ('Notification' in window && Notification.permission === 'default') {
event.preventDefault();
requestPermission().finally(function () {
window.location.href = button.href;
});
}
});
poll();
window.setInterval(poll, 30000);
})();