V 1.0.0 erste sauber lauffähige Version

This commit is contained in:
rxf
2025-10-29 14:01:16 +01:00
parent 8fb01360be
commit 4aa6ab3eb5
5 changed files with 206 additions and 78 deletions

View File

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

View File

@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { FormProvider, useFormData } from './FormContext' import { FormProvider, useFormData } from './FormContext'
import packageJson from '../package.json'
import './App.css' import './App.css'
import FandStattVer from './components/FandStattVer.jsx' import FandStattVer from './components/FandStattVer.jsx'
import BesucherBar from './components/BesucherBar.jsx' import BesucherBar from './components/BesucherBar.jsx'
@@ -16,16 +17,18 @@ function AppContent() {
const [name, setName] = useState("") const [name, setName] = useState("")
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [error, setError] = useState(null) const [error, setError] = useState(null)
const [mitsend, setMitsend] = useState(false)
const [mitback, setMitback] = useState(false)
const version = "1.0.0" const version = packageJson.version
const vdate = "2025-11-23" const vdate = new Date().toLocaleDateString('de-DE')
// States // States
const [schritt, setSchritt] = useState(0) const [schritt, setSchritt] = useState(0)
const [pfad, setPfad] = useState('') const [pfad, setPfad] = useState('')
// Hole formData aus dem Context // Hole formData aus dem Context
const { formData } = useFormData() const { formData, updateFormData } = useFormData()
// URL-Parameter und Backend-Aufruf // URL-Parameter und Backend-Aufruf
useEffect(() => { useEffect(() => {
@@ -106,24 +109,76 @@ function AppContent() {
// Callbacks: // Callbacks:
const handleFandStattVerNext = (auswahl) => { const handleFandStattVerNext = (auswahl) => {
setPfad(auswahl) auswahl && setPfad(auswahl)
setSchritt(1) handleNext()
} }
const handleNext = () => { const handleNext = () => {
setSchritt((schritt) => schritt + 1) setSchritt((schritt) => schritt + 1)
} }
const handleBack = () => {
if (schritt > 0) {
const neuerSchritt = schritt - 1
setSchritt(neuerSchritt)
// Entsprechende FormData-Felder zurücksetzen je nach Schritt und Pfad
if (pfad === 'ja') {
// JA-Pfad rückwärts
if (schritt === 1) {
// Von Besucher zurück zu ja/nein → Pfad löschen
setPfad('')
updateFormData('stattgefunden', '')
} else if (schritt === 2) {
// Von Spende zurück zu Besucher → Besucher löschen
updateFormData('besucher', '')
} else if (schritt === 3) {
// Von Betrag/Bemerkungen zurück zu Spende → Spende löschen
updateFormData('spendenArt', '')
} else if (schritt === 4) {
// Von Bemerkungen zurück → Betrag löschen (bei Bar-Spende)
updateFormData('betrag', '')
} else if (schritt === 5) {
// Von Senden zurück → Bemerkungen löschen
updateFormData('bemerkungen', '')
}
} else if (pfad === 'nein') {
// NEIN-Pfad rückwärts
if (schritt === 1) {
// Von abgesagt/verschoben zurück zu ja/nein → Pfad löschen
setPfad('')
updateFormData('stattgefunden', '')
} else if (schritt === 2) {
// Von Datum/Senden zurück zu abgesagt/verschoben → abgesagt löschen
updateFormData('abgesagt', '')
} else if (schritt === 3) {
// Von Senden zurück → neues Datum löschen (bei verschoben)
updateFormData('neuesDatum', '')
}
}
}
}
const setBackButton = () => {
setMitback(true)
}
// Welche Komponeneten werden angezeigt: // Welche Komponeneten werden angezeigt:
const renderCoponents = () => { const renderCoponents = () => {
const components = [] const components = []
// Schritt 0: ja/nein - Auswahl // Schritt 0: ja/nein - Auswahl
components.push( components.push(
<FandStattVer key="fandstatt" left='ja' right='nein' title='Fand die Führung statt?' onNext={handleFandStattVerNext} iscompleted={schritt > 1} /> <FandStattVer key="fandstatt" left='ja' right='nein' title='Fand die Führung statt?'
onNext={handleFandStattVerNext}
setbackButton={setBackButton}
iscompleted={schritt > 1} />
) )
if (schritt === 0) if (schritt === 0) {
// Bei ja/nein Auswahl: Kein Zurück-Button, kein Senden-Button
components.push(<LastButtons key='lastbutt' mitSend={false} mitBack={false} handleBack={handleBack}/>)
return components return components
}
// JA-Pfad: // JA-Pfad:
if (pfad === 'ja') { if (pfad === 'ja') {
@@ -152,13 +207,6 @@ function AppContent() {
) )
} }
// Schritt 5 (bei Bar-Spende) oder Schritt 4 (bei anderen Spenden): unterste Buttons
const endeSchritt = (formData.spendenArt === 'bar') ? 5 : 4
if (schritt >= endeSchritt) {
components.push(<LastButtons key='lastbutt' />
)
}
} }
// NEIN - Pfad // NEIN - Pfad
if (pfad === 'nein') { if (pfad === 'nein') {
@@ -166,7 +214,10 @@ function AppContent() {
// Schritt 1: abgesagt / verschoben // Schritt 1: abgesagt / verschoben
if (schritt >= 1) { if (schritt >= 1) {
components.push( components.push(
<FandStattVer key="abgesagt" left='abgesagt' right='verschoben' title='Die Führung wurde' radioName='abgesagt' onNext={handleNext} iscompleted={schritt > 1} /> <FandStattVer key="abgesagt" left='abgesagt' right='verschoben' title='Die Führung wurde' radioName='abgesagt'
onNext={handleNext}
setbackButton={setBackButton}
iscompleted={schritt > 1} />
) )
} }
@@ -176,14 +227,31 @@ function AppContent() {
) )
} }
// Schritt 4 (bei verschoben) oder Schritt 3 (bei absage): unterste Buttons
const endeNeinSchritt = (formData.abgesagt === 'verschoben') ? 3 : 2
if (schritt >= endeNeinSchritt) {
components.push(<LastButtons key='lastbutt' />
)
}
} }
// Zurück-Button nur anzeigen wenn nicht bei ja/nein Auswahl
const backVerfuegbar = schritt > 0
// LastButtons IMMER anzeigen, aber Senden-Button nur wenn bereit
const sendenBereit = () => {
if (pfad === 'ja') {
// JA-Pfad: vollständig wenn Bemerkungen-Schritt erreicht
const bemerkungsSchritt = (formData.spendenArt === 'bar') ? 4 : 3
return schritt >= bemerkungsSchritt
} else if (pfad === 'nein') {
// NEIN-Pfad: vollständig wenn abgesagt ODER verschoben mit Datum
if (formData.abgesagt === 'abgesagt') {
return schritt >= 2
} else if (formData.abgesagt === 'verschoben') {
return schritt >= 3 && formData.neuesDatum
}
}
return false
}
// LastButtons immer anzeigen
components.push(<LastButtons key='lastbutt' mitSend={sendenBereit()} mitBack={backVerfuegbar} handleBack={handleBack} />)
return components return components
} }

View File

@@ -6,7 +6,6 @@ import { createContext, useContext, useState } from 'react'
const FormContext = createContext() const FormContext = createContext()
export function FormProvider({ children }) { export function FormProvider({ children }) {
console.log('🚀 FormProvider initialisiert')
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
stattgefunden: '', stattgefunden: '',
@@ -20,19 +19,16 @@ export function FormProvider({ children }) {
}) })
const updateFormData = (field, value) => { const updateFormData = (field, value) => {
console.log('📝 FormContext UPDATE:', field, '=', value)
setFormData(prev => { setFormData(prev => {
const newData = { const newData = {
...prev, ...prev,
[field]: value [field]: value
} }
console.log('📊 FormContext NEU:', newData)
return newData return newData
}) })
} }
const resetFormData = () => { const resetFormData = () => {
console.log('🔄 FormContext RESET')
setFormData({ setFormData({
stattgefunden: '', stattgefunden: '',

View File

@@ -2,7 +2,7 @@ import { useState } from 'react'
import { useFormData } from '../FormContext' import { useFormData } from '../FormContext'
import Modal from './Modal' import Modal from './Modal'
export default function FandStattVer({left, right, title, onNext, radioName = "fst"}) { export default function FandStattVer({left, right, title, onNext, radioName = "fst", setbackButton}) {
const { formData, updateFormData } = useFormData() const { formData, updateFormData } = useFormData()
// Bestimme das Feld basierend auf radioName // Bestimme das Feld basierend auf radioName
@@ -11,9 +11,14 @@ export default function FandStattVer({left, right, title, onNext, radioName = "f
const handleRadioChange = (e) => { const handleRadioChange = (e) => {
const value = e.target.value const value = e.target.value
setAuswahl(value)
updateFormData(fieldName, value) updateFormData(fieldName, value)
onNext(value) setbackButton(true)
if(radioName !== 'abgesagt') {
setAuswahl(value)
onNext(value)
} else {
onNext()
}
} }
return ( return (

View File

@@ -3,51 +3,52 @@ import { useFormData } from '../FormContext'
import Modal from './Modal' import Modal from './Modal'
import ConfirmModal from './ConfirmModal' import ConfirmModal from './ConfirmModal'
export default function LastButtons() { export default function LastButtons({ mitSend, mitBack, handleBack}) {
const { formData } = useFormData() const { formData, resetFormData } = useFormData()
const [isSending, setIsSending] = useState(false) const [isSending, setIsSending] = useState(false)
const [showModal, setShowModal] = useState(false) const [showModal, setShowModal] = useState(false)
const [modalMessage, setModalMessage] = useState('') const [modalMessage, setModalMessage] = useState('')
const [modalType, setModalType] = useState('error') // 'error' oder 'success' const [modalType, setModalType] = useState('error') // 'error' oder 'success'
const [showConfirmModal, setShowConfirmModal] = useState(false) const [showConfirmModal, setShowConfirmModal] = useState(false)
const [isSuccessModal, setIsSuccessModal] = useState(false)
const handleSenden = async () => { const handleSenden = async () => {
console.log("Alle Formulardaten: ", formData) console.log("Alle Formulardaten: ", formData)
setIsSending(true) setIsSending(true)
try { try {
// API URL und Auth-Daten aus Environment // API URL und Auth-Daten aus Environment
const APIURL = import.meta.env.VITE_API_URL const APIURL = import.meta.env.VITE_API_URL
const username = import.meta.env.VITE_API_USERNAME const username = import.meta.env.VITE_API_USERNAME
const password = import.meta.env.VITE_API_PASSWORD const password = import.meta.env.VITE_API_PASSWORD
if (!APIURL) { if (!APIURL) {
throw new Error('API URL nicht konfiguriert.') throw new Error('API URL nicht konfiguriert.')
} }
// URL-Parameter für ID auslesen // URL-Parameter für ID auslesen
const urlParams = new URLSearchParams(window.location.search) const urlParams = new URLSearchParams(window.location.search)
const id = urlParams.get('id') const id = urlParams.get('id')
if (!id) { if (!id) {
throw new Error('Keine ID in der URL gefunden.') throw new Error('Keine ID in der URL gefunden.')
} }
// FormData für PHP Backend erstellen // FormData für PHP Backend erstellen
const backendData = new FormData() const backendData = new FormData()
backendData.append('cmd', 'UPDATEAFTER') backendData.append('cmd', 'UPDATEAFTER')
backendData.append('id', id) backendData.append('id', id)
// Formulardaten zu Backend-Feldern mappen // Formulardaten zu Backend-Feldern mappen
// Basis-Status // Basis-Status
if (formData.stattgefunden === 'ja') { if (formData.stattgefunden === 'ja') {
backendData.append('stattgefunden', '1') backendData.append('stattgefunden', '1')
// Spenden-Informationen // Spenden-Informationen
if (formData.spendenArt) { if (formData.spendenArt) {
switch(formData.spendenArt) { switch (formData.spendenArt) {
case 'bar': case 'bar':
backendData.append('bezahlt', `Kasse ${formData.betrag}€)`) backendData.append('bezahlt', `Kasse ${formData.betrag}€)`)
break break
@@ -62,7 +63,7 @@ export default function LastButtons() {
break break
} }
} }
} else if (formData.stattgefunden === 'nein') { } else if (formData.stattgefunden === 'nein') {
backendData.append('stattgefunden', '0') backendData.append('stattgefunden', '0')
backendData.append('bezahlt', 'keine') backendData.append('bezahlt', 'keine')
@@ -71,19 +72,19 @@ export default function LastButtons() {
if (formData.abgesagt === 'abgesagt') { if (formData.abgesagt === 'abgesagt') {
backendData.append('status', 3) backendData.append('status', 3)
} else if (formData.abgesagt === 'verschoben') { } else if (formData.abgesagt === 'verschoben') {
backendData.append('wtermin', formData.neuesDatum || '1900-01-01 00:00:00') backendData.append('wtermin', formData.neuesDatum || '1900-01-01 00:00:00')
} }
} }
// Bemerkungen // Bemerkungen
backendData.append('remark', formData.bemerkungen || '') backendData.append('remark', formData.bemerkungen || '')
// Besucher // Besucher
backendData.append('besucher', formData.besucher || '0') backendData.append('besucher', formData.besucher || '0')
// // Bearbeitungsdatum setzen // // Bearbeitungsdatum setzen
// const now = new Date().toISOString().slice(0, 19).replace('T', ' ') // const now = new Date().toISOString().slice(0, 19).replace('T', ' ')
// backendData.append('bearbeitet_am', now) // backendData.append('bearbeitet_am', now)
// Debug: FormData kann nicht direkt geloggt werden, deshalb iterieren // Debug: FormData kann nicht direkt geloggt werden, deshalb iterieren
console.log("=== FORM DATA DEBUG ===") console.log("=== FORM DATA DEBUG ===")
console.log("Original formData aus Context:", formData) console.log("Original formData aus Context:", formData)
@@ -93,46 +94,60 @@ export default function LastButtons() {
console.log(` ${key}: ${value}`) console.log(` ${key}: ${value}`)
} }
console.log("========================") console.log("========================")
/*
// HTTP Basic Authentication Header // HTTP Basic Authentication Header
const headers = {} const headers = {}
if (username && password) { if (username && password) {
const credentials = btoa(`${username}:${password}`) const credentials = btoa(`${username}:${password}`)
headers['Authorization'] = `Basic ${credentials}` headers['Authorization'] = `Basic ${credentials}`
} }
// Backend-Aufruf // Backend-Aufruf
const response = await fetch(APIURL, { const response = await fetch(APIURL, {
method: 'POST', method: 'POST',
headers: headers, headers: headers,
body: backendData body: backendData
}) })
if (!response.ok) { if (!response.ok) {
throw new Error(`Server-Fehler: ${response.status}`) throw new Error(`Server-Fehler: ${response.status}`)
} }
const result = await response.json() // Backend Response auslesen
const responseText = await response.text()
if (result.success) { console.log('Backend Response (raw):', responseText)
let result
try {
// Versuche JSON zu parsen
result = JSON.parse(responseText)
console.log('Backend Response (parsed):', result)
} catch (e) {
// Falls kein JSON, behandle als einfachen Text
console.log('Backend Response ist kein JSON, behandle als Text')
result = { success: responseText.trim() === 'true', raw: responseText }
}
// Erfolg prüfen - sowohl JSON als auch Text-Format unterstützen
const isSuccess = result.success === true ||
result.success === 'true' ||
responseText.trim() === 'true'
if (isSuccess) {
setModalType('success') setModalType('success')
setModalMessage('✅ Daten erfolgreich gespeichert!') setModalMessage('✅ Daten erfolgreich gespeichert!')
setIsSuccessModal(true)
setShowModal(true) setShowModal(true)
// Nach erfolgreicher Speicherung könnte man zur Übersicht zurück
setTimeout(() => {
// Optional: Weiterleitung oder Formular zurücksetzen
console.log('Erfolgreich gespeichert:', result)
}, 2000)
} else { } else {
throw new Error(result.message || 'Unbekannter Fehler beim Speichern') throw new Error(result.message || result.error || 'Unbekannter Fehler beim Speichern')
} }
*/
} catch (error) { } catch (error) {
console.error('Fehler beim Speichern:', error) console.error('Fehler beim Speichern:', error)
setModalType('error') setModalType('error')
setModalMessage(`❌ Fehler beim Speichern: ${error.message}`) setModalMessage(`❌ Fehler beim Speichern: ${error.message}`)
setIsSuccessModal(false)
setShowModal(true) setShowModal(true)
} finally { } finally {
setIsSending(false) setIsSending(false)
@@ -145,8 +160,17 @@ export default function LastButtons() {
const confirmAbbruch = () => { const confirmAbbruch = () => {
setShowConfirmModal(false) setShowConfirmModal(false)
// Zurück zur vorherigen Seite oder Startseite
window.history.back() // Versuche das Browser-Fenster zu schließen (wie beim Speichern)
window.close()
// Fallback: Falls window.close() nicht funktioniert
setTimeout(() => {
if (!window.closed) {
// Wenn das Fenster nicht geschlossen werden kann, zur vorherigen Seite
window.location.reload()
}
}, 100)
} }
const cancelAbbruch = () => { const cancelAbbruch = () => {
@@ -176,10 +200,50 @@ export default function LastButtons() {
} }
const closeModal = () => { const closeModal = () => {
setShowModal(false) if (isSuccessModal) {
setModalMessage('') // Bei erfolgreichem Speichern: Browser-Fenster schließen
console.log('Schließe Browser-Fenster nach erfolgreichem Speichern...')
// Formular zurücksetzen (für den Fall, dass das Schließen nicht funktioniert)
resetFormData()
// Browser-Fenster schließen
window.close()
// Fallback: Falls window.close() nicht funktioniert (z.B. bei direkt aufgerufenen URLs)
// Nach 100ms prüfen, ob das Fenster noch offen ist
setTimeout(() => {
// Wenn das Fenster noch offen ist, zur vorherigen Seite oder Neustart
if (!window.closed) {
console.log('Fenster konnte nicht geschlossen werden, führe Neustart durch...')
window.location.reload()
}
}, 100)
} else {
// Normales Modal schließen
setShowModal(false)
setModalMessage('')
setIsSuccessModal(false)
}
} }
const sendeButton = mitSend ?
<button
className="btnsend"
onClick={handleSenden}
disabled={isSending}
>
{isSending ? 'Speichert...' : 'Senden'}
</button>
: null
const backButton = mitBack ?
<button className="btnback" onClick={handleBack}>
Zurück
</button>
: null
return ( return (
<> <>
<div className="lastbuttons"> <div className="lastbuttons">
@@ -189,23 +253,18 @@ export default function LastButtons() {
<button className="btnanleit" onClick={handleAnleitung}> <button className="btnanleit" onClick={handleAnleitung}>
Anleitung Anleitung
</button> </button>
<button {backButton}
className="btnsend" {sendeButton}
onClick={handleSenden}
disabled={isSending}
>
{isSending ? 'Speichert...' : 'Senden'}
</button>
</div> </div>
{showModal && ( {showModal && (
<Modal <Modal
message={modalMessage} message={modalMessage}
onClose={closeModal} onClose={closeModal}
type={modalType} type={modalType}
/> />
)} )}
{showConfirmModal && ( {showConfirmModal && (
<ConfirmModal <ConfirmModal
message={`Möchten Sie wirklich abbrechen? message={`Möchten Sie wirklich abbrechen?