Refactor loan and user management components and backend routes
- Updated LoanTable component to fetch loan data from new API endpoint and display notes. - Enhanced UserTable component to include additional user fields (first name, last name, email, admin status) and updated input handling. - Modified fetcher utility to use new user data API endpoint. - Adjusted login functionality to point to the new admin login endpoint and handle unauthorized access. - Refactored user actions utility to align with updated API endpoints for user management. - Updated backend routes for user and loan data management to reflect new structure and naming conventions. - Revised SQL schema and mock data to accommodate new fields and constraints. - Changed Docker configuration to use the new database name.
This commit is contained in:
@@ -17,17 +17,14 @@ import { useState, useEffect } from "react";
|
||||
import { deleteAPKey } from "@/utils/userActions";
|
||||
import AddAPIKey from "./AddAPIKey";
|
||||
import { formatDateTime } from "@/utils/userFuncs";
|
||||
|
||||
const API_BASE =
|
||||
(import.meta as any).env?.VITE_BACKEND_URL ||
|
||||
import.meta.env.VITE_BACKEND_URL ||
|
||||
"http://localhost:8002";
|
||||
import { API_BASE } from "@/config/api.config";
|
||||
|
||||
type Items = {
|
||||
id: number;
|
||||
apiKey: string;
|
||||
user: string;
|
||||
api_key: string;
|
||||
entry_name: string;
|
||||
entry_created_at: string;
|
||||
last_used_at: string | null;
|
||||
};
|
||||
|
||||
const APIKeyTable: React.FC = () => {
|
||||
@@ -56,13 +53,17 @@ const APIKeyTable: React.FC = () => {
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/apiKeys`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||
},
|
||||
});
|
||||
const response = await fetch(
|
||||
`${API_BASE}/api/admin/api-data/get-api-keys`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
return data;
|
||||
} catch (error) {
|
||||
setError("error", "Failed to fetch items", "There is an error");
|
||||
@@ -159,29 +160,37 @@ const APIKeyTable: React.FC = () => {
|
||||
<strong>API Key</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Benutzer</strong>
|
||||
<strong>Name</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Eintrag erstellt am</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Zuletzt benutzt am</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Aktionen</strong>
|
||||
</Table.ColumnHeader>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{items.map((apiKey) => (
|
||||
<Table.Row key={apiKey.id}>
|
||||
<Table.Cell>{apiKey.id}</Table.Cell>
|
||||
<Table.Cell>{apiKey.apiKey}</Table.Cell>
|
||||
<Table.Cell>{apiKey.user}</Table.Cell>
|
||||
<Table.Cell>{formatDateTime(apiKey.entry_created_at)}</Table.Cell>
|
||||
{items.map((item) => (
|
||||
<Table.Row key={item.id}>
|
||||
<Table.Cell>{item.id}</Table.Cell>
|
||||
<Table.Cell>{item.api_key}</Table.Cell>
|
||||
<Table.Cell>{item.entry_name}</Table.Cell>
|
||||
<Table.Cell>{formatDateTime(item.entry_created_at)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{!item.last_used_at
|
||||
? "Nie benutzt"
|
||||
: formatDateTime(item.last_used_at)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
onClick={() =>
|
||||
deleteAPKey(apiKey.id).then((response) => {
|
||||
deleteAPKey(item.id).then((response) => {
|
||||
if (response.success) {
|
||||
setItems(items.filter((i) => i.id !== apiKey.id));
|
||||
setItems(items.filter((i) => i.id !== item.id));
|
||||
setError(
|
||||
"success",
|
||||
"Gegenstand gelöscht",
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import React from "react";
|
||||
import { Button, Card, Field, Input, Stack } from "@chakra-ui/react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Field,
|
||||
Input,
|
||||
Stack,
|
||||
InputGroup,
|
||||
Span,
|
||||
} from "@chakra-ui/react";
|
||||
import { createAPIentry } from "@/utils/userActions";
|
||||
import { useState } from "react";
|
||||
|
||||
type AddAPIKeyProps = {
|
||||
onClose: () => void;
|
||||
@@ -12,6 +21,8 @@ type AddAPIKeyProps = {
|
||||
};
|
||||
|
||||
const AddAPIKey: React.FC<AddAPIKeyProps> = ({ onClose, alert }) => {
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
|
||||
<Card.Root maxW="sm">
|
||||
@@ -23,13 +34,26 @@ const AddAPIKey: React.FC<AddAPIKeyProps> = ({ onClose, alert }) => {
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<Stack gap="4" w="full">
|
||||
<InputGroup
|
||||
endElement={
|
||||
<Span color="fg.muted" textStyle="xs">
|
||||
{value.length} / {15}
|
||||
</Span>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
placeholder="Er muss 15 Zeichen lang sein"
|
||||
value={value}
|
||||
id="apiKey"
|
||||
maxLength={15}
|
||||
onChange={(e) => {
|
||||
setValue(e.currentTarget.value.slice(0, 15));
|
||||
}}
|
||||
/>
|
||||
</InputGroup>
|
||||
<Field.Root>
|
||||
<Field.Label>API key</Field.Label>
|
||||
<Input type="number" id="apiKey" />
|
||||
</Field.Root>
|
||||
<Field.Root>
|
||||
<Field.Label>Benutzer</Field.Label>
|
||||
<Input id="user" type="text" />
|
||||
<Field.Label>Name</Field.Label>
|
||||
<Input id="name" type="text" />
|
||||
</Field.Root>
|
||||
</Stack>
|
||||
</Card.Body>
|
||||
@@ -44,14 +68,14 @@ const AddAPIKey: React.FC<AddAPIKeyProps> = ({ onClose, alert }) => {
|
||||
(
|
||||
document.getElementById("apiKey") as HTMLInputElement
|
||||
)?.value.trim() || "";
|
||||
const user =
|
||||
const name =
|
||||
(
|
||||
document.getElementById("user") as HTMLInputElement
|
||||
document.getElementById("name") as HTMLInputElement
|
||||
)?.value.trim() || "";
|
||||
|
||||
if (!apiKey || !user) return;
|
||||
if (!apiKey || !name) return;
|
||||
|
||||
const res = await createAPIentry(apiKey, user);
|
||||
const res = await createAPIentry(apiKey, name);
|
||||
if (res.success) {
|
||||
alert(
|
||||
"success",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import React from "react";
|
||||
import { Button, Card, Field, Input, Stack } from "@chakra-ui/react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Field,
|
||||
Input,
|
||||
Stack,
|
||||
Text,
|
||||
Checkbox,
|
||||
} from "@chakra-ui/react";
|
||||
import { createUser } from "@/utils/userActions";
|
||||
|
||||
type AddFormProps = {
|
||||
@@ -12,73 +20,128 @@ type AddFormProps = {
|
||||
};
|
||||
|
||||
const AddForm: React.FC<AddFormProps> = ({ onClose, alert }) => {
|
||||
const [admin, setAdmin] = React.useState(false);
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
|
||||
<Card.Root maxW="sm">
|
||||
<Card.Header>
|
||||
<Card.Title>Neuen Nutzer erstellen</Card.Title>
|
||||
<Card.Description>
|
||||
Füllen Sie das folgende Formular aus, um einen Nutzer zu erstellen.
|
||||
</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<Stack gap="4" w="full">
|
||||
<Field.Root>
|
||||
<Field.Label>Username</Field.Label>
|
||||
<Input id="username" />
|
||||
</Field.Root>
|
||||
<Field.Root>
|
||||
<Field.Label>Password</Field.Label>
|
||||
<Input id="password" type="password" />
|
||||
</Field.Root>
|
||||
<Field.Root>
|
||||
<Field.Label>Role</Field.Label>
|
||||
<Input id="role" type="number" />
|
||||
</Field.Root>
|
||||
</Stack>
|
||||
</Card.Body>
|
||||
<Card.Footer justifyContent="flex-end">
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
Abbrechen
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
onClick={async () => {
|
||||
const username =
|
||||
(
|
||||
document.getElementById("username") as HTMLInputElement
|
||||
)?.value.trim() || "";
|
||||
const password =
|
||||
(document.getElementById("password") as HTMLInputElement)
|
||||
?.value || "";
|
||||
const role = Number(
|
||||
(document.getElementById("role") as HTMLInputElement)?.value
|
||||
);
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<Card.Root maxW="sm">
|
||||
<Card.Header>
|
||||
<Card.Title>Neuen Nutzer erstellen</Card.Title>
|
||||
<Card.Description>
|
||||
Füllen Sie das folgende Formular aus, um einen Nutzer zu
|
||||
erstellen.
|
||||
</Card.Description>
|
||||
</Card.Header>
|
||||
|
||||
if (!username || !password || Number.isNaN(role)) return;
|
||||
<Card.Body>
|
||||
<Stack gap="4" w="full">
|
||||
<Field.Root>
|
||||
<Field.Label>Benutzername</Field.Label>
|
||||
<Input id="username" />
|
||||
</Field.Root>
|
||||
<Field.Root>
|
||||
<Field.Label>Passwort</Field.Label>
|
||||
<Input id="password" type="password" />
|
||||
</Field.Root>
|
||||
<Field.Root>
|
||||
<Field.Label>Vorname</Field.Label>
|
||||
<Input id="firstname" />
|
||||
</Field.Root>
|
||||
<Field.Root>
|
||||
<Field.Label>Nachname</Field.Label>
|
||||
<Input id="lastname" />
|
||||
</Field.Root>
|
||||
<Field.Root>
|
||||
<Field.Label>E-Mail</Field.Label>
|
||||
<Input id="email" type="email" />
|
||||
</Field.Root>
|
||||
|
||||
const res = await createUser(username, role, password);
|
||||
if (res.success) {
|
||||
alert(
|
||||
"success",
|
||||
"Nutzer erstellt",
|
||||
"Der Nutzer wurde erfolgreich erstellt."
|
||||
{/* Kontrollierte Checkbox */}
|
||||
<Checkbox.Root
|
||||
checked={admin}
|
||||
onCheckedChange={(e: any) => setAdmin(Boolean(e?.checked ?? e))}
|
||||
>
|
||||
<Checkbox.HiddenInput />
|
||||
<Checkbox.Control />
|
||||
<Checkbox.Label>Admin</Checkbox.Label>
|
||||
</Checkbox.Root>
|
||||
|
||||
<Field.Root>
|
||||
<Field.Label>Rolle</Field.Label>
|
||||
<Input id="role" type="number" />
|
||||
</Field.Root>
|
||||
</Stack>
|
||||
</Card.Body>
|
||||
<Card.Footer justifyContent="flex-end">
|
||||
<Text>Der Benutzername kann nicht mehr geändert werden.</Text>
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
Abbrechen
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
type="submit"
|
||||
onClick={async () => {
|
||||
const username =
|
||||
(
|
||||
document.getElementById("username") as HTMLInputElement
|
||||
)?.value.trim() || "";
|
||||
const password =
|
||||
(document.getElementById("password") as HTMLInputElement)
|
||||
?.value || "";
|
||||
const role = Number(
|
||||
(document.getElementById("role") as HTMLInputElement)?.value
|
||||
);
|
||||
onClose();
|
||||
} else {
|
||||
alert(
|
||||
"error",
|
||||
"Fehler beim Erstellen des Nutzers",
|
||||
"Es gab einen Fehler beim Erstellen des Nutzers. Vielleicht gibt es bereits einen Nutzer mit diesem Benutzernamen."
|
||||
const firstname =
|
||||
(
|
||||
document.getElementById("firstname") as HTMLInputElement
|
||||
)?.value.trim() || "";
|
||||
const lastname =
|
||||
(
|
||||
document.getElementById("lastname") as HTMLInputElement
|
||||
)?.value.trim() || "";
|
||||
const email =
|
||||
(
|
||||
document.getElementById("email") as HTMLInputElement
|
||||
)?.value.trim() || "";
|
||||
|
||||
// admin kommt jetzt zuverlässig aus dem State
|
||||
const res = await createUser(
|
||||
username,
|
||||
role,
|
||||
password,
|
||||
firstname,
|
||||
lastname,
|
||||
email,
|
||||
admin
|
||||
);
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Erstellen
|
||||
</Button>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
|
||||
if (res.success) {
|
||||
alert(
|
||||
"success",
|
||||
"Nutzer erstellt",
|
||||
"Der Nutzer wurde erfolgreich erstellt."
|
||||
);
|
||||
onClose();
|
||||
} else {
|
||||
alert(
|
||||
"error",
|
||||
"Fehler beim Erstellen des Nutzers",
|
||||
"Es gab einen Fehler beim Erstellen des Nutzers. Vielleicht gibt es bereits einen Nutzer mit diesem Benutzernamen."
|
||||
);
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Erstellen
|
||||
</Button>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -30,18 +30,17 @@ import {
|
||||
} from "@/utils/userActions";
|
||||
import AddItemForm from "./AddItemForm";
|
||||
import { formatDateTime } from "@/utils/userFuncs";
|
||||
|
||||
const API_BASE =
|
||||
(import.meta as any).env?.VITE_BACKEND_URL ||
|
||||
import.meta.env.VITE_BACKEND_URL ||
|
||||
"http://localhost:8002";
|
||||
import { API_BASE } from "@/config/api.config";
|
||||
|
||||
type Items = {
|
||||
id: number;
|
||||
item_name: string;
|
||||
can_borrow_role: string;
|
||||
inSafe: boolean;
|
||||
in_safe: boolean;
|
||||
entry_created_at: string;
|
||||
entry_updated_at: string;
|
||||
last_borrowed_person: string | null;
|
||||
currently_borrowing: string | null;
|
||||
};
|
||||
|
||||
const ItemTable: React.FC = () => {
|
||||
@@ -82,12 +81,15 @@ const ItemTable: React.FC = () => {
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/allItems`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||
},
|
||||
});
|
||||
const response = await fetch(
|
||||
`${API_BASE}/api/admin/item-data/all-items`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
@@ -193,6 +195,15 @@ const ItemTable: React.FC = () => {
|
||||
<Table.ColumnHeader>
|
||||
<strong>Eintrag erstellt am</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Eintrag aktualisiert am</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Letzte ausleihende Person</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Derzeit ausgeliehen von</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Aktionen</strong>
|
||||
</Table.ColumnHeader>
|
||||
@@ -229,31 +240,34 @@ const ItemTable: React.FC = () => {
|
||||
py={1}
|
||||
gap={2}
|
||||
variant="ghost"
|
||||
color={item.inSafe ? "green.600" : "red.600"}
|
||||
color={item.in_safe ? "green.600" : "red.600"}
|
||||
borderWidth="1px"
|
||||
borderColor={item.inSafe ? "green.300" : "red.300"}
|
||||
borderColor={item.in_safe ? "green.300" : "red.300"}
|
||||
_hover={{
|
||||
bg: item.inSafe ? "green.50" : "red.50",
|
||||
borderColor: item.inSafe ? "green.400" : "red.400",
|
||||
bg: item.in_safe ? "green.50" : "red.50",
|
||||
borderColor: item.in_safe ? "green.400" : "red.400",
|
||||
transform: "translateY(-1px)",
|
||||
shadow: "sm",
|
||||
}}
|
||||
_active={{ transform: "translateY(0)" }}
|
||||
aria-label={
|
||||
item.inSafe ? "Mark as not in safe" : "Mark as in safe"
|
||||
item.in_safe ? "Mark as not in safe" : "Mark as in safe"
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
as={item.inSafe ? CheckCircle2 : XCircle}
|
||||
as={item.in_safe ? CheckCircle2 : XCircle}
|
||||
boxSize={3.5}
|
||||
mr={2}
|
||||
/>
|
||||
<Text as="span" fontSize="xs" fontWeight="semibold">
|
||||
{item.inSafe ? "Yes" : "No"}
|
||||
{item.in_safe ? "Yes" : "No"}
|
||||
</Text>
|
||||
</Button>
|
||||
</Table.Cell>
|
||||
<Table.Cell>{formatDateTime(item.entry_created_at)}</Table.Cell>
|
||||
<Table.Cell>{formatDateTime(item.entry_updated_at)}</Table.Cell>
|
||||
<Table.Cell>{item.last_borrowed_person}</Table.Cell>
|
||||
<Table.Cell>{item.currently_borrowing}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
onClick={() =>
|
||||
|
||||
@@ -17,11 +17,7 @@ import MyAlert from "./myChakra/MyAlert";
|
||||
import { formatDateTime } from "@/utils/userFuncs";
|
||||
import { Trash2, RefreshCcwDot } from "lucide-react";
|
||||
import { deleteLoan } from "@/utils/userActions";
|
||||
|
||||
const API_BASE =
|
||||
(import.meta as any).env?.VITE_BACKEND_URL ||
|
||||
import.meta.env.VITE_BACKEND_URL ||
|
||||
"http://localhost:8002";
|
||||
import { API_BASE } from "@/config/api.config";
|
||||
|
||||
const LoanTable: React.FC = () => {
|
||||
const [items, setItems] = useState<Loan[]>([]);
|
||||
@@ -55,18 +51,22 @@ const LoanTable: React.FC = () => {
|
||||
created_at: string;
|
||||
loaned_items_name: string[];
|
||||
deleted: boolean;
|
||||
note: string;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/allLoans`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||
},
|
||||
});
|
||||
const response = await fetch(
|
||||
`${API_BASE}/api/admin/loan-data/all-loans`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
@@ -161,6 +161,9 @@ const LoanTable: React.FC = () => {
|
||||
<Table.ColumnHeader>
|
||||
<strong>Ausgeliehene Artikel</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Notiz</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Aktionen</strong>
|
||||
</Table.ColumnHeader>
|
||||
@@ -180,6 +183,7 @@ const LoanTable: React.FC = () => {
|
||||
<Table.Cell>{formatDateTime(item.returned_date)}</Table.Cell>
|
||||
<Table.Cell>{formatDateTime(item.created_at)}</Table.Cell>
|
||||
<Table.Cell>{item.loaned_items_name.join(", ")}</Table.Cell>
|
||||
<Table.Cell>{item.note}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
onClick={() =>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
HStack,
|
||||
IconButton,
|
||||
Heading,
|
||||
Switch, // neu
|
||||
} from "@chakra-ui/react";
|
||||
import { Tooltip } from "@/components/ui/tooltip";
|
||||
import { fetchUserData } from "@/utils/fetcher";
|
||||
@@ -23,9 +24,13 @@ import ChangePWform from "./ChangePWform";
|
||||
type User = {
|
||||
id: number;
|
||||
username: string;
|
||||
password: string;
|
||||
role: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email: string;
|
||||
is_admin: boolean;
|
||||
role: number;
|
||||
entry_created_at: string;
|
||||
entry_updated_at: string;
|
||||
};
|
||||
|
||||
const UserTable: React.FC = () => {
|
||||
@@ -52,10 +57,20 @@ const UserTable: React.FC = () => {
|
||||
setIsError(true);
|
||||
};
|
||||
|
||||
const handleInputChange = (userId: number, field: string, value: string) => {
|
||||
const handleInputChange = (userId: number, field: string, value: any) => {
|
||||
setUsers((prevUsers) =>
|
||||
prevUsers.map((user) =>
|
||||
user.id === userId ? { ...user, [field]: value } : user
|
||||
user.id === userId
|
||||
? {
|
||||
...user,
|
||||
[field]:
|
||||
field === "role"
|
||||
? Number(value)
|
||||
: field === "is_admin"
|
||||
? value === true || value === "true" || value === 1
|
||||
: value,
|
||||
}
|
||||
: user
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -70,7 +85,7 @@ const UserTable: React.FC = () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const data = await fetchUserData();
|
||||
console.log("user api response", data);
|
||||
console.log(data);
|
||||
if (Array.isArray(data)) {
|
||||
setUsers(data);
|
||||
} else {
|
||||
@@ -189,6 +204,18 @@ const UserTable: React.FC = () => {
|
||||
<Table.ColumnHeader>
|
||||
<strong>Benutzername</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Vorname</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Nachname</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>E-Mail</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Admin</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Passwort ändern</strong>
|
||||
</Table.ColumnHeader>
|
||||
@@ -198,6 +225,9 @@ const UserTable: React.FC = () => {
|
||||
<Table.ColumnHeader>
|
||||
<strong>Eintrag erstellt am</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Eintrag aktualisiert am</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Aktionen</strong>
|
||||
</Table.ColumnHeader>
|
||||
@@ -207,14 +237,58 @@ const UserTable: React.FC = () => {
|
||||
{users.map((user) => (
|
||||
<Table.Row key={user.id}>
|
||||
<Table.Cell>{user.id}</Table.Cell>
|
||||
<Table.Cell>{user.username}</Table.Cell>
|
||||
|
||||
{/* Vorname */}
|
||||
<Table.Cell>
|
||||
<Input
|
||||
size="sm"
|
||||
value={user.first_name ?? ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange(user.id, "username", e.target.value)
|
||||
handleInputChange(user.id, "first_name", e.target.value)
|
||||
}
|
||||
value={user.username}
|
||||
/>
|
||||
</Table.Cell>
|
||||
|
||||
{/* Nachname */}
|
||||
<Table.Cell>
|
||||
<Input
|
||||
size="sm"
|
||||
value={user.last_name ?? ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange(user.id, "last_name", e.target.value)
|
||||
}
|
||||
/>
|
||||
</Table.Cell>
|
||||
|
||||
{/* E-Mail */}
|
||||
<Table.Cell>
|
||||
<Input
|
||||
type="email"
|
||||
size="sm"
|
||||
value={user.email ?? ""}
|
||||
onChange={(e) =>
|
||||
handleInputChange(user.id, "email", e.target.value)
|
||||
}
|
||||
/>
|
||||
</Table.Cell>
|
||||
|
||||
{/* Admin */}
|
||||
<Table.Cell>
|
||||
<Switch.Root
|
||||
size="sm"
|
||||
checked={!!user.is_admin}
|
||||
onCheckedChange={(details) =>
|
||||
handleInputChange(user.id, "is_admin", details.checked)
|
||||
}
|
||||
aria-label="Adminrechte umschalten"
|
||||
>
|
||||
<Switch.Control>
|
||||
<Switch.Thumb />
|
||||
</Switch.Control>
|
||||
<Switch.HiddenInput />
|
||||
</Switch.Root>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button onClick={() => handlePasswordChange(user.username)}>
|
||||
Passwort ändern
|
||||
@@ -230,13 +304,17 @@ const UserTable: React.FC = () => {
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>{formatDateTime(user.entry_created_at)}</Table.Cell>
|
||||
<Table.Cell>{formatDateTime(user.entry_updated_at)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
onClick={() =>
|
||||
handleEdit(
|
||||
user.id,
|
||||
user.username,
|
||||
user.role,
|
||||
user.first_name,
|
||||
user.last_name,
|
||||
user.email,
|
||||
user.is_admin,
|
||||
Number(user.role)
|
||||
).then((response) => {
|
||||
if (response.success) {
|
||||
setError(
|
||||
|
||||
Reference in New Issue
Block a user