Team-API des Kundenportals und Oeffnen im neuen Tab ohne Pop-up-Sperre
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
52968b3017
commit
8c7a3fcced
3 files changed
+99
No files matched your search
@@ -0,0 +1,16 @@
|
|||||||
|
// Einmal-Links des Kundenportals (Kundenansicht, Preview) in einem neuen Tab oeffnen. Der Tab geht synchron im Klick
|
||||||
|
// auf (sonst blockiert der Browser das Pop-up), die Adresse kommt erst nach der Anfrage ans Portal. Rein (ohne React),
|
||||||
|
// damit unter Node testbar.
|
||||||
|
|
||||||
|
/** getUrl: () => Promise<string>. Fehler schliessen den leeren Tab wieder und werden weitergereicht. */
|
||||||
|
export async function openInNewTab(getUrl, { win = globalThis.window } = {}) {
|
||||||
|
const tab = win.open('', '_blank')
|
||||||
|
if (!tab) throw new Error('Neuer Tab wurde vom Browser blockiert.')
|
||||||
|
tab.opener = null
|
||||||
|
try {
|
||||||
|
tab.location.href = await getUrl()
|
||||||
|
} catch (err) {
|
||||||
|
tab.close()
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { account } from './appwrite'
|
||||||
|
import { createFinanceRequest } from './financeRequest'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client fuer den Team-Eingang des Kundenportals (/api/team): alle Mitarbeiter, Appwrite-JWT (Bearer).
|
||||||
|
* Anfrage-Kern (JWT-Cache, Wiederholung bei 401, deutsche Fehlertexte) wie bei den Finanzen: financeRequest.js.
|
||||||
|
*/
|
||||||
|
const PROJECT_ADMIN_URL =
|
||||||
|
import.meta.env.VITE_PROJECT_ADMIN_URL || 'https://project.webklar.com'
|
||||||
|
|
||||||
|
const request = createFinanceRequest({ baseUrl: `${PROJECT_ADMIN_URL}/api/team`, createJWT: () => account.createJWT() })
|
||||||
|
|
||||||
|
const enc = encodeURIComponent
|
||||||
|
|
||||||
|
/** { hasAccess, lastLoginAt } */
|
||||||
|
export const getCustomerPortalStatus = (customerId) => request(`/customers/${enc(customerId)}/portal`)
|
||||||
|
|
||||||
|
/** { entries: [{ id, at, staffName, action, detail }] }, neueste zuerst */
|
||||||
|
export const getCustomerViewLog = (customerId) => request(`/customers/${enc(customerId)}/protokoll`)
|
||||||
|
|
||||||
|
/** Einmal-Link (60 s) in die Kundenansicht des Portals */
|
||||||
|
export async function createKundenansichtLink(customerId) {
|
||||||
|
const { openUrl } = await request(`/customers/${enc(customerId)}/kundenansicht`, { method: 'POST', body: {} })
|
||||||
|
return openUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Einmal-Link fuer die Preview eines Projekts */
|
||||||
|
export async function createPreviewLink({ subdomain, previewUrl }) {
|
||||||
|
const { openUrl } = await request('/preview-link', { method: 'POST', body: { subdomain, previewUrl } })
|
||||||
|
return openUrl
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import test from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { loadModule } from './load.mjs'
|
||||||
|
|
||||||
|
// Neuer Tab fuer Einmal-Links (Kundenansicht, Preview): der Tab muss im Klick synchron aufgehen, sonst blockiert
|
||||||
|
// der Browser das Pop-up; die Adresse kommt erst nach der Anfrage ans Portal.
|
||||||
|
const { openInNewTab } = await loadModule('src/lib/openInNewTab.js')
|
||||||
|
|
||||||
|
function fakeWindow() {
|
||||||
|
const order = []
|
||||||
|
const tab = { location: {}, opener: 'ticketsystem', close: () => order.push('close') }
|
||||||
|
const win = {
|
||||||
|
open: (...args) => {
|
||||||
|
order.push('open')
|
||||||
|
win.openArgs = args
|
||||||
|
return tab
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return { win, tab, order }
|
||||||
|
}
|
||||||
|
|
||||||
|
test('open laeuft vor getUrl, danach wird die Adresse gesetzt', async () => {
|
||||||
|
const { win, tab, order } = fakeWindow()
|
||||||
|
await openInNewTab(async () => {
|
||||||
|
order.push('getUrl')
|
||||||
|
return 'https://project.webklar.com/api/auth/kundenansicht?token=abc'
|
||||||
|
}, { win })
|
||||||
|
assert.deepEqual(order, ['open', 'getUrl'])
|
||||||
|
assert.deepEqual(win.openArgs, ['', '_blank'])
|
||||||
|
assert.equal(tab.location.href, 'https://project.webklar.com/api/auth/kundenansicht?token=abc')
|
||||||
|
assert.equal(tab.opener, null)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Fehler beim Holen -> Tab wird geschlossen, Fehler weitergereicht', async () => {
|
||||||
|
const { win, tab, order } = fakeWindow()
|
||||||
|
await assert.rejects(
|
||||||
|
() => openInNewTab(async () => { throw new Error('Kein Portal-Zugang') }, { win }),
|
||||||
|
/Kein Portal-Zugang/
|
||||||
|
)
|
||||||
|
assert.deepEqual(order, ['open', 'close'])
|
||||||
|
assert.equal(tab.location.href, undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Pop-up blockiert -> Fehlermeldung, getUrl nicht aufgerufen', async () => {
|
||||||
|
let called = false
|
||||||
|
const win = { open: () => null }
|
||||||
|
await assert.rejects(
|
||||||
|
() => openInNewTab(async () => { called = true; return 'x' }, { win }),
|
||||||
|
{ message: 'Neuer Tab wurde vom Browser blockiert.' }
|
||||||
|
)
|
||||||
|
assert.equal(called, false)
|
||||||
|
})
|
||||||
Reference in new issue
Block a user