2 Commits

Author SHA1 Message Date
85b519c5b1 Merge branch 'dev_v1-admin' into debian12_v1-admin 2025-09-24 17:56:52 +02:00
49f4ba8483 added itemview to landingpage 2025-09-22 13:22:27 +02:00
2 changed files with 99 additions and 39 deletions

View File

@@ -6,10 +6,11 @@ import {
Table, Table,
Heading, Heading,
HStack, HStack,
IconButton, Card,
SimpleGrid,
Button,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Tooltip } from "@/components/ui/tooltip"; import { Lock, LockOpen } from "lucide-react";
import { RefreshCcwDot } from "lucide-react";
import MyAlert from "../myChakra/MyAlert"; import MyAlert from "../myChakra/MyAlert";
import { formatDateTime } from "@/utils/userFuncs"; import { formatDateTime } from "@/utils/userFuncs";
@@ -23,14 +24,22 @@ type Loan = {
loaned_items_name: string[] | string; loaned_items_name: string[] | string;
}; };
type Device = {
id: number;
item_name: string;
can_borrow_role: string;
inSafe: number;
entry_created_at: string;
};
const Landingpage: React.FC = () => { const Landingpage: React.FC = () => {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [loans, setLoans] = useState<Loan[]>([]); const [loans, setLoans] = useState<Loan[]>([]);
const [devices, setDevices] = useState<Device[]>([]);
const [isError, setIsError] = useState(false); const [isError, setIsError] = useState(false);
const [errorStatus, setErrorStatus] = useState<"error" | "success">("error"); const [errorStatus, setErrorStatus] = useState<"error" | "success">("error");
const [errorMessage, setErrorMessage] = useState(""); const [errorMessage, setErrorMessage] = useState("");
const [errorDsc, setErrorDsc] = useState(""); const [errorDsc, setErrorDsc] = useState("");
const [reload, setReload] = useState(false);
const setError = ( const setError = (
status: "error" | "success", status: "error" | "success",
@@ -45,20 +54,30 @@ const Landingpage: React.FC = () => {
}; };
useEffect(() => { useEffect(() => {
const fetchLoans = async () => { const fetchData = async () => {
setIsLoading(true); setIsLoading(true);
try { try {
const res = await fetch( const loanRes = await fetch("http://localhost:8002/apiV2/allLoans");
"https://backend.insta.the1s.de/apiV2/allLoans" const loanData = await loanRes.json();
); if (Array.isArray(loanData)) {
const data = await res.json(); setLoans(loanData);
if (Array.isArray(data)) {
setLoans(data);
} else { } else {
setError( setError(
"error", "error",
"Fehler beim Laden", "Fehler beim Laden",
"Unerwartetes Datenformat erhalten." "Unerwartetes Datenformat erhalten. (Ausleihen)"
);
}
const deviceRes = await fetch("http://localhost:8002/apiV2/allItems");
const deviceData = await deviceRes.json();
if (Array.isArray(deviceData)) {
setDevices(deviceData);
} else {
setError(
"error",
"Fehler beim Laden",
"Unerwartetes Datenformat erhalten. (Geräte)"
); );
} }
} catch (e) { } catch (e) {
@@ -71,8 +90,8 @@ const Landingpage: React.FC = () => {
setIsLoading(false); setIsLoading(false);
} }
}; };
fetchLoans(); fetchData();
}, [reload]); }, []);
return ( return (
<> <>
@@ -80,31 +99,6 @@ const Landingpage: React.FC = () => {
Matthias-Claudius-Schule Technik Matthias-Claudius-Schule Technik
</Heading> </Heading>
{/* Action toolbar */}
<HStack
mb={4}
gap={3}
justify="flex-start"
align="center"
flexWrap="wrap"
>
<Tooltip content="Ausleihen neu laden" openDelay={300}>
<IconButton
aria-label="Refresh loans"
size="sm"
variant="outline"
rounded="md"
shadow="sm"
_hover={{ shadow: "md", transform: "translateY(-2px)" }}
_active={{ transform: "translateY(0)" }}
onClick={() => setReload(!reload)}
>
<RefreshCcwDot size={18} />
</IconButton>
</Tooltip>
</HStack>
{/* End action toolbar */}
<Heading as="h2" size="md" mb={4}> <Heading as="h2" size="md" mb={4}>
Alle Ausleihen Alle Ausleihen
</Heading> </Heading>
@@ -176,6 +170,62 @@ const Landingpage: React.FC = () => {
Keine Ausleihen vorhanden. Keine Ausleihen vorhanden.
</Text> </Text>
)} )}
<Heading as="h2" size="md" mb={4}>
Alle Geräte
</Heading>
{/* Responsive Grid mit gleich hohen Karten */}
<SimpleGrid minChildWidth="200px" gap={2} alignItems="stretch">
{devices.map((device) => (
<Card.Root
key={device.id}
size="sm"
bg={device.inSafe ? "green" : "red"}
h="full"
minH="100px"
>
<Card.Header>
{device.inSafe ? <LockOpen size={16} /> : <Lock size={16} />}
<Heading size="md">{device.item_name}</Heading>
</Card.Header>
<Card.Body color="fg.muted">
<Text>Ausleihrolle: {device.can_borrow_role}</Text>
</Card.Body>
</Card.Root>
))}
</SimpleGrid>
<HStack mt={3} gap={3} align="center" role="group" aria-label="Legende">
<Text fontWeight="medium" color="fg.muted">
Legende:
</Text>
<Button
size="sm"
variant="subtle"
colorPalette="green"
pointerEvents="none"
cursor="default"
borderRadius="full"
>
<HStack gap={2}>
<Lock size={16} />
<Text>Im Schließfach</Text>
</HStack>
</Button>
<Button
size="sm"
variant="subtle"
colorPalette="red"
pointerEvents="none"
cursor="default"
borderRadius="full"
>
<HStack gap={2}>
<LockOpen size={16} />
<Text>Nicht im Schließfach</Text>
</HStack>
</Button>
</HStack>
</> </>
); );
}; };

View File

@@ -101,4 +101,14 @@ router.get("/allLoans", async (req, res) => {
return res.status(500).json({ message: "Failed to fetch loans" }); return res.status(500).json({ message: "Failed to fetch loans" });
}); });
// Route for API to get ALL items form the database without key
router.get("/allItems", async (req, res) => {
const result = await getItemsFromDatabaseV2();
if (result.success) {
res.status(200).json(result.data);
} else {
res.status(500).json({ message: "Failed to fetch items" });
}
});
export default router; export default router;