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",
"private": true,
"version": "0.0.0",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react'
import { FormProvider, useFormData } from './FormContext'
import packageJson from '../package.json'
import './App.css'
import FandStattVer from './components/FandStattVer.jsx'
import BesucherBar from './components/BesucherBar.jsx'
@@ -16,16 +17,18 @@ 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 version = "1.0.0"
const vdate = "2025-11-23"
const version = packageJson.version
const vdate = new Date().toLocaleDateString('de-DE')
// States
const [schritt, setSchritt] = useState(0)
const [pfad, setPfad] = useState('')
// Hole formData aus dem Context
const { formData } = useFormData()
const { formData, updateFormData } = useFormData()
// URL-Parameter und Backend-Aufruf
useEffect(() => {
@@ -106,24 +109,76 @@ function AppContent() {
// Callbacks:
const handleFandStattVerNext = (auswahl) => {
setPfad(auswahl)
setSchritt(1)
auswahl && setPfad(auswahl)
handleNext()
}
const handleNext = () => {
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:
const renderCoponents = () => {
const components = []
// Schritt 0: ja/nein - Auswahl
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
}
// JA-Pfad:
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
if (pfad === 'nein') {
@@ -166,7 +214,10 @@ function AppContent() {
// Schritt 1: abgesagt / verschoben
if (schritt >= 1) {
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
}

View File

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

View File

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

View File

@@ -3,51 +3,52 @@ import { useFormData } from '../FormContext'
import Modal from './Modal'
import ConfirmModal from './ConfirmModal'
export default function LastButtons() {
const { formData } = useFormData()
export default function LastButtons({ mitSend, mitBack, handleBack}) {
const { formData, resetFormData } = useFormData()
const [isSending, setIsSending] = useState(false)
const [showModal, setShowModal] = useState(false)
const [modalMessage, setModalMessage] = useState('')
const [modalType, setModalType] = useState('error') // 'error' oder 'success'
const [showConfirmModal, setShowConfirmModal] = useState(false)
const [isSuccessModal, setIsSuccessModal] = useState(false)
const handleSenden = async () => {
console.log("Alle Formulardaten: ", formData)
setIsSending(true)
try {
// API URL und Auth-Daten aus Environment
const APIURL = import.meta.env.VITE_API_URL
const username = import.meta.env.VITE_API_USERNAME
const password = import.meta.env.VITE_API_PASSWORD
if (!APIURL) {
throw new Error('API URL nicht konfiguriert.')
}
// URL-Parameter für ID auslesen
const urlParams = new URLSearchParams(window.location.search)
const id = urlParams.get('id')
if (!id) {
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)
// Formulardaten zu Backend-Feldern mappen
// Basis-Status
if (formData.stattgefunden === 'ja') {
backendData.append('stattgefunden', '1')
// Spenden-Informationen
if (formData.spendenArt) {
switch(formData.spendenArt) {
switch (formData.spendenArt) {
case 'bar':
backendData.append('bezahlt', `Kasse ${formData.betrag}€)`)
break
@@ -62,7 +63,7 @@ export default function LastButtons() {
break
}
}
} else if (formData.stattgefunden === 'nein') {
backendData.append('stattgefunden', '0')
backendData.append('bezahlt', 'keine')
@@ -71,19 +72,19 @@ export default function LastButtons() {
if (formData.abgesagt === 'abgesagt') {
backendData.append('status', 3)
} 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
backendData.append('remark', formData.bemerkungen || '')
// Besucher
backendData.append('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 ===")
console.log("Original formData aus Context:", formData)
@@ -93,46 +94,60 @@ export default function LastButtons() {
console.log(` ${key}: ${value}`)
}
console.log("========================")
/*
// HTTP Basic Authentication Header
const headers = {}
if (username && password) {
const credentials = btoa(`${username}:${password}`)
headers['Authorization'] = `Basic ${credentials}`
}
// Backend-Aufruf
const response = await fetch(APIURL, {
method: 'POST',
headers: headers,
body: backendData
})
if (!response.ok) {
throw new Error(`Server-Fehler: ${response.status}`)
}
const result = await response.json()
if (result.success) {
// Backend Response auslesen
const responseText = await response.text()
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')
setModalMessage('✅ Daten erfolgreich gespeichert!')
setIsSuccessModal(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 {
throw new Error(result.message || 'Unbekannter Fehler beim Speichern')
throw new Error(result.message || result.error || 'Unbekannter Fehler beim Speichern')
}
*/
} catch (error) {
console.error('Fehler beim Speichern:', error)
setModalType('error')
setModalMessage(`❌ Fehler beim Speichern: ${error.message}`)
setIsSuccessModal(false)
setShowModal(true)
} finally {
setIsSending(false)
@@ -145,8 +160,17 @@ export default function LastButtons() {
const confirmAbbruch = () => {
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 = () => {
@@ -176,10 +200,50 @@ export default function LastButtons() {
}
const closeModal = () => {
setShowModal(false)
setModalMessage('')
if (isSuccessModal) {
// 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 (
<>
<div className="lastbuttons">
@@ -189,23 +253,18 @@ export default function LastButtons() {
<button className="btnanleit" onClick={handleAnleitung}>
Anleitung
</button>
<button
className="btnsend"
onClick={handleSenden}
disabled={isSending}
>
{isSending ? 'Speichert...' : 'Senden'}
</button>
{backButton}
{sendeButton}
</div>
{showModal && (
<Modal
<Modal
message={modalMessage}
onClose={closeModal}
type={modalType}
/>
)}
{showConfirmModal && (
<ConfirmModal
message={`Möchten Sie wirklich abbrechen?