Shop Review B I-8: Webshop ohne Zahlung nur mit Zustimmung in Textform aktivieren
Das Textform-Formular (Haken, Datum, Weg, Chat-Vorlage) gilt wie beim Terminplaner auch fuer den Webshop, wenn das Portal im Feld shop textformNoetig meldet. Kaestchentext und Chat-Vorlage nennen die Unterlagen des Webshops, die Fassung und die Erklaerungen, die das Portal im Nachweis speichert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0a2eab85d5
commit
00fa229235
4 files changed
+112
-23
No files matched your search
@@ -114,10 +114,10 @@ function AnleitungPanel({ customerId, projectId, feature, reloadKey }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zustimmung des Kunden in Textform (Terminplaner im Modus on): Haken, Datum, Weg und die Textvorlage fuer den Chat. Die Pflichtangaben
|
// Zustimmung des Kunden in Textform (Terminplaner und Webshop im Modus on): Haken, Datum, Weg und die Textvorlage fuer den Chat. Die Pflichtangaben
|
||||||
// prueft textformBereit (funktionenView); das Portal prueft sie noch einmal. Links nur als geprueftes https (textformAnsicht).
|
// prueft textformBereit (funktionenView); das Portal prueft sie noch einmal. Links nur als geprueftes https (textformAnsicht).
|
||||||
function TextformPanel({ termine, value, onChange, projectId, today, disabled }) {
|
function TextformPanel({ funktion, feld, value, onChange, projectId, today, disabled }) {
|
||||||
const ansicht = textformAnsicht(termine)
|
const ansicht = textformAnsicht(feld, funktion)
|
||||||
const { label, links, vorlage } = ansicht
|
const { label, links, vorlage } = ansicht
|
||||||
return (
|
return (
|
||||||
<div className="area-row funktionen-panel">
|
<div className="area-row funktionen-panel">
|
||||||
@@ -140,7 +140,7 @@ function TextformPanel({ termine, value, onChange, projectId, today, disabled })
|
|||||||
<span>Weg:</span>
|
<span>Weg:</span>
|
||||||
{CHANNELS.map((c) => (
|
{CHANNELS.map((c) => (
|
||||||
<label key={c} className="area-inline">
|
<label key={c} className="area-inline">
|
||||||
<input type="radio" name={`termine-weg-${projectId}`} value={c} checked={value.channel === c} disabled={disabled} onChange={() => onChange({ ...value, channel: c })} />
|
<input type="radio" name={`textform-weg-${funktion}-${projectId}`} value={c} checked={value.channel === c} disabled={disabled} onChange={() => onChange({ ...value, channel: c })} />
|
||||||
{channelLabel(c)}
|
{channelLabel(c)}
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
@@ -232,7 +232,7 @@ const LEERE_TEXTFORM = { confirmed: false, date: '', channel: '' }
|
|||||||
|
|
||||||
export default function CustomerFunktionen({ customerId }) {
|
export default function CustomerFunktionen({ customerId }) {
|
||||||
const [projects, setProjects] = useState(null)
|
const [projects, setProjects] = useState(null)
|
||||||
const [termine, setTermine] = useState(null) // Portal-Feld termine: { textformNoetig, avvVersion, links }
|
const [felder, setFelder] = useState({ termine: null, shop: null }) // Portal-Felder termine und shop: { textformNoetig, avvVersion, links }
|
||||||
const [textform, setTextform] = useState(LEERE_TEXTFORM)
|
const [textform, setTextform] = useState(LEERE_TEXTFORM)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [confirm, setConfirm] = useState(null) // { projectId, key, action }
|
const [confirm, setConfirm] = useState(null) // { projectId, key, action }
|
||||||
@@ -247,7 +247,7 @@ export default function CustomerFunktionen({ customerId }) {
|
|||||||
const load = useCallback(() => {
|
const load = useCallback(() => {
|
||||||
setError(null)
|
setError(null)
|
||||||
getFunktionen(customerId)
|
getFunktionen(customerId)
|
||||||
.then(({ projects: list, termine: feld }) => { setProjects(list || []); setTermine(feld || null) })
|
.then(({ projects: list, termine, shop: shopFeld }) => { setProjects(list || []); setFelder({ termine: termine || null, shop: shopFeld || null }) })
|
||||||
.catch((err) => setError(err.message || 'Funktionen konnten nicht geladen werden.'))
|
.catch((err) => setError(err.message || 'Funktionen konnten nicht geladen werden.'))
|
||||||
}, [customerId])
|
}, [customerId])
|
||||||
const loadShop = useCallback(() => {
|
const loadShop = useCallback(() => {
|
||||||
@@ -257,14 +257,14 @@ export default function CustomerFunktionen({ customerId }) {
|
|||||||
.catch((err) => setShopFehler(err.message || 'Webshop-Daten konnten nicht geladen werden.'))
|
.catch((err) => setShopFehler(err.message || 'Webshop-Daten konnten nicht geladen werden.'))
|
||||||
}, [customerId])
|
}, [customerId])
|
||||||
useEffect(() => { load(); loadShop() }, [load, loadShop])
|
useEffect(() => { load(); loadShop() }, [load, loadShop])
|
||||||
const nachEntscheidung = () => Promise.all([getFunktionen(customerId).then(({ projects: list, termine: feld }) => { setProjects(list || []); setTermine(feld || null) }), loadShop()])
|
const nachEntscheidung = () => Promise.all([getFunktionen(customerId).then(({ projects: list, termine, shop: shopFeld }) => { setProjects(list || []); setFelder({ termine: termine || null, shop: shopFeld || null }) }), loadShop()])
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
const { projectId, key, action } = confirm
|
const { projectId, key, action } = confirm
|
||||||
setBusy(true)
|
setBusy(true)
|
||||||
setStatus(null)
|
setStatus(null)
|
||||||
try {
|
try {
|
||||||
const mitTextform = textformNoetig(key, action, termine)
|
const mitTextform = textformNoetig(key, action, felder[key])
|
||||||
const antwort = await setFeature(customerId, key, {
|
const antwort = await setFeature(customerId, key, {
|
||||||
projectId, action, note: note.trim(),
|
projectId, action, note: note.trim(),
|
||||||
...(mitTextform ? { textformDate: textform.date, textformChannel: textform.channel } : {}),
|
...(mitTextform ? { textformDate: textform.date, textformChannel: textform.channel } : {}),
|
||||||
@@ -306,8 +306,8 @@ export default function CustomerFunktionen({ customerId }) {
|
|||||||
const isConfirming = confirm?.projectId === p.projectId && confirm?.key === f.key
|
const isConfirming = confirm?.projectId === p.projectId && confirm?.key === f.key
|
||||||
const hatAnleitung = f.key === 'termine' && feature.state !== 'off'
|
const hatAnleitung = f.key === 'termine' && feature.state !== 'off'
|
||||||
const anleitungOffenHier = hatAnleitung && panelOffen(p.projectId)
|
const anleitungOffenHier = hatAnleitung && panelOffen(p.projectId)
|
||||||
const braucheTextform = isConfirming && textformNoetig(f.key, confirm.action, termine)
|
const braucheTextform = isConfirming && textformNoetig(f.key, confirm.action, felder[f.key])
|
||||||
const textformFehlt = braucheTextform && !textformBereit({ ...textform, today: heute }, termine)
|
const textformFehlt = braucheTextform && !textformBereit({ ...textform, today: heute }, felder[f.key])
|
||||||
return (
|
return (
|
||||||
<Fragment key={`${p.projectId}:${f.key}`}>
|
<Fragment key={`${p.projectId}:${f.key}`}>
|
||||||
<div className="area-row funktionen-row">
|
<div className="area-row funktionen-row">
|
||||||
@@ -359,7 +359,7 @@ export default function CustomerFunktionen({ customerId }) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{braucheTextform && (
|
{braucheTextform && (
|
||||||
<TextformPanel termine={termine} value={textform} onChange={setTextform} projectId={p.projectId} today={heute} disabled={busy} />
|
<TextformPanel funktion={f.key} feld={felder[f.key]} value={textform} onChange={setTextform} projectId={p.projectId} today={heute} disabled={busy} />
|
||||||
)}
|
)}
|
||||||
{anleitungOffenHier && (
|
{anleitungOffenHier && (
|
||||||
<AnleitungPanel customerId={customerId} projectId={p.projectId} feature={feature} reloadKey={panelReload[p.projectId] || 0} />
|
<AnleitungPanel customerId={customerId} projectId={p.projectId} feature={feature} reloadKey={panelReload[p.projectId] || 0} />
|
||||||
|
|||||||
+32
-11
@@ -174,10 +174,16 @@ export function mergeFunktion(projects, projectId, key, aktualisiert) {
|
|||||||
return projects.map((p) => (p.projectId === projectId ? { ...p, features: { ...p.features, [key]: view } } : p))
|
return projects.map((p) => (p.projectId === projectId ? { ...p, features: { ...p.features, [key]: view } } : p))
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Aktivieren (ohne Zahlung) mit Zustimmung in Textform (Modus on des Terminplaners; Portal: GET /funktionen/:customerId => termine) ---
|
// --- Aktivieren (ohne Zahlung) mit Zustimmung in Textform (Modus on des Terminplaners und des Webshops; Portal: GET /funktionen/:customerId => termine, shop) ---
|
||||||
|
|
||||||
/** true, wenn diese Aktion die Zustimmung in Textform braucht: nur "Aktivieren (ohne Zahlung)" des Terminplaners und nur, wenn das Portal es verlangt (termine.textformNoetig === true). */
|
/** Funktionen, deren Aktivierung durch das Team die Zustimmung des Kunden in Textform brauchen kann (Portal: TEXTFORM_KEYS). */
|
||||||
export const textformNoetig = (key, action, termine) => key === 'termine' && action === 'activate' && termine?.textformNoetig === true
|
export const TEXTFORM_FUNKTIONEN = ['termine', 'shop']
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true, wenn diese Aktion die Zustimmung in Textform braucht: nur "Aktivieren (ohne Zahlung)" des Terminplaners oder des Webshops und nur, wenn das
|
||||||
|
* Portal es verlangt (feld = Portal-Feld dieser Funktion, termine bzw. shop, feld.textformNoetig === true).
|
||||||
|
*/
|
||||||
|
export const textformNoetig = (key, action, feld) => TEXTFORM_FUNKTIONEN.includes(key) && action === 'activate' && feld?.textformNoetig === true
|
||||||
|
|
||||||
/** Kaestchentext (wie im Portal-Nachweis, ohne Datum und Weg); die beiden Links setzt die Komponente. */
|
/** Kaestchentext (wie im Portal-Nachweis, ohne Datum und Weg); die beiden Links setzt die Komponente. */
|
||||||
export const termineTextformLabel = (version) => ({
|
export const termineTextformLabel = (version) => ({
|
||||||
@@ -188,10 +194,23 @@ export const termineTextformLabel = (version) => ({
|
|||||||
after: ` (Fassung ${version}) in Textform zugestimmt und bestätigt, dass sein Angebot nicht im Gesundheitsbereich liegt.`,
|
after: ` (Fassung ${version}) in Textform zugestimmt und bestätigt, dass sein Angebot nicht im Gesundheitsbereich liegt.`,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/** Kaestchentext des Webshops; die Erklaerungen entsprechen den Bestelltexten "unterlagen", "verantwortung" und "stripe", die das Portal im Nachweis speichert. */
|
||||||
|
export const shopTextformLabel = (version) => ({
|
||||||
|
before: 'Der Kunde hat der ',
|
||||||
|
lb: 'Leistungsbeschreibung Webshop',
|
||||||
|
between: ' und dem ',
|
||||||
|
avv: 'Auftragsverarbeitungsvertrag Webshop',
|
||||||
|
after: ` (Fassung ${version}) in Textform zugestimmt und bestätigt, dass er als Unternehmer in eigenem Namen verkauft, für seine Produkte, Preise, Produktangaben und Rechtstexte selbst verantwortlich ist, keine der in der Leistungsbeschreibung ausgeschlossenen Waren verkauft und die Zahlungen seiner Käufer über sein eigenes Stripe-Konto laufen.`,
|
||||||
|
})
|
||||||
|
|
||||||
/** Textvorlage fuer den Chat (Kopierknopf); links = die versionierten Adressen aus dem Portal (termine.links). */
|
/** Textvorlage fuer den Chat (Kopierknopf); links = die versionierten Adressen aus dem Portal (termine.links). */
|
||||||
export const termineChatTemplate = (version, links) =>
|
export const termineChatTemplate = (version, links) =>
|
||||||
`Damit wir den Terminplaner für Dich einschalten können, brauchen wir Deine Zustimmung in Textform. Es gelten die Leistungsbeschreibung Terminplaner und der Auftragsverarbeitungsvertrag Terminplaner in der Fassung ${version}: ${links.lb} · ${links.avv}. Bitte antworte hier mit „Ich stimme zu“ und bestätige, dass Dein Angebot nicht im Gesundheitsbereich liegt.`
|
`Damit wir den Terminplaner für Dich einschalten können, brauchen wir Deine Zustimmung in Textform. Es gelten die Leistungsbeschreibung Terminplaner und der Auftragsverarbeitungsvertrag Terminplaner in der Fassung ${version}: ${links.lb} · ${links.avv}. Bitte antworte hier mit „Ich stimme zu“ und bestätige, dass Dein Angebot nicht im Gesundheitsbereich liegt.`
|
||||||
|
|
||||||
|
/** Textvorlage fuer den Chat beim Webshop; links = shop.links. Die Bestaetigung deckt dieselben Erklaerungen wie shopTextformLabel. */
|
||||||
|
export const shopChatTemplate = (version, links) =>
|
||||||
|
`Damit wir den Webshop für Dich einschalten können, brauchen wir Deine Zustimmung in Textform. Es gelten die Leistungsbeschreibung Webshop und der Auftragsverarbeitungsvertrag Webshop in der Fassung ${version}: ${links.lb} · ${links.avv}. Bitte antworte hier mit „Ich stimme zu“ und bestätige, dass Du als Unternehmer in eigenem Namen verkaufst, für Deine Produkte, Preise, Produktangaben und Rechtstexte selbst verantwortlich bist, keine der in der Leistungsbeschreibung ausgeschlossenen Waren verkaufst und Deine Zahlungen über Dein eigenes Stripe-Konto laufen.`
|
||||||
|
|
||||||
/** Nur geprüfte https-Adressen werden Links; alles andere ''. */
|
/** Nur geprüfte https-Adressen werden Links; alles andere ''. */
|
||||||
const httpsUrl = (wert) => {
|
const httpsUrl = (wert) => {
|
||||||
if (typeof wert !== 'string') return ''
|
if (typeof wert !== 'string') return ''
|
||||||
@@ -203,23 +222,25 @@ const httpsUrl = (wert) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alles, was der Bestaetigungsdialog zeigt, aus dem Portal-Feld termine: { version, links { lb, avv } (nur https, sonst ''), label, vorlage }.
|
* Alles, was der Bestaetigungsdialog zeigt, aus dem Portal-Feld der Funktion (feld = termine bzw. shop, key = 'termine' | 'shop'):
|
||||||
|
* { version, links { lb, avv } (nur https, sonst ''), label, vorlage }.
|
||||||
* vorlage nur, wenn Fassung und beide Adressen da sind (eine Vorlage mit leeren Adressen geht nie an den Kunden), sonst ''.
|
* vorlage nur, wenn Fassung und beide Adressen da sind (eine Vorlage mit leeren Adressen geht nie an den Kunden), sonst ''.
|
||||||
*/
|
*/
|
||||||
export function textformAnsicht(termine) {
|
export function textformAnsicht(feld, key = 'termine') {
|
||||||
const version = typeof termine?.avvVersion === 'string' ? termine.avvVersion : ''
|
const shop = key === 'shop'
|
||||||
const links = { lb: httpsUrl(termine?.links?.lb), avv: httpsUrl(termine?.links?.avv) }
|
const version = typeof feld?.avvVersion === 'string' ? feld.avvVersion : ''
|
||||||
|
const links = { lb: httpsUrl(feld?.links?.lb), avv: httpsUrl(feld?.links?.avv) }
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
links,
|
links,
|
||||||
label: termineTextformLabel(version),
|
label: (shop ? shopTextformLabel : termineTextformLabel)(version),
|
||||||
vorlage: version && links.lb && links.avv ? termineChatTemplate(version, links) : '',
|
vorlage: version && links.lb && links.avv ? (shop ? shopChatTemplate : termineChatTemplate)(version, links) : '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Alle drei Pflichtangaben (Haken, Datum zwischen Fassung und heute, Weg); ohne Fassung im Portal-Feld nie bereit. */
|
/** Alle drei Pflichtangaben (Haken, Datum zwischen Fassung und heute, Weg); ohne Fassung im Portal-Feld nie bereit. */
|
||||||
export function textformBereit({ confirmed, date, channel, today }, termine) {
|
export function textformBereit({ confirmed, date, channel, today }, feld) {
|
||||||
const minDate = typeof termine?.avvVersion === 'string' ? termine.avvVersion : ''
|
const minDate = typeof feld?.avvVersion === 'string' ? feld.avvVersion : ''
|
||||||
if (!minDate) return false
|
if (!minDate) return false
|
||||||
return canSubmitTextform({ confirmed, date, channel, today, minDate })
|
return canSubmitTextform({ confirmed, date, channel, today, minDate })
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ export const markStorageDone = (id) => request(`/storage-tasks/${enc(id)}/done`,
|
|||||||
export const getFunktionen = (customerId) => request(`/funktionen/${enc(customerId)}`)
|
export const getFunktionen = (customerId) => request(`/funktionen/${enc(customerId)}`)
|
||||||
/**
|
/**
|
||||||
* Aktivieren (ohne Zahlung) oder Abschalten einer Funktion (key: news | termine | shop); Antwort { projects: [das eine Projekt] }.
|
* Aktivieren (ohne Zahlung) oder Abschalten einer Funktion (key: news | termine | shop); Antwort { projects: [das eine Projekt] }.
|
||||||
* Terminplaner im Modus on: beim Aktivieren zusaetzlich textformDate (JJJJ-MM-TT) und textformChannel (chat | email).
|
* Terminplaner und Webshop im Modus on: beim Aktivieren zusaetzlich textformDate (JJJJ-MM-TT) und textformChannel (chat | email).
|
||||||
*/
|
*/
|
||||||
export const setFeature = (customerId, key, { projectId, action, note, textformDate, textformChannel }) =>
|
export const setFeature = (customerId, key, { projectId, action, note, textformDate, textformChannel }) =>
|
||||||
request(`/funktionen/${enc(customerId)}/${enc(key)}`, { method: 'POST', body: featureBody({ projectId, action, note, textformDate, textformChannel }) })
|
request(`/funktionen/${enc(customerId)}/${enc(key)}`, { method: 'POST', body: featureBody({ projectId, action, note, textformDate, textformChannel }) })
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ const {
|
|||||||
textformNoetig,
|
textformNoetig,
|
||||||
termineTextformLabel,
|
termineTextformLabel,
|
||||||
termineChatTemplate,
|
termineChatTemplate,
|
||||||
|
shopTextformLabel,
|
||||||
|
shopChatTemplate,
|
||||||
|
TEXTFORM_FUNKTIONEN,
|
||||||
textformAnsicht,
|
textformAnsicht,
|
||||||
textformBereit,
|
textformBereit,
|
||||||
featureBody,
|
featureBody,
|
||||||
@@ -393,6 +396,71 @@ test('textformNoetig: nur "Aktivieren (ohne Zahlung)" des Terminplaners und nur,
|
|||||||
assert.equal(textformNoetig(undefined, 'activate', termine()), false)
|
assert.equal(textformNoetig(undefined, 'activate', termine()), false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Feld "shop" der Portal-Antwort GET /funktionen/:customerId (Fassung = config.shop.fassung)
|
||||||
|
const shopFeld = (extra = {}) => ({
|
||||||
|
textformNoetig: true,
|
||||||
|
avvVersion: '2026-10-05',
|
||||||
|
links: {
|
||||||
|
lb: 'https://project.webklar.com/rechtliches/leistungsbeschreibung-webshop-2026-10-05.html',
|
||||||
|
avv: 'https://project.webklar.com/rechtliches/avv-webshop-2026-10-05.html',
|
||||||
|
},
|
||||||
|
...extra,
|
||||||
|
})
|
||||||
|
|
||||||
|
test('textformNoetig: auch das Aktivieren (ohne Zahlung) des Webshops, mit dem Feld shop; die Funktion kommt aus dem Schlüssel', () => {
|
||||||
|
assert.deepEqual(TEXTFORM_FUNKTIONEN, ['termine', 'shop'])
|
||||||
|
assert.equal(textformNoetig('shop', 'activate', shopFeld()), true)
|
||||||
|
assert.equal(textformNoetig('shop', 'activate', shopFeld({ textformNoetig: false })), false)
|
||||||
|
assert.equal(textformNoetig('shop', 'activate', undefined), false, 'altes Portal ohne Feld shop: wie bisher')
|
||||||
|
assert.equal(textformNoetig('shop', 'activate', null), false)
|
||||||
|
assert.equal(textformNoetig('shop', 'activate', shopFeld({ textformNoetig: 'true' })), false, 'nur echtes true')
|
||||||
|
for (const action of ['disable', 'abnahme', 'ticket']) assert.equal(textformNoetig('shop', action, shopFeld()), false, action)
|
||||||
|
assert.equal(textformNoetig('news', 'activate', shopFeld()), false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('shopTextformLabel und shopChatTemplate: wörtlich, mit Fassung, den Erklärungen des Nachweises und den versionierten Adressen', () => {
|
||||||
|
const l = shopTextformLabel('2026-10-05')
|
||||||
|
assert.equal(l.lb, 'Leistungsbeschreibung Webshop')
|
||||||
|
assert.equal(l.avv, 'Auftragsverarbeitungsvertrag Webshop')
|
||||||
|
assert.equal(
|
||||||
|
l.before + l.lb + l.between + l.avv + l.after,
|
||||||
|
'Der Kunde hat der Leistungsbeschreibung Webshop und dem Auftragsverarbeitungsvertrag Webshop (Fassung 2026-10-05) ' +
|
||||||
|
'in Textform zugestimmt und bestätigt, dass er als Unternehmer in eigenem Namen verkauft, für seine Produkte, Preise, Produktangaben und Rechtstexte selbst verantwortlich ist, ' +
|
||||||
|
'keine der in der Leistungsbeschreibung ausgeschlossenen Waren verkauft und die Zahlungen seiner Käufer über sein eigenes Stripe-Konto laufen.',
|
||||||
|
)
|
||||||
|
const { lb, avv } = shopFeld().links
|
||||||
|
const text = shopChatTemplate('2026-10-05', { lb, avv })
|
||||||
|
assert.ok(text.startsWith('Damit wir den Webshop für Dich einschalten können, brauchen wir Deine Zustimmung in Textform. '))
|
||||||
|
assert.ok(text.includes(`in der Fassung 2026-10-05: ${lb} · ${avv}. `))
|
||||||
|
assert.match(text, /leistungsbeschreibung-webshop-2026-10-05\.html/)
|
||||||
|
assert.match(text, /avv-webshop-2026-10-05\.html/)
|
||||||
|
assert.match(text, /Ich stimme zu/)
|
||||||
|
assert.match(text, /eigenes Stripe-Konto/)
|
||||||
|
assert.ok(!text.includes('undefined') && !text.includes('{') && !text.includes('Terminplaner'))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('textformAnsicht(feld, "shop"): Label, Vorlage und Links des Webshops; ohne Schlüssel bleibt es der Terminplaner', () => {
|
||||||
|
const a = textformAnsicht(shopFeld(), 'shop')
|
||||||
|
assert.equal(a.version, '2026-10-05')
|
||||||
|
assert.deepEqual(a.links, shopFeld().links)
|
||||||
|
assert.deepEqual(a.label, shopTextformLabel('2026-10-05'))
|
||||||
|
assert.equal(a.vorlage, shopChatTemplate('2026-10-05', shopFeld().links))
|
||||||
|
assert.deepEqual(textformAnsicht(termine()).label, termineTextformLabel('2026-10-05'))
|
||||||
|
// Adressen ohne https werden nie Link, ohne beide Adressen keine Vorlage
|
||||||
|
const schlecht = textformAnsicht(shopFeld({ links: { lb: 'javascript:alert(1)', avv: 'http://x.example/a.html' } }), 'shop')
|
||||||
|
assert.deepEqual(schlecht.links, { lb: '', avv: '' })
|
||||||
|
assert.equal(schlecht.vorlage, '')
|
||||||
|
assert.equal(textformAnsicht(undefined, 'shop').version, '')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('textformBereit mit dem Feld shop: Datum zwischen Fassung und heute, ohne Fassung nie bereit', () => {
|
||||||
|
const ok = { confirmed: true, date: '2026-10-06', channel: 'email', today: '2026-10-07' }
|
||||||
|
assert.equal(textformBereit(ok, shopFeld()), true)
|
||||||
|
assert.equal(textformBereit({ ...ok, date: '2026-10-04' }, shopFeld()), false, 'vor der Fassung')
|
||||||
|
assert.equal(textformBereit(ok, shopFeld({ avvVersion: '' })), false)
|
||||||
|
assert.equal(textformBereit(ok, undefined), false)
|
||||||
|
})
|
||||||
|
|
||||||
test('termineChatTemplate: wörtlich, mit Fassung und den beiden versionierten Adressen', () => {
|
test('termineChatTemplate: wörtlich, mit Fassung und den beiden versionierten Adressen', () => {
|
||||||
const { lb, avv } = termine().links
|
const { lb, avv } = termine().links
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
|||||||
Reference in new issue
Block a user