110 lines
3.7 KiB
JavaScript
110 lines
3.7 KiB
JavaScript
/* Kundenansicht (Mitarbeiter oeffnet das Portal eines Kunden aus dem Ticketsystem).
|
|
Ein Browser haelt nur eine Portal-Sitzung: oeffnet jemand Kunde B, waehrend Kunde A noch in einem Tab offen ist,
|
|
darf der Tab von A keine Daten von B laden. Deshalb traegt jede eigene /api-Anfrage die Kennung der Ansicht
|
|
(X-Kundenansicht); der Server antwortet bei fremder Kennung mit 409 view_replaced, nach 2 Stunden mit
|
|
401 view_expired. Dann zeigt dieses Skript nur noch einen Hinweis. Klassisches Skript, vor app.js laden. */
|
|
(function () {
|
|
'use strict'
|
|
|
|
const HEADER = 'X-Kundenansicht'
|
|
let viewId = ''
|
|
|
|
function isOwnApi(url) {
|
|
try {
|
|
const u = new URL(String(url), window.location.href)
|
|
return u.origin === window.location.origin && u.pathname.startsWith('/api/')
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function overlay(text) {
|
|
const existing = document.getElementById('kb-view-overlay-text')
|
|
if (existing) {
|
|
existing.textContent = text
|
|
return
|
|
}
|
|
const box = document.createElement('div')
|
|
box.id = 'kb-view-overlay'
|
|
box.className = 'kb-view-overlay'
|
|
box.setAttribute('role', 'alertdialog')
|
|
box.setAttribute('aria-modal', 'true')
|
|
const card = document.createElement('div')
|
|
card.className = 'kb-view-overlay-card'
|
|
const p = document.createElement('p')
|
|
p.id = 'kb-view-overlay-text'
|
|
p.textContent = text
|
|
const button = document.createElement('button')
|
|
button.type = 'button'
|
|
button.textContent = 'Tab schließen'
|
|
button.addEventListener('click', () => window.close())
|
|
card.appendChild(p)
|
|
card.appendChild(button)
|
|
box.appendChild(card)
|
|
document.body.appendChild(box)
|
|
}
|
|
|
|
function showReplaced(kunde) {
|
|
overlay(`Diese Ansicht wurde durch die Kundenansicht von ${kunde || 'einem anderen Kunden'} ersetzt.`)
|
|
}
|
|
|
|
function showExpired() {
|
|
overlay('Die Kundenansicht ist abgelaufen. Bitte im Ticketsystem erneut öffnen.')
|
|
}
|
|
|
|
function inspect(status, body) {
|
|
if (status === 409 && body && body.code === 'view_replaced') showReplaced(body.kunde)
|
|
else if (status === 401 && body && body.code === 'view_expired') showExpired()
|
|
}
|
|
|
|
const originalFetch = window.fetch.bind(window)
|
|
window.fetch = async function (input, init) {
|
|
const url = typeof input === 'string' ? input : (input && input.url) || String(input)
|
|
const own = isOwnApi(url)
|
|
let options = init || {}
|
|
if (viewId && own) {
|
|
const headers = new Headers(options.headers || (typeof input !== 'string' && input && input.headers) || {})
|
|
headers.set(HEADER, viewId)
|
|
options = { ...options, headers }
|
|
}
|
|
const response = await originalFetch(input, options)
|
|
if (own && (response.status === 409 || response.status === 401)) {
|
|
response.clone().json().then((body) => inspect(response.status, body)).catch(() => {})
|
|
}
|
|
return response
|
|
}
|
|
|
|
if (window.XMLHttpRequest) {
|
|
const proto = window.XMLHttpRequest.prototype
|
|
const open = proto.open
|
|
const send = proto.send
|
|
proto.open = function (method, url, ...rest) {
|
|
this.kbOwnApi = isOwnApi(url)
|
|
return open.call(this, method, url, ...rest)
|
|
}
|
|
proto.send = function (...args) {
|
|
if (this.kbOwnApi) {
|
|
if (viewId) this.setRequestHeader(HEADER, viewId)
|
|
this.addEventListener('load', () => {
|
|
if (this.status !== 409 && this.status !== 401) return
|
|
try {
|
|
inspect(this.status, JSON.parse(this.responseText))
|
|
} catch {
|
|
/* keine JSON-Antwort */
|
|
}
|
|
})
|
|
}
|
|
return send.apply(this, args)
|
|
}
|
|
}
|
|
|
|
window.KbView = {
|
|
setId(id) {
|
|
viewId = String(id || '')
|
|
},
|
|
id: () => viewId,
|
|
showReplaced,
|
|
showExpired,
|
|
}
|
|
})()
|