feat: add entry removal and row saving functionality, enhance admin components with localization
This commit is contained in:
@@ -6,6 +6,8 @@ import {
|
|||||||
loginAdmin,
|
loginAdmin,
|
||||||
getTableData,
|
getTableData,
|
||||||
createEntry,
|
createEntry,
|
||||||
|
removeEntries,
|
||||||
|
saveRow,
|
||||||
} from "./services/database.js";
|
} from "./services/database.js";
|
||||||
import { generateToken, authenticate } from "./services/tokenService.js";
|
import { generateToken, authenticate } from "./services/tokenService.js";
|
||||||
env.config();
|
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) => {
|
app.get("/table-data", authenticate, async (req, res) => {
|
||||||
const result = await getTableData();
|
const result = await getTableData();
|
||||||
if (result.success) {
|
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) => {
|
app.post("/login", async (req, res) => {
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
const result = await loginAdmin(username, password);
|
const result = await loginAdmin(username, password);
|
||||||
|
@@ -51,8 +51,6 @@ export async function getTableData() {
|
|||||||
return { success: false };
|
return { success: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create data from array not working !!!
|
|
||||||
|
|
||||||
export async function createEntry(data) {
|
export async function createEntry(data) {
|
||||||
let { status } = { status: true };
|
let { status } = { status: true };
|
||||||
for (const item of data) {
|
for (const item of data) {
|
||||||
@@ -71,3 +69,42 @@ export async function createEntry(data) {
|
|||||||
|
|
||||||
return status;
|
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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -22,7 +22,7 @@ const Admin: React.FC = () => {
|
|||||||
{token ? (
|
{token ? (
|
||||||
<Table />
|
<Table />
|
||||||
) : (
|
) : (
|
||||||
<div className="p-4">Please log in as an admin.</div>
|
<div className="p-4">Bitte als Admin einloggen. Oder gehe <a className="text-blue-500 hover:underline" href="/">zurück</a>.</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@@ -125,7 +125,7 @@ const MainForm: React.FC = () => {
|
|||||||
Los registrieren
|
Los registrieren
|
||||||
</button>
|
</button>
|
||||||
<p className="mt-1 text-xs text-zinc-500">
|
<p className="mt-1 text-xs text-zinc-500">
|
||||||
Wenn Sie die Daten eines Loses bearbeiten möchten,{" "}
|
Wenn Sie die Daten eines bereits registrierten Loses bearbeiten möchten,{" "}
|
||||||
<a
|
<a
|
||||||
className="text-blue-600 underline"
|
className="text-blue-600 underline"
|
||||||
href="mailto:example@example.com"
|
href="mailto:example@example.com"
|
||||||
|
@@ -2,6 +2,7 @@ import React from "react";
|
|||||||
import { Sheet, WholeWord } from "lucide-react";
|
import { Sheet, WholeWord } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import ImportGUI from "./ImportGUI";
|
import ImportGUI from "./ImportGUI";
|
||||||
|
import { removeSelection } from "../utils/tableActions";
|
||||||
|
|
||||||
type SubHeaderAdminProps = {
|
type SubHeaderAdminProps = {
|
||||||
setFiles: (files: File[]) => void;
|
setFiles: (files: File[]) => void;
|
||||||
@@ -41,6 +42,9 @@ const SubHeaderAdmin: React.FC<SubHeaderAdminProps> = ({ setFiles, files }) => {
|
|||||||
<span className="whitespace-nowrap">Losnummern importieren</span>
|
<span className="whitespace-nowrap">Losnummern importieren</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
removeSelection();
|
||||||
|
}}
|
||||||
type="button"
|
type="button"
|
||||||
className="group inline-flex items-center gap-2 rounded-md bg-rose-600 px-3.5 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-rose-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-500/60 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60"
|
className="group inline-flex items-center gap-2 rounded-md bg-rose-600 px-3.5 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-rose-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-500/60 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
>
|
>
|
||||||
|
@@ -2,8 +2,9 @@ import React, { useEffect, useState } from "react";
|
|||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { getTableData, readCachedTableData } from "../utils/userHandler";
|
import { getTableData, readCachedTableData } from "../utils/userHandler";
|
||||||
import { EllipsisVertical } from "lucide-react";
|
import { Save } from "lucide-react";
|
||||||
import SubHeaderAdmin from "./SubHeaderAdmin";
|
import SubHeaderAdmin from "./SubHeaderAdmin";
|
||||||
|
import { addToRemove, rmFromRemove, saveRow } from "../utils/tableActions";
|
||||||
|
|
||||||
interface DataPackage {
|
interface DataPackage {
|
||||||
losnummer: string;
|
losnummer: string;
|
||||||
@@ -17,9 +18,12 @@ interface DataPackage {
|
|||||||
|
|
||||||
const Table: React.FC = () => {
|
const Table: React.FC = () => {
|
||||||
const [rows, setRows] = useState<DataPackage[]>([]); // holds normalized cache view
|
const [rows, setRows] = useState<DataPackage[]>([]); // holds normalized cache view
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
const [files, setFiles] = useState<File[]>([]);
|
const [files, setFiles] = useState<File[]>([]);
|
||||||
|
|
||||||
|
// Einheitliche Input-Styles (nur Tailwind)
|
||||||
|
const inputClasses =
|
||||||
|
"w-full h-10 px-3 rounded-md border border-gray-300 bg-white text-sm text-gray-900 placeholder-gray-400 shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
||||||
|
|
||||||
// Hilfsfunktion zum Einlesen & Normalisieren der LocalStorage-Daten
|
// Hilfsfunktion zum Einlesen & Normalisieren der LocalStorage-Daten
|
||||||
const loadFromCache = () => {
|
const loadFromCache = () => {
|
||||||
const cached = readCachedTableData<any>();
|
const cached = readCachedTableData<any>();
|
||||||
@@ -48,15 +52,10 @@ const Table: React.FC = () => {
|
|||||||
// Sync normalized cached data into local state whenever query succeeds or cache changes
|
// Sync normalized cached data into local state whenever query succeeds or cache changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadFromCache();
|
loadFromCache();
|
||||||
|
Cookies.remove("removeArr");
|
||||||
}, [tableQuery.data]);
|
}, [tableQuery.data]);
|
||||||
|
|
||||||
useEffect(() => {
|
// Fehleranzeige ist aktuell nicht sichtbar (auskommentierte UI).
|
||||||
if (tableQuery.isError) {
|
|
||||||
setError((tableQuery.error as Error).message);
|
|
||||||
} else {
|
|
||||||
setError(null);
|
|
||||||
}
|
|
||||||
}, [tableQuery.isError, tableQuery.error]);
|
|
||||||
|
|
||||||
// Reagieren auf LocalStorage-Änderungen (z.B. in anderen Tabs)
|
// Reagieren auf LocalStorage-Änderungen (z.B. in anderen Tabs)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -69,13 +68,24 @@ const Table: React.FC = () => {
|
|||||||
return () => window.removeEventListener("storage", handler);
|
return () => window.removeEventListener("storage", handler);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const formatValue = (v: any) =>
|
// Handles input changes for table rows
|
||||||
v === null || v === undefined || v === "" ? "-" : String(v);
|
const handleInputChange = (
|
||||||
|
losnummer: string,
|
||||||
|
field: keyof DataPackage,
|
||||||
|
value: string
|
||||||
|
) => {
|
||||||
|
setRows((prevRows) =>
|
||||||
|
prevRows.map((row) =>
|
||||||
|
row.losnummer === losnummer ? { ...row, [field]: value } : row
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SubHeaderAdmin setFiles={setFiles} files={files} />
|
<SubHeaderAdmin setFiles={setFiles} files={files} />
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
{/*
|
||||||
<div className="mb-4 flex items-center gap-3">
|
<div className="mb-4 flex items-center gap-3">
|
||||||
{(tableQuery.isLoading || tableQuery.isFetching) && (
|
{(tableQuery.isLoading || tableQuery.isFetching) && (
|
||||||
<span className="text-xs text-blue-600 animate-pulse">
|
<span className="text-xs text-blue-600 animate-pulse">
|
||||||
@@ -83,6 +93,7 @@ const Table: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{error && <span className="text-xs text-red-600">{error}</span>}
|
{error && <span className="text-xs text-red-600">{error}</span>}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => tableQuery.refetch()}
|
onClick={() => tableQuery.refetch()}
|
||||||
@@ -91,55 +102,56 @@ const Table: React.FC = () => {
|
|||||||
Refresh
|
Refresh
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="overflow-x-auto rounded-lg shadow ring-1 ring-black/5">
|
*/}
|
||||||
|
<div className="overflow-auto rounded-lg shadow ring-1 ring-black/5">
|
||||||
<table className="min-w-full divide-y divide-gray-200 text-sm">
|
<table className="min-w-full divide-y divide-gray-200 text-sm">
|
||||||
<thead className="bg-gray-50">
|
<thead className="bg-gray-50 sticky top-0">
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600"
|
className="px-4 py-2 text-left invisible font-medium uppercase tracking-wide text-gray-600 w-10 min-w-[2.5rem]"
|
||||||
>
|
>
|
||||||
<input type="checkbox" name="" id="" />
|
<input type="checkbox" name="" id="" />
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600"
|
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600 w-[10rem] min-w-[10rem]"
|
||||||
>
|
>
|
||||||
Losnummer
|
Losnummer
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600"
|
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600 w-[14rem] min-w-[14rem]"
|
||||||
>
|
>
|
||||||
Vorname
|
Vorname
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600"
|
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600 w-[14rem] min-w-[14rem]"
|
||||||
>
|
>
|
||||||
Nachname
|
Nachname
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600"
|
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600 w-[14rem] min-w-[14rem]"
|
||||||
>
|
>
|
||||||
Adresse
|
Adresse
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600"
|
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600 w-[14rem] min-w-[14rem]"
|
||||||
>
|
>
|
||||||
PLZ
|
PLZ
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600"
|
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600 w-[14rem] min-w-[14rem]"
|
||||||
>
|
>
|
||||||
Email
|
Email
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600"
|
className="px-4 py-2 text-left font-medium uppercase tracking-wide text-gray-600 w-12 min-w-[3rem]"
|
||||||
></th>
|
></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -147,7 +159,7 @@ const Table: React.FC = () => {
|
|||||||
{rows.length === 0 && !tableQuery.isLoading && (
|
{rows.length === 0 && !tableQuery.isLoading && (
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
colSpan={6}
|
colSpan={8}
|
||||||
className="px-4 py-6 text-center text-gray-500"
|
className="px-4 py-6 text-center text-gray-500"
|
||||||
>
|
>
|
||||||
Keine Daten vorhanden.
|
Keine Daten vorhanden.
|
||||||
@@ -159,33 +171,95 @@ const Table: React.FC = () => {
|
|||||||
key={row.losnummer ?? idx}
|
key={row.losnummer ?? idx}
|
||||||
className="hover:bg-gray-50 transition-colors"
|
className="hover:bg-gray-50 transition-colors"
|
||||||
>
|
>
|
||||||
<td className="px-4 py-2 font-mono text-xs text-gray-900">
|
<td className="px-4 py-2 font-mono text-xs text-gray-900 w-10 min-w-[2.5rem]">
|
||||||
<input type="checkbox" name="" id={row.losnummer} />
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name=""
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
addToRemove(row.losnummer);
|
||||||
|
} else {
|
||||||
|
rmFromRemove(row.losnummer);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
id={row.losnummer}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2 font-mono text-xs text-gray-900">
|
<td className="px-4 py-2 font-mono text-xs text-gray-900 w-[10rem] min-w-[10rem]">
|
||||||
{formatValue(row.losnummer)}
|
{row.losnummer}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2 w-[14rem] min-w-[14rem]">
|
||||||
<input type="text" value={formatValue(row.vorname)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className={inputClasses}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleInputChange(
|
||||||
|
row.losnummer,
|
||||||
|
"vorname",
|
||||||
|
e.target.value
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
value={row.vorname ?? ""}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2 w-[14rem] min-w-[14rem]">
|
||||||
<input type="text" value={formatValue(row.nachname)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className={inputClasses}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleInputChange(
|
||||||
|
row.losnummer,
|
||||||
|
"nachname",
|
||||||
|
e.target.value
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
value={row.nachname ?? ""}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className="px-4 py-2 max-w-[16rem] truncate"
|
className="px-4 py-2 w-[14rem] min-w-[14rem]"
|
||||||
title={formatValue(row.adresse)}
|
title={row.adresse ?? ""}
|
||||||
>
|
>
|
||||||
<input type="text" value={formatValue(row.adresse)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className={inputClasses}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleInputChange(
|
||||||
|
row.losnummer,
|
||||||
|
"adresse",
|
||||||
|
e.target.value
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
value={row.adresse ?? ""}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2 w-[14rem] min-w-[14rem]">
|
||||||
<input type="text" value={formatValue(row.plz)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className={inputClasses}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleInputChange(row.losnummer, "plz", e.target.value);
|
||||||
|
}}
|
||||||
|
value={row.plz ?? ""}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2 w-[14rem] min-w-[14rem]">
|
||||||
<input type="text" value={formatValue(row.email)} />
|
<input
|
||||||
|
type="text"
|
||||||
|
className={inputClasses}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleInputChange(
|
||||||
|
row.losnummer,
|
||||||
|
"email",
|
||||||
|
e.target.value
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
value={row.email ?? ""}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2 w-12 min-w-[3rem]">
|
||||||
<button>
|
<button onClick={() => saveRow(row)}>
|
||||||
<EllipsisVertical />
|
<Save />
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
58
frontend/src/utils/tableActions.ts
Normal file
58
frontend/src/utils/tableActions.ts
Normal file
@@ -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),
|
||||||
|
});
|
||||||
|
};
|
@@ -7,6 +7,7 @@ export const logoutAdmin = () => {
|
|||||||
myToast("Logged out successfully!", "success");
|
myToast("Logged out successfully!", "success");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Fetch table data and store it in localStorage. Returns the parsed data or null on failure.
|
// Fetch table data and store it in localStorage. Returns the parsed data or null on failure.
|
||||||
export const getTableData = async (token: string) => {
|
export const getTableData = async (token: string) => {
|
||||||
try {
|
try {
|
||||||
@@ -32,7 +33,6 @@ export const getTableData = async (token: string) => {
|
|||||||
// Ensure we parse JSON
|
// Ensure we parse JSON
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
localStorage.setItem("tableData", JSON.stringify(data));
|
localStorage.setItem("tableData", JSON.stringify(data));
|
||||||
myToast("Table data fetched successfully!", "success");
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
myToast(`Error fetching table data! ${error?.message || error}`, "error");
|
myToast(`Error fetching table data! ${error?.message || error}`, "error");
|
||||||
|
Reference in New Issue
Block a user