Added account delete check popup

This commit is contained in:
FlorianSpeicher
2025-06-15 20:27:48 +02:00
parent 37ade30669
commit a6766c2a60
4 changed files with 240 additions and 134 deletions

View File

@@ -108,5 +108,8 @@
"deleteProduct": "Produkt löschen", "deleteProduct": "Produkt löschen",
"description": "Beschreibung", "description": "Beschreibung",
"images": "Bilder", "images": "Bilder",
"loggedInAs": "Angemeldet als" "loggedInAs": "Angemeldet als",
"confirmDeleteAccount": "Bist du sicher, dass du dein Konto löschen möchtest? Dies kann nicht rückgängig gemacht werden.",
"enterPasswordToConfirmDeletion": "Bitte gib dein Passwort ein, um die Löschung zu bestätigen.",
"deleteAccountFailed": "Konto konnte nicht gelöscht werden. Bitte versuche es später erneut."
} }

View File

@@ -108,5 +108,8 @@
"deleteProduct": "Delete Product", "deleteProduct": "Delete Product",
"description": "Description", "description": "Description",
"images": "Images", "images": "Images",
"loggedInAs": "Logged in as" "loggedInAs": "Logged in as",
"confirmDeleteAccount": "Are you sure you want to delete your account? This action cannot be undone.",
"enterPasswordToConfirmDeletion": "Please enter your password to confirm the deletion of your account.",
"deleteAccountFailed": "Failed to delete account. Please try again later."
} }

View File

