// Shared controls for the customer page and service workspace. var SupportUI = { initAttachments(container, label){ container.addEventListener('error', function(event){ const image = event.target; if(!image || image.tagName !== 'IMG' || !image.classList.contains('message-image') || image.dataset.unavailable) return; image.dataset.unavailable = '1'; image.hidden = true; const hint = document.createElement('span'); hint.className = 'attachment-unavailable'; hint.textContent = typeof label === 'function' ? label() : label; image.after(hint); }, true); }, async enableNotifications(button){ if(!('Notification' in window)) return; try{ const permission = await Notification.requestPermission(); button.setAttribute('aria-pressed', String(permission === 'granted')); }catch(error){} }, notify(title, body){ if(!document.hidden || !('Notification' in window) || Notification.permission !== 'granted') return; try{ const notice = new Notification(title, {body:body}); notice.onclick = function(){ window.focus(); notice.close(); }; }catch(error){} }, addRating(container, sid, labels){ const request = labels.fetch || fetch; const box = document.createElement('section'); box.className = 'service-rating'; const question = document.createElement('p'); question.textContent = labels.question; box.appendChild(question); const choices = document.createElement('div'); choices.className = 'rating-choices'; box.appendChild(choices); const status = document.createElement('p'); status.setAttribute('role', 'status'); box.appendChild(status); let sending = false; [1, 2, 3, 4, 5].forEach(function(stars){ const button = document.createElement('button'); button.type = 'button'; button.textContent = '★'; button.setAttribute('aria-label', stars+' / 5'); button.onmouseenter = function(){ Array.from(choices.children).forEach(function(item, index){ item.classList.toggle('selected', index < stars); }); }; button.onclick = async function(){ if(sending) return; sending = true; Array.from(choices.children).forEach(function(item){ item.disabled = true; }); try{ const response = await request('/api/rate', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({sid:sid, stars:stars, comment:''})}); const data = await response.json(); if(!response.ok || !data.ok) throw new Error('Rating failed'); box.textContent = labels.thanks+' '+ '★'.repeat(stars); }catch(error){ sending = false; status.textContent = labels.failed; Array.from(choices.children).forEach(function(item){ item.disabled = false; }); } }; choices.appendChild(button); }); container.appendChild(box); return box; }, initEmoji(button, panel, input, onInsert){ const emojis = ['😊', '😂', '👍', '❤️', '🙏', '😅', '😢', '🎉', '📦', '🚚']; function close(){ panel.hidden = true; button.setAttribute('aria-expanded', 'false'); } button.onclick = function(){ panel.hidden = !panel.hidden; button.setAttribute('aria-expanded', String(!panel.hidden)); }; emojis.forEach(function(emoji){ const choice = document.createElement('button'); choice.type = 'button'; choice.textContent = emoji; choice.onclick = function(){ const start = input.selectionStart == null ? input.value.length : input.selectionStart; const end = input.selectionEnd == null ? start : input.selectionEnd; input.value = input.value.slice(0, start) + emoji + input.value.slice(end); close(); input.focus({preventScroll:true}); input.setSelectionRange(start + emoji.length, start + emoji.length); if(onInsert) onInsert(); }; panel.appendChild(choice); }); ['pointerdown','mousedown'].forEach(function(name){ document.addEventListener(name, function(event){ if(!panel.hidden && !panel.contains(event.target) && !button.contains(event.target)) close(); }); }); document.addEventListener('keydown', function(event){ if(event.key === 'Escape' && !panel.hidden){ close(); button.focus({preventScroll:true}); } }); return {close:close}; } };