diff --git a/backend/server.js b/backend/server.js index 245a424..a3a7912 100644 --- a/backend/server.js +++ b/backend/server.js @@ -8,6 +8,8 @@ import { createEntry, removeEntries, saveRow, + resetData, + getVitals, } from "./services/database.js"; import { generateToken, authenticate } from "./services/tokenService.js"; env.config(); @@ -37,8 +39,10 @@ app.post("/lose", async (req, res) => { app.get("/table-data", authenticate, async (req, res) => { const result = await getTableData(); - if (result.success) { + if (result.success && result.data) { res.status(200).json(result.data); + } else if (result.success && !result.data) { + res.status(204).json({ success: true }); } else { res.status(500).json({ success: false }); } @@ -87,6 +91,15 @@ app.post("/login", async (req, res) => { } }); +app.delete("/reset-data", authenticate, async (req, res) => { + const result = await resetData(); + if (result.success) { + res.status(200).json({ success: true }); + } else { + res.status(400).json({ success: false }); + } +}); + app.listen(port, () => { console.log(`Server is running on port: ${port}`); }); diff --git a/backend/services/database.js b/backend/services/database.js index c40316f..ea175f4 100644 --- a/backend/services/database.js +++ b/backend/services/database.js @@ -47,8 +47,11 @@ export async function getTableData() { if (result.length > 0) { return { success: true, data: result }; + } else if (result.entries.length === 0) { + return { success: true }; + } else { + return { success: false }; } - return { success: false }; } export async function createEntry(data) { @@ -114,3 +117,12 @@ export async function saveRow(payload) { return { success: false }; } } + +export async function resetData() { + const [result] = await pool.query("DELETE FROM lose"); + if (result.affectedRows > 0) { + return { success: true }; + } else { + return { success: false }; + } +} diff --git a/frontend/src/components/DangerZone.tsx b/frontend/src/components/DangerZone.tsx new file mode 100644 index 0000000..c95f5e8 --- /dev/null +++ b/frontend/src/components/DangerZone.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import { CircleX } from "lucide-react"; +import Cookies from "js-cookie"; +import { resetData } from "../utils/tableActions"; + +type DangerZoneProps = { + onClose: () => void; +}; + +const DangerZone: React.FC = ({ onClose }) => { + return ( + <> +
+ {/* Backdrop */} +
+ + {/* Dialog Panel */} +
+
+
+

+ Datenbank Einstellungen und Vitalwerte +

+

+ Doppelklick um Befehl auszuführen +

+
+ +
+
+ +
+ + Diese Aktion kann nicht rückgängig gemacht werden. + +
+
+ {/* Actions */} + {/* +
+ +
+ */} +
+
+ + ); +}; + +export default DangerZone; diff --git a/frontend/src/components/ImportGUI.tsx b/frontend/src/components/ImportGUI.tsx index ad8b247..3ff9813 100644 --- a/frontend/src/components/ImportGUI.tsx +++ b/frontend/src/components/ImportGUI.tsx @@ -78,6 +78,7 @@ const ImportGUI: React.FC = ({ onClose, setFiles, files }) => { if (files && files.length) { postCSV(files[0]); } + onClose(); }} > Importieren diff --git a/frontend/src/components/SubHeaderAdmin.tsx b/frontend/src/components/SubHeaderAdmin.tsx index f51c638..2a050eb 100644 --- a/frontend/src/components/SubHeaderAdmin.tsx +++ b/frontend/src/components/SubHeaderAdmin.tsx @@ -1,8 +1,10 @@ import React from "react"; -import { Sheet, WholeWord, Search } from "lucide-react"; +import { Sheet, WholeWord, Search, DatabaseZap } from "lucide-react"; import { useState } from "react"; import ImportGUI from "./ImportGUI"; +import Cookies from "js-cookie"; import { removeSelection } from "../utils/tableActions"; +import DangerZone from "./DangerZone"; type SubHeaderAdminProps = { setFiles: (files: File[]) => void; @@ -19,6 +21,7 @@ const SubHeaderAdmin: React.FC = ({ setSearch, }) => { const [showImport, setShowImport] = useState(false); + const [showDanger, setShowDanger] = useState(false); return ( <> @@ -61,7 +64,7 @@ const SubHeaderAdmin: React.FC = ({ +
@@ -82,6 +98,10 @@ const SubHeaderAdmin: React.FC = ({ files={files} /> )} + + {showDanger && ( + setShowDanger(false)} /> + )} ); }; diff --git a/frontend/src/components/Table.tsx b/frontend/src/components/Table.tsx index eb19edd..328eb6e 100644 --- a/frontend/src/components/Table.tsx +++ b/frontend/src/components/Table.tsx @@ -76,6 +76,7 @@ const Table: React.FC = () => { enabled: !!token, queryFn: async () => { const data = await getTableData(token); + if (data === null) throw new Error("Fehler beim Laden der Daten."); return data as unknown as DataPackage[] | DataPackage; // server may send single object }, @@ -401,7 +402,7 @@ const Table: React.FC = () => { /> - diff --git a/frontend/src/utils/tableActions.ts b/frontend/src/utils/tableActions.ts index 5de0b55..6b45633 100644 --- a/frontend/src/utils/tableActions.ts +++ b/frontend/src/utils/tableActions.ts @@ -20,19 +20,19 @@ export const rmFromRemove = (losnummer: string) => { rawCookies.set("removeArr", JSON.stringify(removeArr)); }; -const token = Cookies.get("token"); +function createHeaders(token: string) { + return { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }; +} -const headers = { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, -}; - -export const removeSelection = () => { +export const removeSelection = (token: string) => { const selection = Cookies.get("removeArr"); if (selection && selection !== "[]") { fetch("http://localhost:8002/remove-entries", { method: "DELETE", - headers: headers, + headers: createHeaders(token), body: `{ "losnummern": ${selection} }`, @@ -49,10 +49,10 @@ export const removeSelection = () => { } }; -export const saveRow = (data: any) => { +export const saveRow = (data: any, token: string) => { fetch("http://localhost:8002/save-row", { method: "PUT", - headers: headers, + headers: createHeaders(token), body: JSON.stringify(data), }).then((response) => { if (response.ok) { @@ -62,3 +62,18 @@ export const saveRow = (data: any) => { } }); }; + +export const resetData = (token: string) => { + fetch("http://localhost:8002/reset-data", { + method: "DELETE", + headers: createHeaders(token), + }).then((response) => { + if (response.ok) { + myToast("Daten erfolgreich zurückgesetzt.", "success"); + localStorage.removeItem("tableData"); + queryClient.invalidateQueries({ queryKey: ["table-data"] }); + } else { + myToast("Fehler beim Zurücksetzen der Daten.", "error"); + } + }); +}; diff --git a/frontend/src/utils/userHandler.ts b/frontend/src/utils/userHandler.ts index aa2a733..ed15b10 100644 --- a/frontend/src/utils/userHandler.ts +++ b/frontend/src/utils/userHandler.ts @@ -7,7 +7,6 @@ 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 { @@ -21,13 +20,17 @@ export const getTableData = async (token: string) => { if (response.status === 401) { myToast("Session expired. Please log in again.", "error"); logoutAdmin(); - return null; + return []; } if (!response.ok) { const text = await response.text().catch(() => ""); myToast(`Error fetching table data! ${text}`, "error"); - return null; + return []; + } + + if (response.status === 204) { + return []; } // Ensure we parse JSON