diff --git a/frontend/package.json b/frontend/package.json index 025dd33..55413ff 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,8 +2,8 @@ "name": "aerzte", "type": "module", "private": true, - "version": "1.0.0", - "vdate": "2025-11-23 11:00 UTC", + "version": "1.1.0", + "vdate": "2025-11-23 16:00 UTC", "scripts": { "dev": "vite", "build": "vite build", diff --git a/frontend/src/App.css_org b/frontend/src/App.css_org index b9d355d..321c699 100644 --- a/frontend/src/App.css_org +++ b/frontend/src/App.css_org @@ -40,3 +40,34 @@ .read-the-docs { color: #888; } + +/* In AppStyles.css */ + +/* Stellt den Delete-Button in die rechte Ecke des Headers */ +.appointment-header { + display: flex; + align-items: center; + justify-content: space-between; /* Verteilt Checkbox/Label und Button */ + margin-bottom: 10px; +} + +.doctor-info { + /* Nimmt den verfügbaren Platz ein */ + flex-grow: 1; +} + +.delete-button { + background: none; + border: none; + color: #dc3545; + font-size: 1.2em; + cursor: pointer; + margin-left: 15px; + padding: 0 5px; + transition: color 0.2s; +} + +.delete-button:hover { + color: #c82333; + transform: scale(1.1); +} \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 6f13dc1..5f17672 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import AppointmentForm from './components/AppointmentForm'; import AppointmentList from './components/AppointmentList'; +import ConfirmationModal from './components/ConfirmationModal'; import './AppStyles.css'; // Die Basis-URL der Express-API @@ -22,6 +23,8 @@ const normalizeAppointment = (appointment) => ({ function App() { const [appointments, setAppointments] = useState([]); const [editingAppointment, setEditingAppointment] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + const [appointmentToDeleteId, setAppointmentToDeleteId] = useState(null); // NEU: Lädt Termine vom Backend beim Start useEffect(() => { @@ -137,6 +140,50 @@ function App() { } }; +// 4. DELETE LOGIK START + + // 1. Wird vom Löschen-Button in der Liste aufgerufen (öffnet das Modal) + const openDeleteModal = (id) => { + setAppointmentToDeleteId(id); + setIsModalOpen(true); + }; + + // 2. Wird vom 'Abbrechen'-Button des Modals aufgerufen + const handleCancelDelete = () => { + setAppointmentToDeleteId(null); + setIsModalOpen(false); + }; + + // 3. Wird vom 'Bestätigen'-Button des Modals aufgerufen (führt die Löschung aus) + const handleConfirmDelete = async () => { + const id = appointmentToDeleteId; + + // Status zurücksetzen, bevor die asynchrone Funktion aufgerufen wird + setIsModalOpen(false); + setAppointmentToDeleteId(null); + + if (!id) return; // Sicherheit + + try { + const response = await fetch(`${API_URL}/${id}`, { + method: 'DELETE', + }); + + if (response.status !== 204) throw new Error('Fehler beim Löschen des Termins.'); + + // Lokalen State synchronisieren + setAppointments(prev => prev.filter(app => app.id !== id)); + + if (editingAppointment && editingAppointment.id === id) { + setEditingAppointment(null); + } + + } catch (error) { + console.error("Fehler beim Löschen des Termins:", error); + // Optional: Fehlermeldung anzeigen + } + }; + // ... (startEdit und cancelEdit bleiben unverändert, sie manipulieren nur den lokalen State) return ( @@ -153,6 +200,15 @@ function App() { appointments={appointments} onToggleDone={toggleDone} onEdit={(app) => setEditingAppointment(app)} + onDelete={openDeleteModal} + /> + {/* NEU: MODAL KOMPONENTE */} + {/* Debug Output kann nun entfernt werden */} diff --git a/frontend/src/components/AppointmentItem.jsx b/frontend/src/components/AppointmentItem.jsx index 151dd74..6b4a556 100644 --- a/frontend/src/components/AppointmentItem.jsx +++ b/frontend/src/components/AppointmentItem.jsx @@ -6,7 +6,7 @@ const formatDate = (date) => { }; // NEUE PROP: onEdit -const AppointmentItem = ({ appointment, onToggleDone, onEdit }) => { +const AppointmentItem = ({ appointment, onToggleDone, onEdit, onDelete }) => { const { id, arztName, arztArt, termin, erledigt, bemerkungen } = appointment; const itemClass = erledigt ? 'appointment-item done' : 'appointment-item'; @@ -28,6 +28,16 @@ const AppointmentItem = ({ appointment, onToggleDone, onEdit }) => { +

diff --git a/frontend/src/components/AppointmentList.jsx b/frontend/src/components/AppointmentList.jsx index d8d0219..8f25c1c 100644 --- a/frontend/src/components/AppointmentList.jsx +++ b/frontend/src/components/AppointmentList.jsx @@ -2,7 +2,7 @@ import React from 'react'; import AppointmentItem from './AppointmentItem'; // NEUE PROP: onEdit -const AppointmentList = ({ appointments, onToggleDone, onEdit }) => { +const AppointmentList = ({ appointments, onToggleDone, onEdit, onDelete }) => { if (appointments.length === 0) { return

Keine Termine vorhanden. Fügen Sie oben einen neuen Termin hinzu!

; @@ -19,6 +19,7 @@ const AppointmentList = ({ appointments, onToggleDone, onEdit }) => { appointment={appointment} onToggleDone={onToggleDone} onEdit={onEdit} // Funktion an das Item weitergeben + onDelete={onDelete} // NEU: Übergabe an das Item /> ))} diff --git a/frontend/src/components/ConfirmationModal.jsx b/frontend/src/components/ConfirmationModal.jsx new file mode 100644 index 0000000..bee0a42 --- /dev/null +++ b/frontend/src/components/ConfirmationModal.jsx @@ -0,0 +1,40 @@ +// src/components/ConfirmationModal.jsx +import React from 'react'; +import './ModalStyles.css'; // Wird gleich definiert + +const ConfirmationModal = ({ isOpen, title, message, onConfirm, onCancel }) => { + if (!isOpen) { + return null; + } + + return ( + // Hintergrund-Overlay (schließt das Modal bei Klick außerhalb) +
+
e.stopPropagation()} + > +

{title}

+

{message}

+ +
+ + +
+
+
+ ); +}; + +export default ConfirmationModal; \ No newline at end of file diff --git a/frontend/src/components/ModalStyles.css b/frontend/src/components/ModalStyles.css new file mode 100644 index 0000000..a27153e --- /dev/null +++ b/frontend/src/components/ModalStyles.css @@ -0,0 +1,74 @@ +/* src/components/ModalStyles.css */ + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.6); /* Dunkles, halbtransparentes Overlay */ + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; /* Stellt sicher, dass es über allem liegt */ +} + +.modal-content { + background: white; + padding: 30px; + border-radius: 12px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + width: 90%; + max-width: 450px; + animation: fadeIn 0.3s ease-out; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(-20px); } + to { opacity: 1; transform: translateY(0); } +} + +.modal-title { + color: #dc3545; + border-bottom: 2px solid #f0f0f0; + padding-bottom: 10px; + margin-top: 0; +} + +.modal-message { + color: #333; + margin-bottom: 30px; +} + +.modal-actions { + display: flex; + justify-content: flex-end; /* Buttons nach rechts ausrichten */ + gap: 10px; +} + +.modal-button { + padding: 10px 18px; + border: none; + border-radius: 6px; + font-weight: bold; + cursor: pointer; + transition: background-color 0.2s; +} + +.modal-button.cancel { + background-color: #f0f0f0; + color: #333; +} + +.modal-button.cancel:hover { + background-color: #ddd; +} + +.modal-button.confirm { + background-color: #dc3545; /* Rot für Löschaktion */ + color: white; +} + +.modal-button.confirm:hover { + background-color: #c82333; +} \ No newline at end of file