diff --git a/public/neu/ansicht-funktionen.js b/public/neu/ansicht-funktionen.js index 71beb6e..687094f 100644 --- a/public/neu/ansicht-funktionen.js +++ b/public/neu/ansicht-funktionen.js @@ -281,6 +281,7 @@ export async function zeigeFunktionen(wurzel, { route, me, nav } = {}, abh = {}) }, }, }, 'Einschalten') + if (viewAs) knopf.disabled = true // Mitarbeiter-Kundenansicht: gesperrt statt erst nach dem Tipp ein 403-Text const bild = typeof eintrag.bild === 'string' && BILD.test(eintrag.bild) ? h('img', { class: 'kn-fn-bild', src: eintrag.bild, alt: `Beispiel: ${eintrag.name}`, width: 960, height: 600, loading: 'lazy', on: { error: (ev) => ev.target.remove() } }) : null @@ -291,6 +292,7 @@ export async function zeigeFunktionen(wurzel, { route, me, nav } = {}, abh = {}) h('p', { class: 'kn-fn-beschreibung' }, String(eintrag.beschreibung ?? '')), h('p', { class: 'kn-fn-enthalten' }, eintrag.enthaltenIn === 'hosting' ? 'Im Hosting-Abo enthalten' : 'Im Wartungs-Abo enthalten'), nurBetriebHinweis ? h('p', { class: 'kn-fn-hinweis' }, TEXTE.nurBetrieb(String(eintrag.name ?? ''))) : (kandidaten.length ? knopf : null), + !nurBetriebHinweis && kandidaten.length && viewAs ? h('p', { class: 'kn-fn-info' }, TEXTE.kundenansichtKnopf) : null, meldungEl)) } diff --git a/public/neu/funktion-einschalten.js b/public/neu/funktion-einschalten.js index 5238f6a..95f06a1 100644 --- a/public/neu/funktion-einschalten.js +++ b/public/neu/funktion-einschalten.js @@ -23,6 +23,7 @@ export const TEXTE = Object.freeze({ nurBetrieb: (name) => `${name} gibt es für Betriebe. Stimmt das nicht? Schreib uns im Chat.`, registerFehlt: 'Einschalten ist gerade nicht möglich. Schreib uns im Chat.', kundenansicht: 'In der Kundenansicht kann nichts eingeschaltet werden.', + kundenansichtKnopf: 'In der Kundenansicht nicht möglich.', // Hinweis neben dem gesperrten Knopf in Entdecken laden: 'Die Angaben konnten gerade nicht geladen werden. Bitte versuch es gleich noch einmal.', }) diff --git a/public/neu/huelle.js b/public/neu/huelle.js index 390aa93..7cab63a 100644 --- a/public/neu/huelle.js +++ b/public/neu/huelle.js @@ -42,10 +42,13 @@ export const FUNKTIONS_MODULE = { // Diese Module bekommen init*(wurzel, { einschalten }); die Analyse nutzt POST /api/funktionen/analyse/activate und bleibt init*(wurzel) export const MIT_EINSCHALTEN = ['news', 'termine', 'webshop', 'buchungen', 'zeiten'] -/** einschalten(platz, { key, projectId, onFertig, viewAs? }): lädt die Einschalt-Ansicht erst beim Aufruf; ein Ladefehler geht an das Modul zurück. */ -export const einschaltenOption = (lade) => async (platz, optionen) => { +/** + * einschalten(platz, { key, projectId, onFertig, viewAs? }): lädt die Einschalt-Ansicht erst beim Aufruf; ein Ladefehler geht an das Modul zurück. + * In der Mitarbeiter-Kundenansicht (viewAs der Sitzung) gilt immer viewAs true: Der Knopf ist gesperrt, auch wenn das Modul es nicht nennt. + */ +export const einschaltenOption = (lade, { viewAs = false } = {}) => async (platz, optionen) => { const modul = await lade('/neu/funktion-einschalten.js') - return modul.zeigeEinschalten(platz, optionen) + return modul.zeigeEinschalten(platz, viewAs ? { ...optionen, viewAs: true } : optionen) } /** Antwort von /api/auth/me → weiterleiten (Anmeldung, Mitarbeiter, abgelaufen, Passwort geändert) oder zeigen (Kunde, Kundenansicht). */ @@ -210,7 +213,7 @@ export async function starteHuelle({ win = window, doc = document, api = apiStan if (modul) { const projekt = new URLSearchParams(route.query).get('projekt') if (projekt) wurzel.dataset.wunschProjekt = projekt - if (MIT_EINSCHALTEN.includes(route.etage)) await geladen[name](wurzel, { einschalten: einschaltenOption(lade) }) + if (MIT_EINSCHALTEN.includes(route.etage)) await geladen[name](wurzel, { einschalten: einschaltenOption(lade, { viewAs }) }) else await geladen[name](wurzel) router.stempeln() // die Module löschen mit replaceState(null, …) den Verlaufsstempel } else { diff --git a/test/knFunktionenAnsicht.test.js b/test/knFunktionenAnsicht.test.js index 9ab49ff..bc5c543 100644 --- a/test/knFunktionenAnsicht.test.js +++ b/test/knFunktionenAnsicht.test.js @@ -292,3 +292,35 @@ test('wechselFunktion: nicht, wenn die Website die Funktion schon hat, die Websi assert.deepEqual(u.gegangen, []) } })) + +test('Mitarbeiter-Kundenansicht (me.viewAs), Entdecken: jeder Einschalten-Knopf ist gesperrt, der Hinweis steht daneben, der Tipp geht nirgends hin', mit(async (fx) => { + const u = umgebung(fx, { d: daten({ abo: { produkt: 'wartung' } }), me: { viewAs: true } }) + await u.zeige(ENTDECKEN) + const karten = fx.alle(u.wurzel, 'section[data-fn]') + assert.ok(karten.length >= 5) + for (const karte of karten) { + const knopf = fx.finde(karte, '[data-aktion="einschalten"]') + if (!knopf) continue // Funktion nur für Betriebe: dort steht ohnehin ein Hinweis statt des Knopfes + assert.equal(knopf.disabled, true, karte.getAttribute('data-fn')) + assert.match(fx.text(karte), /In der Kundenansicht nicht möglich\./, karte.getAttribute('data-fn')) + await fx.klick(knopf) + } + assert.deepEqual(u.gegangen, []) + assert.equal(fx.finde(u.wurzel, 'section.kn-fn-einschalten'), null) + // ohne Kundenansicht bleibt der Knopf frei und ohne Hinweis + const frei = umgebung(fx, { d: daten({ abo: { produkt: 'wartung' } }) }) + await frei.zeige(ENTDECKEN) + assert.equal(fx.finde(frei.wurzel, 'section[data-fn="news"] [data-aktion="einschalten"]').disabled, false) + assert.doesNotMatch(fx.text(frei.wurzel), /In der Kundenansicht nicht möglich/) +})) + +test('Mitarbeiter-Kundenansicht, Meine: eine angenommene Anfrage öffnet die Bestätigung mit gesperrtem Knopf und Hinweis, ohne Abruf zum Einschalten', mit(async (fx) => { + const u = umgebung(fx, { d: daten({ projekte: [projekt('p1', 'Bäckerei', { shop: feature('off', { anfrageStatus: 'angenommen' }) })] }), me: { viewAs: true } }) + await u.zeige(MEINE) + await fx.klick(fx.finde(fx.finde(u.wurzel, 'li[data-fn="shop"]'), 'button')) + const knopf = fx.finde(u.wurzel, 'section.kn-fn-einschalten [data-aktion="einschalten"]') + assert.equal(knopf.disabled, true) + assert.match(fx.text(u.wurzel), /In der Kundenansicht kann nichts eingeschaltet werden\./) + await fx.klick(knopf) + assert.equal(u.aufrufe.some(([m]) => m === 'POST'), false) +})) diff --git a/test/knHuelleStart.test.js b/test/knHuelleStart.test.js index c8ba38e..831d726 100644 --- a/test/knHuelleStart.test.js +++ b/test/knHuelleStart.test.js @@ -121,3 +121,18 @@ test('Zähler: Verträge laden beim Betreten und bei kn:zaehler-neu (document) n b.doc.dispatchEvent(new CustomEvent('kn:zaehler-neu', { detail: { was: 'vertraege' } })); await b.fx.ruhe(); assert.equal(n('/api/invoices'), i0 + 2) await b.zeige('#chat', 'tab'); assert.equal(chatTab(), null) }) + +test('Option einschalten in der Mitarbeiter-Kundenansicht: die Hülle reicht viewAs true durch, auch wenn das Modul es nicht nennt; sonst bleibt die Option unverändert', async () => { + const MODULE = { news: ['/funktionen.js', 'initNews'], termine: ['/termine.js', 'initTermine'], webshop: ['/shop.js', 'initShop'], buchungen: ['/buchungen.js', 'initBuchungen'], zeiten: ['/zeiten.js', 'initZeiten'] } + for (const [etage, [, init]] of Object.entries(MODULE)) { + const aufrufe = [] + const lade = async (p) => (p === '/neu/funktion-einschalten.js' ? { zeigeEinschalten: (platz, o) => aufrufe.push(['zeige', o]) } : { [init]: (root, opt) => aufrufe.push(['init', opt]) }) + await boot({ hash: `#${etage}`, karte: { ...KARTE_KUNDE, '/api/auth/me': ME_VIEW }, lade }) + const einschalten = aufrufe.find((a) => a[0] === 'init')[1].einschalten + const platz = { id: 'platz' } + await einschalten(platz, { key: etage, projectId: 'p1' }) + assert.deepEqual(aufrufe.at(-1), ['zeige', { key: etage, projectId: 'p1', viewAs: true }], `${etage}: viewAs fehlt im Aufruf`) + await einschalten(platz, { key: etage, projectId: 'p1', viewAs: false }) + assert.equal(aufrufe.at(-1)[1].viewAs, true, `${etage}: ein viewAs false des Moduls darf die Sperre nicht aufheben`) + } +})