Delete implemnetier - Alles im frontend
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */}
|
||||
<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 */}
|
||||
</div>
|
||||
|
||||
@@ -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 }) => {
|
||||
<label htmlFor={`check-${id}`} className="doctor-info">
|
||||
<strong>{arztName}</strong> {arztArt && <span className="arzt-art">({arztArt})</span>}
|
||||
</label>
|
||||
<button
|
||||
className="delete-button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation(); // Verhindert, dass das Bearbeiten ausgelöst wird
|
||||
onDelete(id);
|
||||
}}
|
||||
title="Termin löschen"
|
||||
>
|
||||
🗑️
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p className="appointment-date">
|
||||
|
||||
@@ -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 <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}
|
||||
onToggleDone={onToggleDone}
|
||||
onEdit={onEdit} // Funktion an das Item weitergeben
|
||||
onDelete={onDelete} // NEU: Übergabe an das Item
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
40
frontend/src/components/ConfirmationModal.jsx
Normal file
40
frontend/src/components/ConfirmationModal.jsx
Normal 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;
|
||||
74
frontend/src/components/ModalStyles.css
Normal file
74
frontend/src/components/ModalStyles.css
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user