DB4js als _all zusammengefasst

Beoanswer ist nun OK
This commit is contained in:
rxf
2025-11-17 14:22:52 +01:00
parent ad0f7b2912
commit 3e8b1f9691
14 changed files with 1528 additions and 81 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "beoanswer_react",
"private": true,
"version": "1.0.2",
"version": "1.0.3",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -17,8 +17,8 @@ function AppContent() {
const [name, setName] = useState("")
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [mitsend, setMitsend] = useState(false)
const [mitback, setMitback] = useState(false)
//const [mitsend, setMitsend] = useState(false)
//const [mitback, setMitback] = useState(false)
const version = packageJson.version
const vdate = new Date().toLocaleDateString('de-DE')
@@ -66,6 +66,8 @@ function AppContent() {
headers['Authorization'] = `Basic ${credentials}`
}
console.log(formData)
const response = await fetch(APIURL, {
method: 'POST',
headers: headers,
@@ -160,7 +162,7 @@ function AppContent() {
}
const setBackButton = () => {
setMitback(true)
// setMitback(true)
}
// Welche Komponeneten werden angezeigt:

View File

@@ -0,0 +1,14 @@
.modal-content.custom-modal {
width: 95vw; /* Nearly full window width */
max-width: none; /* Remove base max-width constraint */
height: 85vh; /* Tall modal */
max-height: 85vh; /* Cap at viewport height */
display: flex; /* Allow header/body/footer layout */
flex-direction: column;
}
.modal-content.custom-modal .modal-body {
flex: 1; /* Fill remaining space */
overflow: auto; /* Scroll body when content is taller than container */
text-align: left; /* Better for long instructions */
}

View File

@@ -2,6 +2,7 @@ import { useState } from 'react'
import { useFormData } from '../FormContext'
import Modal from './Modal'
import ConfirmModal from './ConfirmModal'
import './LastButtons.css';
export default function LastButtons({ mitSend, mitBack, handleBack}) {
@@ -13,7 +14,9 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
const [isModalHtml, setIsModalHtml] = useState(false)
const [showConfirmModal, setShowConfirmModal] = useState(false)
const [isSuccessModal, setIsSuccessModal] = useState(false)
const [isWideModal, setIsWideModal] = useState(false)
const handleSenden = async () => {
console.log("Alle Formulardaten: ", formData)
@@ -37,77 +40,74 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
throw new Error('Keine ID in der URL gefunden.')
}
// FormData für PHP Backend erstellen
const backendData = new FormData()
backendData.append('cmd', 'UPDATEAFTER')
backendData.append('id', id)
// JSON-Objekt statt FormData erstellen
const backendData = {
cmd: 'UPDATEAFTER',
id: id
}
// Formulardaten zu Backend-Feldern mappen
// Basis-Status
if (formData.stattgefunden === 'ja') {
backendData.append('stattgefunden', '1')
backendData.stattgefunden = '1'
// Spenden-Informationen
if (formData.spendenArt) {
switch (formData.spendenArt) {
case 'bar':
backendData.append('bezahlt', `Kasse ${formData.betrag})`)
backendData.bezahlt = `Kasse ${formData.betrag}`
break
case 'ueber':
backendData.append('bezahlt', 'Überweisung')
backendData.bezahlt = 'Überweisung'
break
case 'kasse':
backendData.append('bezahlt', 'Spendenkässle')
backendData.bezahlt = 'Spendenkässle'
break
case 'keine':
backendData.append('bezahlt', 'keine')
backendData.bezahlt = 'keine'
break
}
}
} else if (formData.stattgefunden === 'nein') {
backendData.append('stattgefunden', '0')
backendData.append('bezahlt', 'keine')
backendData.stattgefunden = '0'
backendData.bezahlt = 'keine'
// Grund für Ausfall
if (formData.abgesagt === 'abgesagt') {
backendData.append('status', 3)
backendData.status = 3
} else if (formData.abgesagt === 'verschoben') {
backendData.append('wtermin', formData.neuesDatum || '1900-01-01 00:00:00')
backendData.wtermin = formData.neuesDatum || '1900-01-01 00:00:00'
}
}
// Bemerkungen
backendData.append('remark', formData.bemerkungen || '')
backendData.remark = formData.bemerkungen || ''
// Besucher
backendData.append('besucher', formData.besucher || '0')
backendData.besucher = formData.besucher || '0'
// // Bearbeitungsdatum setzen
// const now = new Date().toISOString().slice(0, 19).replace('T', ' ')
// backendData.append('bearbeitet_am', now)
// Debug: FormData kann nicht direkt geloggt werden, deshalb iterieren
console.log("=== FORM DATA DEBUG ===")
// Debug: JSON-Daten loggen
console.log("=== JSON DATA DEBUG ===")
console.log("Original formData aus Context:", formData)
console.log("URL ID:", id)
console.log("Backend FormData Inhalt:")
for (let [key, value] of backendData.entries()) {
console.log(` ${key}: ${value}`)
}
console.log("Backend JSON Daten:", JSON.stringify(backendData, null, 2))
console.log("========================")
// HTTP Basic Authentication Header
const headers = {}
// HTTP Headers mit Basic Authentication und Content-Type
const headers = {
'Content-Type': 'application/json'
}
if (username && password) {
const credentials = btoa(`${username}:${password}`)
headers['Authorization'] = `Basic ${credentials}`
}
// Backend-Aufruf
// Backend-Aufruf mit JSON
const response = await fetch(APIURL, {
method: 'POST',
headers: headers,
body: backendData
body: JSON.stringify(backendData)
})
if (!response.ok) {
@@ -135,9 +135,13 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
responseText.trim() === 'true'
if (isSuccess) {
// E-Mail-Benachrichtigung senden (nicht blockierend)
sendEmailNotification(id, formData, backendData, APIURL, headers)
setModalType('success')
setModalMessage('✅ Daten erfolgreich gespeichert!')
setIsSuccessModal(true)
setIsWideModal(false)
setShowModal(true)
} else {
@@ -149,12 +153,93 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
setModalType('error')
setModalMessage(`❌ Fehler beim Speichern: ${error.message}`)
setIsSuccessModal(false)
setIsWideModal(false)
setShowModal(true)
} finally {
setIsSending(false)
}
}
// E-Mail-Benachrichtigung senden (asynchron, nicht blockierend)
const sendEmailNotification = async (id, formData, backendData, apiUrl, headers) => {
try {
// Details zur Sonderführung laden (für BEO und Besucher-Name)
let beoName = 'unbekannt'
let visitorName = 'unbekannt'
let termin = ''
try {
const detailsResp = await fetch(apiUrl, {
method: 'POST',
headers,
body: JSON.stringify({ cmd: 'GET_ONE', id })
})
if (detailsResp.ok) {
const details = await detailsResp.json()
// Backend kann Objekt oder Array liefern; robust extrahieren
const d = Array.isArray(details) ? (details[0] || {}) : (details || {})
// Felder: mitarbeiter (BEO), name/vorname (Besucher), wtermin (Termin)
beoName = d.mitarbeiter || beoName
const vn = d.vorname || ''
const nn = d.name || ''
visitorName = (vn + ' ' + nn).trim() || visitorName
termin = d.wtermin || ''
}
} catch (e) {
console.warn('Konnte Details für E-Mail nicht laden:', e)
}
// E-Mail-Betreff und Inhalt erstellen
const stattgefunden = formData.stattgefunden === 'ja' ? 'Ja' : 'Nein'
const besucher = formData.besucher || '0'
const spenden = backendData.bezahlt || 'keine'
const bemerkungen = formData.bemerkungen || 'keine'
const subject = `Sonderführung vom ${termin} - Nachbearbeitung`
const body = `Nachbearbeitung für Sonderführung ID ${id}
BEO: ${beoName}
Besucher: ${visitorName}
Termin: ${termin}
Stattgefunden: ${stattgefunden}
Anzahl Besucher: ${besucher}
Spenden: ${spenden}
Bemerkungen: ${bemerkungen}
Status: ${formData.stattgefunden === 'ja' ? 'Durchgeführt' :
formData.abgesagt === 'abgesagt' ? 'Abgesagt' :
formData.abgesagt === 'verschoben' ? `Verschoben auf ${formData.neuesDatum}` :
'Unbekannt'}
Diese E-Mail wurde automatisch vom Nachbearbeitungs-System generiert.
`
// E-Mail-Command an Backend senden
const emailData = {
cmd: 'SEND_CONFIRMATION',
to: 'rxf@gmx.de',
subject: subject,
body: body
}
const emailResponse = await fetch(apiUrl, {
method: 'POST',
headers: headers,
body: JSON.stringify(emailData)
})
if (emailResponse.ok) {
console.log('✅ E-Mail-Benachrichtigung erfolgreich gesendet')
} else {
console.warn('⚠️ E-Mail-Benachrichtigung konnte nicht gesendet werden:', emailResponse.status)
}
} catch (error) {
// Fehler beim E-Mail-Versand nicht kritisch - nur loggen
console.warn('⚠️ E-Mail-Benachrichtigung fehlgeschlagen:', error)
}
}
const handleAbbruch = () => {
setShowConfirmModal(true)
}
@@ -178,41 +263,41 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
setShowConfirmModal(false)
}
const handleAnleitung = () => {
// Öffne die HTML-Anleitung in einem neuen Fenster/Tab
const anleitungUrl = '/anleitung.html'
const windowFeatures = 'width=800,height=600,scrollbars=yes,resizable=yes,toolbar=no,menubar=no,location=no'
const handleAnleitung = async () => {
try {
// Versuche ein Popup-Fenster zu öffnen
const anleitungWindow = window.open(anleitungUrl, 'anleitung', windowFeatures)
// Fallback: Wenn Popup blockiert wird, öffne in neuem Tab
if (!anleitungWindow) {
window.open(anleitungUrl, '_blank')
// Anleitung soll im großen Modal erscheinen
setIsWideModal(true)
// Respect Vite base path in production (vite.config.js base: '/beoanswer/')
const base = (import.meta.env && import.meta.env.BASE_URL) ? import.meta.env.BASE_URL : '/'
const normalizedBase = base.endsWith('/') ? base : base + '/'
const url = `${normalizedBase}Anleitung.html?v=${Date.now()}` // cache-bust
// Fetch Anleitung.html relative to app base
let response = await fetch(url)
// Fallback: try relative path without base if first attempt failed
if (!response.ok) {
const fallbackUrl = `Anleitung.html?v=${Date.now()}`
response = await fetch(fallbackUrl)
}
} catch (error) {
// Letzter Fallback: Als Modal anzeigen
console.warn('Anleitung konnte nicht in neuem Fenster geöffnet werden:', error)
if (!response.ok) {
throw new Error(`Fehler beim Laden der Anleitung (${response.status}): ${response.url}`)
}
const anleitungContent = await response.text()
// Display the content in the modal
setModalType('info')
setModalMessage(`
📋 Anleitung:
1. **Fand statt?** - Wählen Sie "ja" oder "nein"
2. **Bei "ja":**
- Anzahl Besucher eingeben
- Spenden-Art auswählen
- Bei Barspende: Betrag eingeben
- Optional: Bemerkungen
3. **Bei "nein":**
- "abgesagt" oder "verschoben" wählen
- Bei verschoben: neues Datum eingeben
4. **Senden** - Speichert alle Daten im System
`)
setModalMessage(anleitungContent)
setIsModalHtml(true)
setShowModal(true)
} catch (error) {
console.error('Fehler beim Laden der Anleitung:', error)
setModalType('error')
setModalMessage('❌ Anleitung konnte nicht geladen werden.')
setIsModalHtml(false)
setIsWideModal(false)
setShowModal(true)
}
}
@@ -244,6 +329,7 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
setModalMessage('')
setIsModalHtml(false)
setIsSuccessModal(false)
setIsWideModal(false)
}
}
@@ -282,6 +368,7 @@ export default function LastButtons({ mitSend, mitBack, handleBack}) {
onClose={closeModal}
type={modalType}
isHtml={isModalHtml}
className={isWideModal ? 'custom-modal' : ''}
/>
)}

View File

@@ -2,7 +2,19 @@ import React from 'react'
// Import des CSS direkt hier
import './Modal.css'
export default function Modal({ isOpen = true, onClose, title, children, message, type = 'info', isHtml = false }) {
export default function Modal({
isOpen = true,
onClose,
title,
children,
message,
type = 'info',
isHtml = false,
className,
style,
bodyClassName,
bodyStyle
}) {
if (!isOpen) return null
const handleOverlayClick = (e) => {
@@ -33,7 +45,7 @@ export default function Modal({ isOpen = true, onClose, title, children, message
// CSS-Klasse basierend auf type
const getModalClass = () => {
return `modal-content modal-${type}`
return `modal-content modal-${type}${className ? ' ' + className : ''}`
}
const displayTitle = title || getDefaultTitle()
@@ -57,12 +69,12 @@ export default function Modal({ isOpen = true, onClose, title, children, message
return (
<div className="modal-overlay" onClick={handleOverlayClick} onKeyDown={handleKeyDown} tabIndex={0}>
<div className={getModalClass()}>
<div className={getModalClass()} style={style}>
<div className="modal-header">
<h3 className="modal-title">{displayTitle}</h3>
<button className="modal-close" onClick={onClose}>&times;</button>
</div>
<div className="modal-body">
<div className={`modal-body${bodyClassName ? ' ' + bodyClassName : ''}`} style={bodyStyle}>
{getDisplayContent()}
</div>
<div className="modal-footer">