@@ -132,8 +132,8 @@ export const fetchCustomer = async (userId: number) => {
return data; return data;
}; };
export const deleteAccount = async (userId: number) => { export const deleteAccount = async (user: User) => {
const response = await fetch('http://localhost:8085/account?id=' + userId, { const response = await fetch('http://localhost:8085/account?email=' + user.email + '&password=' + user.password, {
method: 'DELETE', method: 'DELETE',
}); });
if (!response.ok) { if (!response.ok) {
@@ -165,4 +165,18 @@ export const fetchItems = async (loginData: User) => {
throw new Error("Login failed"); throw new Error("Login failed");
} }
return response.json(); return response.json();
};
export const editAccount = async (customer: CustomerType) => {
const response = await fetch('http://localhost:8085/customer?id=' + customer.id, {
method: 'PUT',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(customer),
});
if (!response.ok) {
throw new Error('Fehler beim Löschen des Accounts');
}
return await response.json();
}; };

View File

@@ -1,154 +1,240 @@
import { Box, Button, Divider, Paper, Stack, TextField, Typography } from "@mui/material"; import {
import { useQuery } from "@tanstack/react-query"; Box,
import { useEffect, useState } from "react"; Button,
import { useTranslation } from "react-i18next"; Divider,
import { useNavigate } from "react-router-dom"; Paper,
import { CustomerType } from "../components/Account"; Stack,
import { useAccount } from "../helper/AccountProvider"; TextField,
import { deleteAccount, fetchCustomer } from "../helper/query/Queries"; Typography,
import "./pages.css"; Dialog,
DialogTitle,
export default function Account() { DialogContent,
DialogActions,
} from "@mui/material";
import { useQuery } from "@tanstack/react-query";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { CustomerType, User } from "../components/Account";
import { useAccount } from "../helper/AccountProvider";
import { deleteAccount, editAccount, fetchCustomer } from "../helper/query/Queries";
import "./pages.css";
export default function Account() {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const { user: userData, logout } = useAccount(); const { user: userData, logout } = useAccount();
// Beispielhafte Userdaten (könnten aus Context/Backend kommen)
const [user, setUser] = useState<CustomerType>({ const [user, setUser] = useState<CustomerType>({
name: "", name: "",
surname: "", surname: "",
address: "", address: "",
country: "", country: "",
zip: "", zip: "",
id: userData?.customerId || 0, // Initialwert id: userData?.customerId || 0,
}); });
// Aktualisiere den `user`-State, wenn sich `userData` ändert const [userDataState, setUserDataState] = useState<User>(userData || {
password: "",
email: "",
customerId: 0,
session: "",
isAdmin: false,
});
useEffect(() => { useEffect(() => {
if (userData?.customerId) { if (userData?.customerId) {
setUser((prev) => ({ setUser((prev) => ({
...prev, ...prev,
id: userData.customerId, // Aktualisiere die ID id: userData.customerId,
})); }));
} }
}, [userData]); }, [userData]);
const [edit, setEdit] = useState(false); const [edit, setEdit] = useState(false);
const [form, setForm] = useState(user); const [form, setForm] = useState(user);
// Neu: Passwort-Dialog-Status und Passwort-Input
const [passwordDialogOpen, setPasswordDialogOpen] = useState(false);
const [passwordInput, setPasswordInput] = useState("");
const { data } = useQuery<CustomerType>({ const { data } = useQuery<CustomerType>({
queryKey: ['fetchCustomer', userData?.customerId], queryKey: ["fetchCustomer", userData?.customerId],
queryFn: () => fetchCustomer(userData?.customerId || 0), // Funktion zum Abrufen der Kundendaten queryFn: () => fetchCustomer(userData?.customerId || 0),
retry: 3, // Versucht es 3-mal erneut retry: 1,
retryDelay: 1000, // Wartezeit zwischen den Versuchen (in ms) retryDelay: 1000,
}); });
const { refetch: deleteRefetch } = useQuery({ const { refetch: deleteRefetch } = useQuery({
queryKey: ['deleteAccount', userData?.customerId], queryKey: ["deleteAccount", userDataState],
queryFn: () => deleteAccount(userData?.customerId || 0), // Funktion zum Löschen des Accounts queryFn: () => deleteAccount(userDataState!),
enabled: false, // Diese Abfrage wird nicht automatisch ausgeführt enabled: false,
}); });
const { refetch: editRefetch } = useQuery({
queryKey: ["editAccount", form],
queryFn: () => editAccount(form),
enabled: false,
});
useEffect(() => { useEffect(() => {
if (data) { if (data) {
setUser(data); // Aktualisiere den user-State mit den abgerufenen Daten setUser(data);
setForm(data); // Optional: Aktualisiere auch den form-State setForm(data);
} }
}, [data]); }, [data]);
const handleEdit = () => setEdit(true); const handleEdit = () => setEdit(true);
const handleCancel = () => { const handleCancel = () => {
setForm(user); setForm(user);
setEdit(false); setEdit(false);
}; };
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setForm({ ...form, [e.target.name]: e.target.value }); setForm({ ...form, [e.target.name]: e.target.value });
}; };
const handleSave = () => { const handleSave = async () => {
setUser(form); setUser(form);
setEdit(false); setEdit(false);
await editRefetch();
}; };
const handleDelete = () => {
deleteRefetch(); // Neu: Passwort-Dialog öffnen
const handleDeleteClick = () => {
setPasswordInput("");
setPasswordDialogOpen(true);
};
// Neu: Passwort-Dialog schließen
const handlePasswordDialogClose = () => {
setPasswordDialogOpen(false);
};
// Neu: Passwort-Eingabe bestätigen
const handlePasswordConfirm = async () => {
if (!passwordInput) {
alert(t("pleaseEnterPassword"));
return;
}
// Passwort in Form aktualisieren (hier z.B. als field "password", anpassen falls anders)
setUserDataState({ ...userDataState, password: passwordInput });
// Erst User-Daten mit Passwort aktualisieren
try {
await editRefetch(); // Achtung: editRefetch verwendet immer noch alten form, daher call direkt mit updatedForm:
// Danach Account löschen
await deleteRefetch();
logout();
navigate("/"); navigate("/");
logout(); // Logout nach dem Löschen } catch (error) {
console.error("Fehler beim Löschen des Accounts:", error);
alert(t("deleteAccountFailed"));
} finally {
setPasswordDialogOpen(false);
}
}; };
return ( return (
<Box className="page-background page-background-center" sx={{ minHeight: "100vh", justifyContent: "flex-start", pt: 4 }}> <Box
<Paper elevation={3} sx={{ p: 4, maxWidth: 500, width: "100%", mx: "auto" }}> className="page-background page-background-center"
<Typography variant="h4" gutterBottom> sx={{ minHeight: "100vh", justifyContent: "flex-start", pt: 4 }}
{t('myAccount')} >
</Typography> <Paper elevation={3} sx={{ p: 4, maxWidth: 500, width: "100%", mx: "auto" }}>
<Divider sx={{ mb: 3 }} /> <Typography variant="h4" gutterBottom>
<Stack spacing={2}> {t("myAccount")}
<TextField </Typography>
label={t('name')} <Divider sx={{ mb: 3 }} />
name="name" <Stack spacing={2}>
value={edit ? form.name : user.name} <TextField
onChange={handleChange} label={t("name")}
disabled={!edit} name="name"
fullWidth value={edit ? form.name : user.name}
/> onChange={handleChange}
<TextField disabled={!edit}
label={t('surname')} fullWidth
name="surname" />
value={edit ? form.surname : user.surname} <TextField
onChange={handleChange} label={t("surname")}
disabled={!edit} name="surname"
fullWidth value={edit ? form.surname : user.surname}
/> onChange={handleChange}
<TextField disabled={!edit}
label={t('address')} fullWidth
name="address" />
value={edit ? form.address : user.address} <TextField
onChange={handleChange} label={t("address")}
disabled={!edit} name="address"
fullWidth value={edit ? form.address : user.address}
/> onChange={handleChange}
<TextField disabled={!edit}
label={t('country')} fullWidth
name="country" />
value={edit ? form.country : user.country} <TextField
onChange={handleChange} label={t("country")}
disabled={!edit} name="country"
fullWidth value={edit ? form.country : user.country}
/> onChange={handleChange}
<TextField disabled={!edit}
label={t('zip')} fullWidth
name="zip" />
value={edit ? form.zip : user.zip} <TextField
onChange={handleChange} label={t("zip")}
disabled={!edit} name="zip"
fullWidth value={edit ? form.zip : user.zip}
/> onChange={handleChange}
</Stack> disabled={!edit}
<Box sx={{ display: "flex", gap: 2, mt: 4 }}> fullWidth
{edit ? ( />
<> </Stack>
<Button variant="contained" color="primary" onClick={handleSave}> <Box sx={{ display: "flex", gap: 2, mt: 4 }}>
{t('save')} {edit ? (
</Button> <>
<Button variant="outlined" color="secondary" onClick={handleCancel}> <Button variant="contained" color="primary" onClick={handleSave}>
{t('cancel')} {t("save")}
</Button> </Button>
</> <Button variant="outlined" color="secondary" onClick={handleCancel}>
) : ( {t("cancel")}
<Button variant="contained" color="primary" onClick={handleEdit}> </Button>
{t('edit')} </>
</Button> ) : (
)} <Button variant="contained" color="primary" onClick={handleEdit}>
<Button {t("edit")}
variant="outlined" </Button>
color="error" )}
onClick={handleDelete} <Button
sx={{ marginLeft: "auto" }} variant="outlined"
> color="error"
{t('deleteAccount')} onClick={handleDeleteClick} // Neu: Passwort-Dialog öffnen
</Button> sx={{ marginLeft: "auto" }}
</Box> >
</Paper> {t("deleteAccount")}
</Box> </Button>
</Box>
</Paper>
{/* Passwort-Dialog */}
<Dialog open={passwordDialogOpen} onClose={handlePasswordDialogClose}>
<DialogTitle>{t("confirmDeleteAccount")}</DialogTitle>
<DialogContent>
<Typography>{t("enterPasswordToConfirmDeletion")}</Typography>
<TextField
autoFocus
margin="dense"
label={t("password")}
type="password"
fullWidth
variant="standard"
value={passwordInput}
onChange={(e) => setPasswordInput(e.target.value)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handlePasswordDialogClose}>{t("cancel")}</Button>
<Button color="error" onClick={handlePasswordConfirm}>
{t("delete")}
</Button>
</DialogActions>
</Dialog>
</Box>
); );
} }