Delete implemnetier - Alles im frontend

This commit is contained in:
rxf
2025-11-23 16:51:19 +01:00
parent 268b4dee18
commit fa53c5daed
7 changed files with 216 additions and 4 deletions

View File

@@ -2,8 +2,8 @@
"name": "aerzte", "name": "aerzte",
"type": "module", "type": "module",
"private": true, "private": true,
"version": "1.0.0", "version": "1.1.0",
"vdate": "2025-11-23 11:00 UTC", "vdate": "2025-11-23 16:00 UTC",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",

View File

@@ -40,3 +40,34 @@
.read-the-docs { .read-the-docs {
color: #888; 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);
}

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import AppointmentForm from './components/AppointmentForm'; import AppointmentForm from './components/AppointmentForm';
import AppointmentList from './components/AppointmentList'; import AppointmentList from './components/AppointmentList';
import ConfirmationModal from './components/ConfirmationModal';
import './AppStyles.css'; import './AppStyles.css';
// Die Basis-URL der Express-API // Die Basis-URL der Express-API
@@ -22,6 +23,8 @@ const normalizeAppointment = (appointment) => ({
function App() { function App() {
const [appointments, setAppointments] = useState([]); const [appointments, setAppointments] = useState([]);
const [editingAppointment, setEditingAppointment] = useState(null); const [editingAppointment, setEditingAppointment] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [appointmentToDeleteId, setAppointmentToDeleteId] = useState(null);
// NEU: Lädt Termine vom Backend beim Start // NEU: Lädt Termine vom Backend beim Start
useEffect(() => { 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) // ... (startEdit und cancelEdit bleiben unverändert, sie manipulieren nur den lokalen State)
return ( return (
@@ -153,6 +200,15 @@ function App() {
appointments={appointments} appointments={appointments}
onToggleDone={toggleDone} onToggleDone={toggleDone}
onEdit={(app) => setEditingAppointment(app)} onEdit={(app) => setEditingAppointment(app)}
onDelete={openDeleteModal}
/>
{/* NEU: MODAL KOMPONENTE */}
<ConfirmationModal
isOpen={isModalOpen}
title="Termin löschen bestätigen"
message="Möchten Sie diesen Termin wirklich unwiderruflich löschen? Diese Aktion kann nicht rückgängig gemacht werden."
onConfirm={handleConfirmDelete}
onCancel={handleCancelDelete}
/> />
{/* Debug Output kann nun entfernt werden */} {/* Debug Output kann nun entfernt werden */}
</div> </div>

View File

@@ -6,7 +6,7 @@ const formatDate = (date) => {
}; };
// NEUE PROP: onEdit // NEUE PROP: onEdit
const AppointmentItem = ({ appointment, onToggleDone, onEdit }) => { const AppointmentItem = ({ appointment, onToggleDone, onEdit, onDelete }) => {
const { id, arztName, arztArt, termin, erledigt, bemerkungen } = appointment; const { id, arztName, arztArt, termin, erledigt, bemerkungen } = appointment;
const itemClass = erledigt ? 'appointment-item done' : 'appointment-item'; const itemClass = erledigt ? 'appointment-item done' : 'appointment-item';
@@ -28,6 +28,16 @@ const AppointmentItem = ({ appointment, onToggleDone, onEdit }) => {
<label htmlFor={`check-${id}`} className="doctor-info"> <label htmlFor={`check-${id}`} className="doctor-info">
<strong>{arztName}</strong> {arztArt && <span className="arzt-art">({arztArt})</span>} <strong>{arztName}</strong> {arztArt && <span className="arzt-art">({arztArt})</span>}
</label> </label>
<button
className="delete-button"
onClick={(e) => {
e.stopPropagation(); // Verhindert, dass das Bearbeiten ausgelöst wird
onDelete(id);
}}
title="Termin löschen"
>
🗑
</button>
</div> </div>
<p className="appointment-date"> <p className="appointment-date">

View File

@@ -2,7 +2,7 @@ import React from 'react';
import AppointmentItem from './AppointmentItem'; import AppointmentItem from './AppointmentItem';
// NEUE PROP: onEdit // NEUE PROP: onEdit
const AppointmentList = ({ appointments, onToggleDone, onEdit }) => { const AppointmentList = ({ appointments, onToggleDone, onEdit, onDelete }) => {
if (appointments.length === 0) { if (appointments.length === 0) {
return <p className="no-appointments-message">Keine Termine vorhanden. Fügen Sie oben einen neuen Termin hinzu!</p>; return <p className="no-appointments-message">Keine Termine vorhanden. Fügen Sie oben einen neuen Termin hinzu!</p>;
@@ -19,6 +19,7 @@ const AppointmentList = ({ appointments, onToggleDone, onEdit }) => {
appointment={appointment} appointment={appointment}
onToggleDone={onToggleDone} onToggleDone={onToggleDone}
onEdit={onEdit} // Funktion an das Item weitergeben onEdit={onEdit} // Funktion an das Item weitergeben
onDelete={onDelete} // NEU: Übergabe an das Item
/> />
))} ))}
</ul> </ul>

View File

@@ -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)
<div className="modal-overlay" onClick={onCancel}>
<div
className="modal-content"
// WICHTIG: Stoppt Event-Bubbling, damit Klick auf Content das Modal nicht schließt
onClick={(e) => e.stopPropagation()}
>
<h3 className="modal-title">{title}</h3>
<p className="modal-message">{message}</p>
<div className="modal-actions">
<button
className="modal-button cancel"
onClick={onCancel}
>
Abbrechen
</button>
<button
className="modal-button confirm"
onClick={onConfirm}
>
Bestätigen und Löschen
</button>
</div>
</div>
</div>
);
};
export default ConfirmationModal;

View File

@@ -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;
}