Merge branch 'dev_v1-admin' into debian12_v1-admin
This commit is contained in:
@@ -65,6 +65,13 @@ const AddForm: React.FC<AddFormProps> = ({ onClose, alert }) => {
|
||||
"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();
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
115
admin/src/components/ChangePWform.tsx
Normal file
115
admin/src/components/ChangePWform.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import React from "react";
|
||||
import { Button, Card, Field, Input, Stack, Alert } from "@chakra-ui/react";
|
||||
import { changePW } from "@/utils/userActions";
|
||||
import { useState } from "react";
|
||||
|
||||
type ChangePWformProps = {
|
||||
onClose: () => void;
|
||||
alert: (
|
||||
status: "success" | "error",
|
||||
message: string,
|
||||
description: string
|
||||
) => void;
|
||||
username: string;
|
||||
};
|
||||
|
||||
const ChangePWform: React.FC<ChangePWformProps> = ({
|
||||
onClose,
|
||||
alert,
|
||||
username,
|
||||
}) => {
|
||||
const [showSubAlert, setShowSubAlert] = useState(false);
|
||||
const [subAlertMessage, setSubAlertMessage] = useState("");
|
||||
|
||||
const subAlert = (message: string) => {
|
||||
setSubAlertMessage(message);
|
||||
setShowSubAlert(true);
|
||||
};
|
||||
|
||||
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>Passwort ändern</Card.Title>
|
||||
<Card.Description>
|
||||
Füllen Sie das folgende Formular aus, um das Passwort zu ändern.
|
||||
</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<Stack gap="4" w="full">
|
||||
<Field.Root>
|
||||
<Field.Label>Neues Passwort</Field.Label>
|
||||
<Input
|
||||
id="new_password"
|
||||
type="password"
|
||||
placeholder="Neues Passwort"
|
||||
/>
|
||||
</Field.Root>
|
||||
<Field.Root>
|
||||
<Field.Label>Neues Passwort widerholen</Field.Label>
|
||||
<Input
|
||||
id="confirm_new_password"
|
||||
type="password"
|
||||
placeholder="Wiederholen Sie das neue Passwort"
|
||||
/>
|
||||
</Field.Root>
|
||||
</Stack>
|
||||
</Card.Body>
|
||||
<Card.Footer justifyContent="flex-end" gap="2">
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
Abbrechen
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
onClick={async () => {
|
||||
const newPassword =
|
||||
(
|
||||
document.getElementById("new_password") as HTMLInputElement
|
||||
)?.value.trim() || "";
|
||||
const confirmNewPassword =
|
||||
(
|
||||
document.getElementById(
|
||||
"confirm_new_password"
|
||||
) as HTMLInputElement
|
||||
)?.value.trim() || "";
|
||||
|
||||
if (!newPassword || newPassword !== confirmNewPassword) {
|
||||
subAlert("Passwörter stimmen nicht überein!");
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await changePW(newPassword, username);
|
||||
if (res.success) {
|
||||
alert(
|
||||
"success",
|
||||
"Passwort geändert",
|
||||
"Das Passwort wurde erfolgreich geändert."
|
||||
);
|
||||
onClose();
|
||||
} else {
|
||||
alert(
|
||||
"error",
|
||||
"Fehler",
|
||||
"Das Passwort konnte nicht geändert werden."
|
||||
);
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Ändern
|
||||
</Button>
|
||||
{showSubAlert && (
|
||||
<Alert.Root status="error">
|
||||
<Alert.Indicator />
|
||||
<Alert.Content>
|
||||
<Alert.Title>{subAlertMessage}</Alert.Title>
|
||||
</Alert.Content>
|
||||
</Alert.Root>
|
||||
)}
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChangePWform;
|
@@ -24,6 +24,7 @@ import Cookies from "js-cookie";
|
||||
import { useState, useEffect } from "react";
|
||||
import { deleteItem } from "@/utils/userActions";
|
||||
import AddItemForm from "./AddItemForm";
|
||||
import { formatDateTime } from "@/utils/userFuncs";
|
||||
|
||||
type Items = {
|
||||
id: number;
|
||||
@@ -232,7 +233,7 @@ const ItemTable: React.FC = () => {
|
||||
</Tag.Root>
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>{item.entry_created_at}</Table.Cell>
|
||||
<Table.Cell>{formatDateTime(item.entry_created_at)}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
onClick={() =>
|
||||
|
@@ -78,10 +78,6 @@ const LoanTable: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading marginBottom={4} size="md">
|
||||
Ausleihen
|
||||
</Heading>
|
||||
|
||||
{/* Action toolbar */}
|
||||
<HStack
|
||||
mb={4}
|
||||
@@ -107,6 +103,10 @@ const LoanTable: React.FC = () => {
|
||||
</HStack>
|
||||
{/* End action toolbar */}
|
||||
|
||||
<Heading marginBottom={4} size="md">
|
||||
Ausleihen
|
||||
</Heading>
|
||||
|
||||
{isError && (
|
||||
<MyAlert
|
||||
status={errorStatus}
|
||||
|
@@ -18,6 +18,7 @@ import { handleDelete, handleEdit } from "@/utils/userActions";
|
||||
import MyAlert from "./myChakra/MyAlert";
|
||||
import AddForm from "./AddForm";
|
||||
import { formatDateTime } from "@/utils/userFuncs";
|
||||
import ChangePWform from "./ChangePWform";
|
||||
|
||||
type User = {
|
||||
id: number;
|
||||
@@ -36,6 +37,8 @@ const UserTable: React.FC = () => {
|
||||
const [errorDsc, setErrorDsc] = useState("");
|
||||
const [reload, setReload] = useState(false);
|
||||
const [addForm, setAddForm] = useState(false);
|
||||
const [changePWform, setChangePWform] = useState(false);
|
||||
const [changeUsr, setChangeUsr] = useState("");
|
||||
|
||||
const setError = (
|
||||
status: "error" | "success",
|
||||
@@ -57,6 +60,11 @@ const UserTable: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const handlePasswordChange = (username: string) => {
|
||||
setChangeUsr(username);
|
||||
setChangePWform(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUsers = async () => {
|
||||
setIsLoading(true);
|
||||
@@ -139,6 +147,16 @@ const UserTable: React.FC = () => {
|
||||
<Heading marginBottom={4} size="md">
|
||||
Benutzer
|
||||
</Heading>
|
||||
{changePWform && (
|
||||
<ChangePWform
|
||||
onClose={() => {
|
||||
setChangePWform(false);
|
||||
setReload(!reload);
|
||||
}}
|
||||
alert={setError}
|
||||
username={changeUsr}
|
||||
/>
|
||||
)}
|
||||
{isError && (
|
||||
<MyAlert
|
||||
status={errorStatus}
|
||||
@@ -172,7 +190,7 @@ const UserTable: React.FC = () => {
|
||||
<strong>Benutzername</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Passwort</strong>
|
||||
<strong>Passwort ändern</strong>
|
||||
</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>
|
||||
<strong>Rolle</strong>
|
||||
@@ -198,12 +216,9 @@ const UserTable: React.FC = () => {
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
onChange={(e) =>
|
||||
handleInputChange(user.id, "password", e.target.value)
|
||||
}
|
||||
value={user.password}
|
||||
/>
|
||||
<Button onClick={() => handlePasswordChange(user.username)}>
|
||||
Passwort ändern
|
||||
</Button>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
@@ -222,7 +237,6 @@ const UserTable: React.FC = () => {
|
||||
user.id,
|
||||
user.username,
|
||||
user.role,
|
||||
user.password
|
||||
).then((response) => {
|
||||
if (response.success) {
|
||||
setError(
|
||||
|
@@ -24,19 +24,18 @@ export const handleDelete = async (userId: number) => {
|
||||
export const handleEdit = async (
|
||||
userId: number,
|
||||
username: string,
|
||||
role: string,
|
||||
password: string
|
||||
role: string
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://backend.insta.the1s.de/api/editUser/${userId}`,
|
||||
{
|
||||
method: "PUT",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||
},
|
||||
body: JSON.stringify({ username, role, password }),
|
||||
body: JSON.stringify({ username, role }),
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
@@ -73,6 +72,26 @@ export const createUser = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const changePW = async (newPassword: string, username: string) => {
|
||||
try {
|
||||
const response = await fetch(`http://localhost:8002/api/changePWadmin`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||
},
|
||||
body: JSON.stringify({ newPassword, username }),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to change password");
|
||||
}
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Error changing password:", error);
|
||||
return { success: false };
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteLoan = async (loanId: number) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
|
@@ -1,14 +1,7 @@
|
||||
export const formatDateTime = (value: string | null | undefined) => {
|
||||
if (!value) return "N/A";
|
||||
const inpDate = new Date(value);
|
||||
if (isNaN(inpDate.getTime())) return "N/A";
|
||||
return (
|
||||
inpDate.toLocaleString(undefined, {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}) + " Uhr"
|
||||
);
|
||||
const m = value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})/);
|
||||
if (!m) return "N/A";
|
||||
const [, y, M, d, h, min] = m;
|
||||
return `${d}.${M}.${y} ${h}:${min} Uhr`;
|
||||
};
|
||||
|
Reference in New Issue
Block a user