From 3096e4ab8398498a6a72b74101b81ddf601f4a61 Mon Sep 17 00:00:00 2001 From: "theis.gaedigk" Date: Wed, 13 Aug 2025 21:48:19 +0200 Subject: [PATCH] feat: add entry removal and row saving functionality, enhance admin components with localization --- backend/server.js | 36 +++++ backend/services/database.js | 41 +++++- frontend/src/components/Admin.tsx | 2 +- frontend/src/components/MainForm.tsx | 2 +- frontend/src/components/SubHeaderAdmin.tsx | 4 + frontend/src/components/Table.tsx | 154 +++++++++++++++------ frontend/src/utils/tableActions.ts | 58 ++++++++ frontend/src/utils/userHandler.ts | 2 +- 8 files changed, 254 insertions(+), 45 deletions(-) create mode 100644 frontend/src/utils/tableActions.ts diff --git a/backend/server.js b/backend/server.js index 9a88522..b1ad974 100644 --- a/backend/server.js +++ b/backend/server.js @@ -6,6 +6,8 @@ import { loginAdmin, getTableData, createEntry, + removeEntries, + saveRow, } from "./services/database.js"; import { generateToken, authenticate } from "./services/tokenService.js"; env.config(); @@ -30,6 +32,22 @@ app.post("/lose", async (req, res) => { } }); + + + + + + +// !!!!!!! AUTHORISATION HINZUFÜGEN - DENN GEHT NICHT !!!!!!!! + + + + + + + + + app.get("/table-data", authenticate, async (req, res) => { const result = await getTableData(); if (result.success) { @@ -48,6 +66,24 @@ app.post("/create-entry", async (req, res) => { } }); +app.delete("/remove-entries", async (req, res) => { + const result = await removeEntries(req.body.losnummern); + if (result) { + res.status(200).json({ success: true }); + } else { + res.status(400).json({ success: false }); + } +}); + +app.put("/save-row", async (req, res) => { + const result = await saveRow(req.body); + if (result.success) { + res.status(200).json({ success: true }); + } else { + res.status(400).json({ success: false }); + } +}); + app.post("/login", async (req, res) => { const { username, password } = req.body; const result = await loginAdmin(username, password); diff --git a/backend/services/database.js b/backend/services/database.js index 6b91a1f..8dd0fd9 100644 --- a/backend/services/database.js +++ b/backend/services/database.js @@ -51,8 +51,6 @@ export async function getTableData() { return { success: false }; } -// Create data from array not working !!! - export async function createEntry(data) { let { status } = { status: true }; for (const item of data) { @@ -71,3 +69,42 @@ export async function createEntry(data) { return status; } + +export async function removeEntries(losnummern) { + let { status } = { status: true }; + + for (const losnummer of losnummern) { + const [result] = await pool.query("DELETE FROM lose WHERE losnummer = ?", [ + losnummer, + ]); + + if (result.affectedRows > 0) { + status = true; + } else { + status = false; + break; + } + } + + return status; +} + +export async function saveRow(payload) { + const [result] = await pool.query( + "UPDATE lose SET vorname = ?, nachname = ?, adresse = ?, plz = ?, email = ? WHERE losnummer = ?", + [ + payload.vorname, + payload.nachname, + payload.adresse, + payload.plz, + payload.email, + payload.losnummer, + ] + ); + + if (result.affectedRows > 0) { + return { success: true }; + } else { + return { success: false }; + } +} diff --git a/frontend/src/components/Admin.tsx b/frontend/src/components/Admin.tsx index c476288..6de45cc 100644 --- a/frontend/src/components/Admin.tsx +++ b/frontend/src/components/Admin.tsx @@ -22,7 +22,7 @@ const Admin: React.FC = () => { {token ? ( ) : ( -
Please log in as an admin.
+
Bitte als Admin einloggen. Oder gehe zurück.
)} ); diff --git a/frontend/src/components/MainForm.tsx b/frontend/src/components/MainForm.tsx index cddb159..46a74a4 100644 --- a/frontend/src/components/MainForm.tsx +++ b/frontend/src/components/MainForm.tsx @@ -125,7 +125,7 @@ const MainForm: React.FC = () => { Los registrieren

- Wenn Sie die Daten eines Loses bearbeiten möchten,{" "} + Wenn Sie die Daten eines bereits registrierten Loses bearbeiten möchten,{" "} void; @@ -41,6 +42,9 @@ const SubHeaderAdmin: React.FC = ({ setFiles, files }) => { Losnummern importieren -

+ */} +
- + @@ -147,7 +159,7 @@ const Table: React.FC = () => { {rows.length === 0 && !tableQuery.isLoading && ( - - - - - - diff --git a/frontend/src/utils/tableActions.ts b/frontend/src/utils/tableActions.ts new file mode 100644 index 0000000..e92fcf1 --- /dev/null +++ b/frontend/src/utils/tableActions.ts @@ -0,0 +1,58 @@ +import Cookies from "js-cookie"; +import { myToast } from "./toastify"; +import { queryClient } from "../queryClient"; + +let removeArr: string[] = []; + +export const addToRemove = (losnummer: string) => { + removeArr.push(losnummer); + const rawCookies = Cookies.withConverter({ + write: (value: string, _name: string) => value, + }); + rawCookies.set("removeArr", JSON.stringify(removeArr)); +}; + +export const rmFromRemove = (losnummer: string) => { + removeArr = removeArr.filter((item) => item !== losnummer); + const rawCookies = Cookies.withConverter({ + write: (value: string, _name: string) => value, + }); + rawCookies.set("removeArr", JSON.stringify(removeArr)); +}; + +const token = Cookies.get("token"); + +const headers = { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, +}; + +export const removeSelection = () => { + const selection = Cookies.get("removeArr"); + if (selection && selection !== "[]") { + fetch("http://localhost:8002/remove-entries", { + method: "DELETE", + headers: headers, + body: `{ + "losnummern": ${selection} + }`, + }).then((response) => { + if (response.ok) { + myToast("Einträge erfolgreich entfernt.", "success"); + queryClient.invalidateQueries({ queryKey: ["table-data"] }); + } else { + myToast("Fehler beim Entfernen der Einträge.", "error"); + } + }); + } else { + myToast("Keine Einträge zum Entfernen ausgewählt.", "info"); + } +}; + +export const saveRow = (data: any) => { + fetch("http://localhost:8002/save-row", { + method: "PUT", + headers: headers, + body: JSON.stringify(data), + }); +}; diff --git a/frontend/src/utils/userHandler.ts b/frontend/src/utils/userHandler.ts index e77f3c2..aa2a733 100644 --- a/frontend/src/utils/userHandler.ts +++ b/frontend/src/utils/userHandler.ts @@ -7,6 +7,7 @@ export const logoutAdmin = () => { myToast("Logged out successfully!", "success"); }; + // Fetch table data and store it in localStorage. Returns the parsed data or null on failure. export const getTableData = async (token: string) => { try { @@ -32,7 +33,6 @@ export const getTableData = async (token: string) => { // Ensure we parse JSON const data = await response.json(); localStorage.setItem("tableData", JSON.stringify(data)); - myToast("Table data fetched successfully!", "success"); return data; } catch (error: any) { myToast(`Error fetching table data! ${error?.message || error}`, "error");
Losnummer Vorname Nachname Adresse PLZ Email
Keine Daten vorhanden. @@ -159,33 +171,95 @@ const Table: React.FC = () => { key={row.losnummer ?? idx} className="hover:bg-gray-50 transition-colors" > - - + + { + if (e.target.checked) { + addToRemove(row.losnummer); + } else { + rmFromRemove(row.losnummer); + } + }} + id={row.losnummer} + /> - {formatValue(row.losnummer)} + + {row.losnummer} - + + { + handleInputChange( + row.losnummer, + "vorname", + e.target.value + ); + }} + value={row.vorname ?? ""} + /> - + + { + handleInputChange( + row.losnummer, + "nachname", + e.target.value + ); + }} + value={row.nachname ?? ""} + /> - + { + handleInputChange( + row.losnummer, + "adresse", + e.target.value + ); + }} + value={row.adresse ?? ""} + /> - + + { + handleInputChange(row.losnummer, "plz", e.target.value); + }} + value={row.plz ?? ""} + /> - + + { + handleInputChange( + row.losnummer, + "email", + e.target.value + ); + }} + value={row.email ?? ""} + /> - +