Compare commits

..

2 Commits

Author SHA1 Message Date
theis.gaedigk f8e29dca10 improved loan tabel on admin panel 2026-04-26 22:21:00 +02:00
theis.gaedigk e52fc13da4 updated changelog 2026-04-26 22:09:19 +02:00
4 changed files with 358 additions and 267 deletions
+2 -2
View File
@@ -47,7 +47,7 @@ const Dashboard: React.FC<DashboardProps> = ({ onLogout }) => {
viewAPI={() => setActiveView("API")} viewAPI={() => setActiveView("API")}
viewConfig={() => setActiveView("Server Konfiguration")} viewConfig={() => setActiveView("Server Konfiguration")}
/> />
<Box flex="1" display="flex" flexDirection="column"> <Box flex="1" display="flex" flexDirection="column" minH={0}>
<Flex <Flex
as="header" as="header"
align="center" align="center"
@@ -68,7 +68,7 @@ const Dashboard: React.FC<DashboardProps> = ({ onLogout }) => {
</Button> </Button>
</Flex> </Flex>
</Flex> </Flex>
<Box as="main" flex="1" p={6}> <Box as="main" flex="1" p={6} minH={0} overflow="hidden">
{activeView === "" && ( {activeView === "" && (
<Flex <Flex
align="center" align="center"
+57 -13
View File
@@ -57,32 +57,32 @@ const ItemTable: React.FC = () => {
const handleItemNameChange = (id: number, value: string) => { const handleItemNameChange = (id: number, value: string) => {
setItems((prev) => setItems((prev) =>
prev.map((it) => (it.id === id ? { ...it, item_name: value } : it)) prev.map((it) => (it.id === id ? { ...it, item_name: value } : it)),
); );
}; };
const handleCanBorrowRoleChange = (id: number, value: string) => { const handleCanBorrowRoleChange = (id: number, value: string) => {
setItems((prev) => setItems((prev) =>
prev.map((it) => (it.id === id ? { ...it, can_borrow_role: value } : it)) prev.map((it) => (it.id === id ? { ...it, can_borrow_role: value } : it)),
); );
}; };
const handleLockerNumberChange = (id: number, value: string) => { const handleLockerNumberChange = (id: number, value: string) => {
setItems((prev) => setItems((prev) =>
prev.map((it) => (it.id === id ? { ...it, safe_nr: value } : it)) prev.map((it) => (it.id === id ? { ...it, safe_nr: value } : it)),
); );
}; };
const handleDoorKeyChange = (id: number, value: string) => { const handleDoorKeyChange = (id: number, value: string) => {
setItems((prev) => setItems((prev) =>
prev.map((it) => (it.id === id ? { ...it, door_key: value } : it)) prev.map((it) => (it.id === id ? { ...it, door_key: value } : it)),
); );
}; };
const setError = ( const setError = (
status: "error" | "success", status: "error" | "success",
message: string, message: string,
description: string description: string,
) => { ) => {
setIsError(false); setIsError(false);
setErrorStatus(status); setErrorStatus(status);
@@ -102,7 +102,7 @@ const ItemTable: React.FC = () => {
headers: { headers: {
Authorization: `Bearer ${Cookies.get("token")}`, Authorization: `Bearer ${Cookies.get("token")}`,
}, },
} },
); );
const data = await response.json(); const data = await response.json();
return data; return data;
@@ -193,11 +193,50 @@ const ItemTable: React.FC = () => {
{/* make table fill available width, like UserTable */} {/* make table fill available width, like UserTable */}
{!isLoading && ( {!isLoading && (
<Table.ScrollArea flex="1" minH={0} rounded="md" mt={4}>
<Table.Root <Table.Root
size="sm" size="sm"
striped striped
w="100%" stickyHeader
style={{ tableLayout: "auto" }} // Spalten nach Content css={{
"& [data-sticky]": {
position: "sticky",
zIndex: 1,
bg: "bg",
_after: {
content: '""',
position: "absolute",
pointerEvents: "none",
top: "0",
bottom: "-1px",
width: "32px",
},
},
"& [data-sticky=end]": {
_after: {
insetInlineEnd: "0",
translate: "100% 0",
shadow: "inset 8px 0px 8px -8px rgba(0, 0, 0, 0.16)",
},
},
"& [data-sticky=start]": {
_after: {
insetInlineStart: "0",
translate: "-100% 0",
shadow: "inset -8px 0px 8px -8px rgba(0, 0, 0, 0.16)",
},
},
"& thead tr": {
shadow: "0 1px 0 0 {colors.border}",
"&:has(th[data-sticky])": {
zIndex: 2,
},
},
}}
> >
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
@@ -315,8 +354,12 @@ const ItemTable: React.FC = () => {
value={item.door_key} value={item.door_key}
/> />
</Table.Cell> </Table.Cell>
<Table.Cell>{formatDateTime(item.entry_created_at)}</Table.Cell> <Table.Cell>
<Table.Cell>{formatDateTime(item.entry_updated_at)}</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.last_borrowed_person}</Table.Cell>
<Table.Cell>{item.currently_borrowing}</Table.Cell> <Table.Cell>{item.currently_borrowing}</Table.Cell>
<Table.Cell whiteSpace="nowrap"> <Table.Cell whiteSpace="nowrap">
@@ -327,7 +370,7 @@ const ItemTable: React.FC = () => {
item.item_name, item.item_name,
item.safe_nr, item.safe_nr,
item.door_key, item.door_key,
item.can_borrow_role item.can_borrow_role,
).then((response) => { ).then((response) => {
if (response.success) { if (response.success) {
setError( setError(
@@ -338,7 +381,7 @@ const ItemTable: React.FC = () => {
item.item_name + item.item_name +
'" mit ID ' + '" mit ID ' +
item.id + item.id +
" bearbeitet." " bearbeitet.",
); );
} }
}) })
@@ -356,7 +399,7 @@ const ItemTable: React.FC = () => {
setError( setError(
"success", "success",
"Gegenstand gelöscht", "Gegenstand gelöscht",
"Der Gegenstand wurde erfolgreich gelöscht." "Der Gegenstand wurde erfolgreich gelöscht.",
); );
} }
}) })
@@ -372,6 +415,7 @@ const ItemTable: React.FC = () => {
))} ))}
</Table.Body> </Table.Body>
</Table.Root> </Table.Root>
</Table.ScrollArea>
)} )}
<Text>* LaP = Letzte ausleihende Person</Text> <Text>* LaP = Letzte ausleihende Person</Text>
<Text>** Dav = Derzeit ausgeliehen von</Text> <Text>** Dav = Derzeit ausgeliehen von</Text>
+52 -6
View File
@@ -1,5 +1,6 @@
import React from "react"; import React from "react";
import { import {
Box,
Table, Table,
Spinner, Spinner,
Text, Text,
@@ -31,7 +32,7 @@ const LoanTable: React.FC = () => {
const setError = ( const setError = (
status: "error" | "success", status: "error" | "success",
message: string, message: string,
description: string description: string,
) => { ) => {
setIsError(false); setIsError(false);
setErrorStatus(status); setErrorStatus(status);
@@ -65,7 +66,7 @@ const LoanTable: React.FC = () => {
headers: { headers: {
Authorization: `Bearer ${Cookies.get("token")}`, Authorization: `Bearer ${Cookies.get("token")}`,
}, },
} },
); );
const data = await response.json(); const data = await response.json();
return data; return data;
@@ -83,7 +84,7 @@ const LoanTable: React.FC = () => {
}, [reload]); }, [reload]);
return ( return (
<> <Box h="full" display="flex" flexDirection="column" minH={0}>
{/* Action toolbar */} {/* Action toolbar */}
<HStack <HStack
mb={4} mb={4}
@@ -131,7 +132,51 @@ const LoanTable: React.FC = () => {
</VStack> </VStack>
)} )}
{!isLoading && ( {!isLoading && (
<Table.Root size="sm" striped> <Table.ScrollArea flex="1" minH={0} rounded="md" mt={4}>
<Table.Root
size="sm"
striped
stickyHeader
css={{
"& [data-sticky]": {
position: "sticky",
zIndex: 1,
bg: "bg",
_after: {
content: '""',
position: "absolute",
pointerEvents: "none",
top: "0",
bottom: "-1px",
width: "32px",
},
},
"& [data-sticky=end]": {
_after: {
insetInlineEnd: "0",
translate: "100% 0",
shadow: "inset 8px 0px 8px -8px rgba(0, 0, 0, 0.16)",
},
},
"& [data-sticky=start]": {
_after: {
insetInlineStart: "0",
translate: "-100% 0",
shadow: "inset -8px 0px 8px -8px rgba(0, 0, 0, 0.16)",
},
},
"& thead tr": {
shadow: "0 1px 0 0 {colors.border}",
"&:has(th[data-sticky])": {
zIndex: 2,
},
},
}}
>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.ColumnHeader> <Table.ColumnHeader>
@@ -193,7 +238,7 @@ const LoanTable: React.FC = () => {
setError( setError(
"success", "success",
"Loan deleted", "Loan deleted",
"The loan has been successfully deleted." "The loan has been successfully deleted.",
); );
} }
}) })
@@ -209,8 +254,9 @@ const LoanTable: React.FC = () => {
))} ))}
</Table.Body> </Table.Body>
</Table.Root> </Table.Root>
</Table.ScrollArea>
)} )}
</> </Box>
); );
}; };
+1
View File
@@ -15,6 +15,7 @@ This update provides some new features for the design. It also contains some imp
- Improved error logging - Improved error logging
- If you try to delete a loan that has not been returned yet, you will get an 507 error code. - If you try to delete a loan that has not been returned yet, you will get an 507 error code.
- When the admin deletes a loan, the loan will be still visible in the database, but it will be marked as deleted. This is to prevent data loss and to keep track of deleted loans. - When the admin deletes a loan, the loan will be still visible in the database, but it will be marked as deleted. This is to prevent data loss and to keep track of deleted loans.
- Mailer improvements: The mailer is now more clearly organised. Two large code files are now split into five smaller code files which are easier to maintain. Also the design of the mails has improved.
## Fixed bugs ## Fixed bugs