DB4js als _all zusammengefasst
Beoanswer ist nun OK
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "beoanswer_react",
|
||||
"private": true,
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -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:
|
||||
|
||||
14
sternwarte/beoanswer/src/components/LastButtons.css
Normal file
14
sternwarte/beoanswer/src/components/LastButtons.css
Normal 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 */
|
||||
}
|
||||
@@ -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' : ''}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -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}>×</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className={`modal-body${bodyClassName ? ' ' + bodyClassName : ''}`} style={bodyStyle}>
|
||||
{getDisplayContent()}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
|
||||
Reference in New Issue
Block a user