Buchungskalender: Abschluss – Zustimmung in Textform auch für den Buchungskalender

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-10-07 15:22:56 +00:00
1 parent b2a0ff5d53
commit 38020bfdff
3 files changed
+64 -13

No files matched your search

@@ -115,7 +115,7 @@ function AnleitungPanel({ customerId, projectId, feature, reloadKey }) {
)
}
// Zustimmung des Kunden in Textform (Terminplaner und Webshop im Modus on): Haken, Datum, Weg und die Textvorlage fuer den Chat. Die Pflichtangaben
// Zustimmung des Kunden in Textform (Terminplaner, Webshop und Buchungskalender 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).
function TextformPanel({ funktion, feld, value, onChange, projectId, today, disabled }) {
const ansicht = textformAnsicht(feld, funktion)
@@ -268,7 +268,7 @@ const LEERE_TEXTFORM = { confirmed: false, date: '', channel: '' }
export default function CustomerFunktionen({ customerId }) {
const [projects, setProjects] = useState(null)
const [felder, setFelder] = useState({ termine: null, shop: null }) // Portal-Felder termine und shop: { textformNoetig, avvVersion, links }
const [felder, setFelder] = useState({ termine: null, shop: null, unterkunft: null }) // Portal-Felder termine, shop und unterkunft: { textformNoetig, avvVersion, links }
const [textform, setTextform] = useState(LEERE_TEXTFORM)
const [error, setError] = useState(null)
const [confirm, setConfirm] = useState(null) // { projectId, key, action }
@@ -285,7 +285,7 @@ export default function CustomerFunktionen({ customerId }) {
const load = useCallback(() => {
setError(null)
getFunktionen(customerId)
.then(({ projects: list, termine, shop: shopFeld }) => { setProjects(list || []); setFelder({ termine: termine || null, shop: shopFeld || null }) })
.then(({ projects: list, termine, shop: shopFeld, unterkunft: ukFeld }) => { setProjects(list || []); setFelder({ termine: termine || null, shop: shopFeld || null, unterkunft: ukFeld || null }) })
.catch((err) => setError(err.message || 'Funktionen konnten nicht geladen werden.'))
}, [customerId])
const loadShop = useCallback(() => {
@@ -301,7 +301,7 @@ export default function CustomerFunktionen({ customerId }) {
.catch((err) => setUnterkunftFehler(err.message || 'Daten des Buchungskalenders konnten nicht geladen werden.'))
}, [customerId])
useEffect(() => { load(); loadShop(); loadUnterkunft() }, [load, loadShop, loadUnterkunft])
const nachEntscheidung = () => Promise.all([getFunktionen(customerId).then(({ projects: list, termine, shop: shopFeld }) => { setProjects(list || []); setFelder({ termine: termine || null, shop: shopFeld || null }) }), loadShop(), loadUnterkunft()])
const nachEntscheidung = () => Promise.all([getFunktionen(customerId).then(({ projects: list, termine, shop: shopFeld, unterkunft: ukFeld }) => { setProjects(list || []); setFelder({ termine: termine || null, shop: shopFeld || null, unterkunft: ukFeld || null }) }), loadShop(), loadUnterkunft()])
const save = async () => {
const { projectId, key, action } = confirm
+27 -8
View File
@@ -201,14 +201,14 @@ export function mergeFunktion(projects, projectId, key, aktualisiert) {
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 und des Webshops; Portal: GET /funktionen/:customerId => termine, shop) ---
// --- Aktivieren (ohne Zahlung) mit Zustimmung in Textform (Modus on des Terminplaners, des Webshops und des Buchungskalenders; Portal: GET /funktionen/:customerId => termine, shop, unterkunft) ---
/** Funktionen, deren Aktivierung durch das Team die Zustimmung des Kunden in Textform brauchen kann (Portal: TEXTFORM_KEYS). */
export const TEXTFORM_FUNKTIONEN = ['termine', 'shop']
export const TEXTFORM_FUNKTIONEN = ['termine', 'shop', 'unterkunft']
/**
* 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).
* true, wenn diese Aktion die Zustimmung in Textform braucht: nur "Aktivieren (ohne Zahlung)" des Terminplaners, des Webshops oder des Buchungskalenders
* und nur, wenn das Portal es verlangt (feld = Portal-Feld dieser Funktion, termine, shop bzw. unterkunft, feld.textformNoetig === true).
*/
export const textformNoetig = (key, action, feld) => TEXTFORM_FUNKTIONEN.includes(key) && action === 'activate' && feld?.textformNoetig === true
@@ -238,6 +238,25 @@ export const termineChatTemplate = (version, links) =>
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.`
/** Kaestchentext des Buchungskalenders (Unterkunftsvermietung); Erklaerungen nach Nr. 4 und 3 der Leistungsbeschreibung (Vermieter = Unternehmer, eigener Name, eigene Angaben, eigenes Stripe-Konto). */
export const unterkunftTextformLabel = (version) => ({
before: 'Der Kunde hat der ',
lb: 'Leistungsbeschreibung Buchungskalender',
between: ' und dem ',
avv: 'Auftragsverarbeitungsvertrag Buchungskalender',
after: ` (Fassung ${version}) in Textform zugestimmt und bestätigt, dass er als Unternehmer in eigenem Namen vermietet, für seine Unterkünfte, Preise, Angaben zum Angebot, Stornobedingungen und Rechtstexte selbst verantwortlich ist und Online-Zahlungen seiner Gäste, wenn er sie nutzt, über sein eigenes Stripe-Konto laufen.`,
})
/** Textvorlage fuer den Chat beim Buchungskalender; links = unterkunft.links. Die Bestaetigung deckt dieselben Erklaerungen wie unterkunftTextformLabel. */
export const unterkunftChatTemplate = (version, links) =>
`Damit wir den Buchungskalender für Dich einschalten können, brauchen wir Deine Zustimmung in Textform. Es gelten die Leistungsbeschreibung Buchungskalender und der Auftragsverarbeitungsvertrag Buchungskalender 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 vermietest, für Deine Unterkünfte, Preise, Angaben zum Angebot, Stornobedingungen und Rechtstexte selbst verantwortlich bist und Online-Zahlungen Deiner Gäste, wenn Du sie nutzt, über Dein eigenes Stripe-Konto laufen.`
const TEXTFORM_TEXTE = {
termine: { label: termineTextformLabel, vorlage: termineChatTemplate },
shop: { label: shopTextformLabel, vorlage: shopChatTemplate },
unterkunft: { label: unterkunftTextformLabel, vorlage: unterkunftChatTemplate },
}
/** Nur geprüfte https-Adressen werden Links; alles andere ''. */
const httpsUrl = (wert) => {
if (typeof wert !== 'string') return ''
@@ -249,19 +268,19 @@ const httpsUrl = (wert) => {
}
/**
* Alles, was der Bestaetigungsdialog zeigt, aus dem Portal-Feld der Funktion (feld = termine bzw. shop, key = 'termine' | 'shop'):
* Alles, was der Bestaetigungsdialog zeigt, aus dem Portal-Feld der Funktion (feld = termine, shop bzw. unterkunft, key = 'termine' | 'shop' | 'unterkunft'):
* { 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 ''.
*/
export function textformAnsicht(feld, key = 'termine') {
const shop = key === 'shop'
const texte = TEXTFORM_TEXTE[key] || TEXTFORM_TEXTE.termine
const version = typeof feld?.avvVersion === 'string' ? feld.avvVersion : ''
const links = { lb: httpsUrl(feld?.links?.lb), avv: httpsUrl(feld?.links?.avv) }
return {
version,
links,
label: (shop ? shopTextformLabel : termineTextformLabel)(version),
vorlage: version && links.lb && links.avv ? (shop ? shopChatTemplate : termineChatTemplate)(version, links) : '',
label: texte.label(version),
vorlage: version && links.lb && links.avv ? texte.vorlage(version, links) : '',
}
}