Fast Alles drin außer echtes Abspeicher und Backtickern
This commit is contained in:
140
src/App.jsx
140
src/App.jsx
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { FormProvider, useFormData } from './FormContext'
|
||||
import './App.css'
|
||||
import FandStattVer from './components/FandStattVer.jsx'
|
||||
@@ -11,11 +11,14 @@ import Verschoben from './components/Verschoben.jsx'
|
||||
|
||||
|
||||
function AppContent() {
|
||||
const datum = "2025-10-23"
|
||||
const name = "Meiehofer"
|
||||
// States für Backend-Daten
|
||||
const [datum, setDatum] = useState("")
|
||||
const [name, setName] = useState("")
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState(null)
|
||||
|
||||
const version = "1.0.0"
|
||||
const vdate = "2025-11-23"
|
||||
//const isBar = true
|
||||
|
||||
// States
|
||||
const [schritt, setSchritt] = useState(0)
|
||||
@@ -24,6 +27,82 @@ function AppContent() {
|
||||
// Hole formData aus dem Context
|
||||
const { formData } = useFormData()
|
||||
|
||||
// URL-Parameter und Backend-Aufruf
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
// API URL aus Environment Variable laden
|
||||
const APIURL = import.meta.env.VITE_API_URL
|
||||
|
||||
if (!APIURL) {
|
||||
throw new Error('API URL nicht konfiguriert. Bitte VITE_API_URL in .env Datei setzen.')
|
||||
}
|
||||
|
||||
try {
|
||||
// URL-Parameter auslesen
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const id = urlParams.get('id')
|
||||
|
||||
if (!id) {
|
||||
throw new Error('Keine ID in der URL gefunden. Bitte rufen Sie die Seite mit ?id=123 auf.')
|
||||
}
|
||||
|
||||
console.log('Loading data for ID:', id)
|
||||
|
||||
// Backend-Aufruf mit Proxy
|
||||
const formData = new FormData()
|
||||
formData.append('cmd', 'GET_ONE')
|
||||
formData.append('id', id)
|
||||
|
||||
// HTTP Basic Authentication Header erstellen
|
||||
const username = import.meta.env.VITE_API_USERNAME
|
||||
const password = import.meta.env.VITE_API_PASSWORD
|
||||
const headers = {}
|
||||
|
||||
if (username && password) {
|
||||
const credentials = btoa(`${username}:${password}`)
|
||||
headers['Authorization'] = `Basic ${credentials}`
|
||||
}
|
||||
|
||||
const response = await fetch(APIURL, {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: formData
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Daten konnten nicht geladen werden. Server-Fehler: ${response.status}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
console.log('Received data:', data) // Debug-Ausgabe
|
||||
|
||||
// Anpassung an die Datenbankfelder der SoFue2 Tabelle
|
||||
if (!data.wtermin || !data.name) {
|
||||
throw new Error('Unvollständige Daten vom Server erhalten.')
|
||||
}
|
||||
|
||||
// Daten aus Backend setzen
|
||||
// wtermin ist vermutlich ein datetime, also nur das Datum extrahieren
|
||||
const terminDate = new Date(data.wtermin)
|
||||
const formatiertesDatum = terminDate.toLocaleDateString('de-DE')
|
||||
|
||||
setDatum(formatiertesDatum)
|
||||
setName(data.name + (data.vorname ? ' ' + data.vorname : ''))
|
||||
|
||||
console.log('Data loaded:', data)
|
||||
setLoading(false)
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error loading data:', err)
|
||||
setError(err.message)
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchData()
|
||||
}, []) // Leere Dependency-Array = nur beim ersten Laden ausführen
|
||||
|
||||
|
||||
// Callbacks:
|
||||
const handleFandStattVerNext = (auswahl) => {
|
||||
@@ -92,13 +171,13 @@ function AppContent() {
|
||||
}
|
||||
|
||||
// Schritt 2: Ende wenn abgesagt bzw. neues Datum bei verschoben
|
||||
if (schritt >= 2 && formData.stattgefunden === 'verschoben') {
|
||||
if (schritt >= 2 && formData.abgesagt === 'verschoben') {
|
||||
components.push(<Verschoben key='verschoben' onNext={handleNext} isCompleted={schritt > 2} />
|
||||
)
|
||||
}
|
||||
|
||||
// Schritt 4 (bei verschoben) oder Schritt 3 (bei absage): unterste Buttons
|
||||
const endeNeinSchritt = (formData.stattgefunden === 'verschoben') ? 3 : 2
|
||||
const endeNeinSchritt = (formData.abgesagt === 'verschoben') ? 3 : 2
|
||||
if (schritt >= endeNeinSchritt) {
|
||||
components.push(<LastButtons key='lastbutt' />
|
||||
)
|
||||
@@ -108,6 +187,55 @@ function AppContent() {
|
||||
return components
|
||||
}
|
||||
|
||||
// Loading und Error States
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="wrapper">
|
||||
<div>
|
||||
<h2 className="topline">Lade Daten...</h2>
|
||||
<p>Bitte warten Sie, während die Führungsdaten geladen werden.</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="wrapper">
|
||||
<div>
|
||||
<h2 className="nachbearbeitung" style={{backgroundColor: '#ff6b6b', color: 'white'}}>
|
||||
Fehler beim Laden der Daten
|
||||
</h2>
|
||||
<div style={{padding: '20px', textAlign: 'left'}}>
|
||||
<h3>❌ Die Anwendung kann nicht gestartet werden</h3>
|
||||
<p><strong>Grund:</strong> {error}</p>
|
||||
<hr />
|
||||
<h4>Mögliche Lösungen:</h4>
|
||||
<ul>
|
||||
<li>Überprüfen Sie die URL - sie sollte eine ID enthalten (z.B. ?id=123)</li>
|
||||
<li>Stellen Sie sicher, dass das Backend erreichbar ist</li>
|
||||
<li>Kontaktieren Sie den Administrator</li>
|
||||
</ul>
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
style={{
|
||||
marginTop: '20px',
|
||||
padding: '10px 20px',
|
||||
backgroundColor: '#007bff',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '5px',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
Seite neu laden
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="wrapper">
|
||||
<div>
|
||||
|
||||
@@ -6,40 +6,42 @@ import { createContext, useContext, useState } from 'react'
|
||||
const FormContext = createContext()
|
||||
|
||||
export function FormProvider({ children }) {
|
||||
// console.log('🚀 FormProvider initialisiert')
|
||||
console.log('🚀 FormProvider initialisiert')
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
stattgefunden: '',
|
||||
besucherAnzahl: '',
|
||||
besucher: '', // war: besucherAnzahl
|
||||
spendenArt: '',
|
||||
barspende: '',
|
||||
betrag: '', // war: barspende
|
||||
bemerkungen: '',
|
||||
neuertermin: '',
|
||||
neuesDatum: '', // war: neuertermin
|
||||
abgesagt: '', // für abgesagt/verschoben
|
||||
// Weitere Felder können hier hinzugefügt werden
|
||||
})
|
||||
|
||||
const updateFormData = (field, value) => {
|
||||
//console.log('📝 FormContext UPDATE:', field, '=', value)
|
||||
console.log('📝 FormContext UPDATE:', field, '=', value)
|
||||
setFormData(prev => {
|
||||
const newData = {
|
||||
...prev,
|
||||
[field]: value
|
||||
}
|
||||
//console.log('📊 FormContext NEU:', newData)
|
||||
console.log('📊 FormContext NEU:', newData)
|
||||
return newData
|
||||
})
|
||||
}
|
||||
|
||||
const resetFormData = () => {
|
||||
//console.log('🔄 FormContext RESET')
|
||||
console.log('🔄 FormContext RESET')
|
||||
|
||||
setFormData({
|
||||
stattgefunden: '',
|
||||
besucherAnzahl: '',
|
||||
besucher: '',
|
||||
spendenArt: '',
|
||||
barspende: '',
|
||||
betrag: '',
|
||||
bemerkungen: '',
|
||||
neuertermin: ''
|
||||
neuesDatum: '',
|
||||
abgesagt: ''
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -12,13 +12,32 @@ export default function Bemerkungen({ onNext, isCompleted }) {
|
||||
onNext()
|
||||
}
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
// Ctrl+Enter oder Cmd+Enter zum Speichern
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||
handleOK()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section id="bemerkungen">
|
||||
<h3>Bemerkungen (optional):</h3>
|
||||
<div className="bemerkdiv">
|
||||
<textarea className="beminfeld" />
|
||||
<button className="okbutton" onClick={handleOK}>OK</button>
|
||||
<textarea
|
||||
className="beminfeld"
|
||||
value={wert}
|
||||
onChange={(e) => setWert(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="Hier können Sie optionale Bemerkungen zur Führung eingeben..."
|
||||
disabled={isCompleted}
|
||||
/>
|
||||
<button
|
||||
className="okbutton"
|
||||
onClick={handleOK}
|
||||
disabled={isCompleted}
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ export default function BesucherBar({ title, euro, onNext, isCompleted }) {
|
||||
|
||||
const { formData, updateFormData } = useFormData()
|
||||
|
||||
// Bestimme das Feld basierend auf dem Titel
|
||||
const fieldName = title.includes('Barspende') ? 'barspende' : 'besucherAnzahl'
|
||||
// Bestimme Feldname basierend auf dem title
|
||||
const fieldName = title.includes('Barspende') ? 'betrag' : 'besucher'
|
||||
|
||||
const [wert, setWert] = useState(formData[fieldName] || '')
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
|
||||
65
src/components/ConfirmModal.jsx
Normal file
65
src/components/ConfirmModal.jsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import React from 'react'
|
||||
import './Modal.css'
|
||||
|
||||
export default function ConfirmModal({ isOpen = true, onClose, onConfirm, title, message, type = 'warning' }) {
|
||||
if (!isOpen) return null
|
||||
|
||||
const handleOverlayClick = (e) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
onClose()
|
||||
}
|
||||
if (e.key === 'Enter') {
|
||||
onConfirm()
|
||||
}
|
||||
}
|
||||
|
||||
const getDefaultTitle = () => {
|
||||
switch(type) {
|
||||
case 'danger': return 'Achtung'
|
||||
case 'warning': return 'Bestätigung erforderlich'
|
||||
case 'info': return 'Information'
|
||||
default: return 'Bestätigung'
|
||||
}
|
||||
}
|
||||
|
||||
const getModalClass = () => {
|
||||
return `modal-content modal-${type}`
|
||||
}
|
||||
|
||||
const displayTitle = title || getDefaultTitle()
|
||||
|
||||
return (
|
||||
<div className="modal-overlay" onClick={handleOverlayClick} onKeyDown={handleKeyDown} tabIndex={0}>
|
||||
<div className={getModalClass()}>
|
||||
<div className="modal-header">
|
||||
<h3 className="modal-title">{displayTitle}</h3>
|
||||
<button className="modal-close" onClick={onClose}>×</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p style={{whiteSpace: 'pre-line'}}>{message}</p>
|
||||
</div>
|
||||
<div className="modal-footer confirm-buttons">
|
||||
<button
|
||||
className="modal-button modal-button-secondary"
|
||||
onClick={onClose}
|
||||
>
|
||||
Nein
|
||||
</button>
|
||||
<button
|
||||
className="modal-button modal-button-danger"
|
||||
onClick={onConfirm}
|
||||
autoFocus
|
||||
>
|
||||
OK Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -4,12 +4,15 @@ import Modal from './Modal'
|
||||
|
||||
export default function FandStattVer({left, right, title, onNext, radioName = "fst"}) {
|
||||
const { formData, updateFormData } = useFormData()
|
||||
const [auswahl, setAuswahl] = useState(formData.stattgefunden || '')
|
||||
|
||||
// Bestimme das Feld basierend auf radioName
|
||||
const fieldName = radioName === 'abgesagt' ? 'abgesagt' : 'stattgefunden'
|
||||
const [auswahl, setAuswahl] = useState(formData[fieldName] || '')
|
||||
|
||||
const handleRadioChange = (e) => {
|
||||
const value = e.target.value
|
||||
setAuswahl(value)
|
||||
updateFormData('stattgefunden', value)
|
||||
updateFormData(fieldName, value)
|
||||
onNext(value)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,222 @@
|
||||
import { useState } from 'react'
|
||||
import { useFormData } from '../FormContext'
|
||||
import Modal from './Modal'
|
||||
import ConfirmModal from './ConfirmModal'
|
||||
|
||||
export default function LastButtons() {
|
||||
|
||||
const { formData } = 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 handleSenden = () => {
|
||||
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) {
|
||||
case 'bar':
|
||||
backendData.append('bezahlt', `Kasse ${formData.betrag}€)`)
|
||||
break
|
||||
case 'ueber':
|
||||
backendData.append('bezahlt', 'Überweisung')
|
||||
break
|
||||
case 'kasse':
|
||||
backendData.append('bezahlt', 'Spendenkässle')
|
||||
break
|
||||
case 'keine':
|
||||
backendData.append('bezahlt', 'keine')
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
} else if (formData.stattgefunden === 'nein') {
|
||||
backendData.append('stattgefunden', '0')
|
||||
backendData.append('bezahlt', 'keine')
|
||||
|
||||
// Grund für Ausfall
|
||||
if (formData.abgesagt === 'abgesagt') {
|
||||
backendData.append('status', 3)
|
||||
} else if (formData.abgesagt === 'verschoben') {
|
||||
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)
|
||||
console.log("URL ID:", id)
|
||||
console.log("Backend FormData Inhalt:")
|
||||
for (let [key, value] of backendData.entries()) {
|
||||
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) {
|
||||
setModalType('success')
|
||||
setModalMessage('✅ Daten erfolgreich gespeichert!')
|
||||
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')
|
||||
}
|
||||
*/
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern:', error)
|
||||
setModalType('error')
|
||||
setModalMessage(`❌ Fehler beim Speichern: ${error.message}`)
|
||||
setShowModal(true)
|
||||
} finally {
|
||||
setIsSending(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAbbruch = () => {
|
||||
console.log("Abbruch")
|
||||
setShowConfirmModal(true)
|
||||
}
|
||||
|
||||
const confirmAbbruch = () => {
|
||||
setShowConfirmModal(false)
|
||||
// Zurück zur vorherigen Seite oder Startseite
|
||||
window.history.back()
|
||||
}
|
||||
|
||||
const cancelAbbruch = () => {
|
||||
setShowConfirmModal(false)
|
||||
}
|
||||
|
||||
const handleAnleitung = () => {
|
||||
console.log("Zeige Anleitung")
|
||||
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
|
||||
`)
|
||||
setShowModal(true)
|
||||
}
|
||||
|
||||
const closeModal = () => {
|
||||
setShowModal(false)
|
||||
setModalMessage('')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="lastbuttons">
|
||||
<button className="btnabbruch" onClick = {handleAbbruch}>Abbruch</button>
|
||||
<button className="btnanleit" onClick = {handleAnleitung}>Anleitung</button>
|
||||
<button className="btnsend" onClick = {handleSenden}>Senden</button>
|
||||
</div>
|
||||
<>
|
||||
<div className="lastbuttons">
|
||||
<button className="btnabbruch" onClick={handleAbbruch}>
|
||||
Abbruch
|
||||
</button>
|
||||
<button className="btnanleit" onClick={handleAnleitung}>
|
||||
Anleitung
|
||||
</button>
|
||||
<button
|
||||
className="btnsend"
|
||||
onClick={handleSenden}
|
||||
disabled={isSending}
|
||||
>
|
||||
{isSending ? 'Speichert...' : 'Senden'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{showModal && (
|
||||
<Modal
|
||||
message={modalMessage}
|
||||
onClose={closeModal}
|
||||
type={modalType}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showConfirmModal && (
|
||||
<ConfirmModal
|
||||
message={`Möchten Sie wirklich abbrechen?
|
||||
|
||||
Alle eingegebenen Daten gehen verloren und werden nicht gespeichert.`}
|
||||
onClose={cancelAbbruch}
|
||||
onConfirm={confirmAbbruch}
|
||||
type="warning"
|
||||
title="Vorgang abbrechen?"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -25,6 +25,61 @@
|
||||
animation: modalSlideIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
/* Modal Type Variants */
|
||||
.modal-success {
|
||||
border-left: 5px solid #28a745;
|
||||
}
|
||||
|
||||
.modal-error {
|
||||
border-left: 5px solid #dc3545;
|
||||
}
|
||||
|
||||
.modal-warning {
|
||||
border-left: 5px solid #ffc107;
|
||||
}
|
||||
|
||||
.modal-info {
|
||||
border-left: 5px solid #17a2b8;
|
||||
}
|
||||
|
||||
/* Type-specific header colors */
|
||||
.modal-success .modal-header {
|
||||
background: #d4edda;
|
||||
border-bottom-color: #c3e6cb;
|
||||
}
|
||||
|
||||
.modal-error .modal-header {
|
||||
background: #f8d7da;
|
||||
border-bottom-color: #f5c6cb;
|
||||
}
|
||||
|
||||
.modal-warning .modal-header {
|
||||
background: #fff3cd;
|
||||
border-bottom-color: #ffeaa7;
|
||||
}
|
||||
|
||||
.modal-info .modal-header {
|
||||
background: #d1ecf1;
|
||||
border-bottom-color: #bee5eb;
|
||||
}
|
||||
|
||||
/* Type-specific title colors */
|
||||
.modal-success .modal-title {
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.modal-error .modal-title {
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.modal-warning .modal-title {
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.modal-info .modal-title {
|
||||
color: #0c5460;
|
||||
}
|
||||
|
||||
@keyframes modalSlideIn {
|
||||
from {
|
||||
transform: scale(0.9) translateY(-10px);
|
||||
@@ -88,6 +143,20 @@
|
||||
border-top: 1px solid #e9ecef;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Confirm Modal - Multiple Buttons */
|
||||
.modal-footer.confirm-buttons {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-footer.confirm-buttons .modal-button:first-child {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.modal-footer.confirm-buttons .modal-button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-button {
|
||||
@@ -112,6 +181,30 @@
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.modal-button-secondary {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal-button-secondary:hover {
|
||||
background: #545862;
|
||||
}
|
||||
|
||||
.modal-button-danger {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal-button-danger:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
/* Type-specific button for single button modals */
|
||||
.modal-content:not(.modal-confirm) .modal-footer {
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 480px) {
|
||||
.modal-content {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
// Import des CSS direkt hier
|
||||
import './Modal.css'
|
||||
|
||||
export default function Modal({ isOpen, onClose, title, children }) {
|
||||
export default function Modal({ isOpen = true, onClose, title, children, message, type = 'info' }) {
|
||||
if (!isOpen) return null
|
||||
|
||||
const handleOverlayClick = (e) => {
|
||||
@@ -20,15 +20,34 @@ export default function Modal({ isOpen, onClose, title, children }) {
|
||||
}
|
||||
}
|
||||
|
||||
// Automatischer Titel basierend auf type
|
||||
const getDefaultTitle = () => {
|
||||
switch(type) {
|
||||
case 'success': return 'Erfolg'
|
||||
case 'error': return 'Fehler'
|
||||
case 'warning': return 'Warnung'
|
||||
case 'info': return 'Information'
|
||||
default: return 'Meldung'
|
||||
}
|
||||
}
|
||||
|
||||
// CSS-Klasse basierend auf type
|
||||
const getModalClass = () => {
|
||||
return `modal-content modal-${type}`
|
||||
}
|
||||
|
||||
const displayTitle = title || getDefaultTitle()
|
||||
const displayContent = message ? <p style={{whiteSpace: 'pre-line'}}>{message}</p> : children
|
||||
|
||||
return (
|
||||
<div className="modal-overlay" onClick={handleOverlayClick} onKeyDown={handleKeyDown} tabIndex={0}>
|
||||
<div className="modal-content">
|
||||
<div className={getModalClass()}>
|
||||
<div className="modal-header">
|
||||
<h3 className="modal-title">{title}</h3>
|
||||
<h3 className="modal-title">{displayTitle}</h3>
|
||||
<button className="modal-close" onClick={onClose}>×</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{children}
|
||||
{displayContent}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button className="modal-button" onClick={onClose} autoFocus>OK</button>
|
||||
|
||||
@@ -6,12 +6,12 @@ export default function Verschoben({onNext, isCompleted}) {
|
||||
const { formData, updateFormData } = useFormData()
|
||||
|
||||
// State für das selektierte Datum
|
||||
const [selectedDate, setSelectedDate] = useState(formData.neuertermin || '')
|
||||
const [selectedDate, setSelectedDate] = useState(formData.neuesDatum || '')
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
|
||||
const handleOK = () => {
|
||||
if (selectedDate) {
|
||||
updateFormData('neuertermin', selectedDate)
|
||||
updateFormData('neuesDatum', selectedDate)
|
||||
onNext()
|
||||
} else {
|
||||
setShowModal(true)
|
||||
@@ -40,7 +40,12 @@ export default function Verschoben({onNext, isCompleted}) {
|
||||
type="datetime-local"
|
||||
id="datetime"
|
||||
value={selectedDate}
|
||||
onChange={(e) => setSelectedDate(e.target.value)}
|
||||
onChange={(e) => {
|
||||
let selD = e.target.value
|
||||
selD = selD + ':00'
|
||||
selD = selD.replace('T',' ')
|
||||
setSelectedDate(selD)
|
||||
}}
|
||||
onKeyDown={handleKeyDown}
|
||||
min={now.toISOString().slice(0,16)}
|
||||
disabled={isCompleted}
|
||||
|
||||
Reference in New Issue
Block a user