Enhance item management: update API key display, add locker number input, and modify database schema for unique locker numbers
This commit is contained in:
@@ -124,8 +124,8 @@ const APIKeyTable: React.FC = () => {
|
|||||||
</HStack>
|
</HStack>
|
||||||
{/* End action toolbar */}
|
{/* End action toolbar */}
|
||||||
|
|
||||||
<Heading marginBottom={4} size="md">
|
<Heading marginBottom={4} size="2xl">
|
||||||
Gegenstände
|
API Keys
|
||||||
</Heading>
|
</Heading>
|
||||||
{isError && (
|
{isError && (
|
||||||
<MyAlert
|
<MyAlert
|
||||||
|
|||||||
@@ -25,15 +25,19 @@ const AddItemForm: React.FC<AddItemFormProps> = ({ onClose, alert }) => {
|
|||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Stack gap="4" w="full">
|
<Stack gap="4" w="full">
|
||||||
<Field.Root>
|
<Field.Root>
|
||||||
<Field.Label>Gegenstandsname</Field.Label>
|
<Field.Label>Gegenstandsname (muss einzigartig sein)</Field.Label>
|
||||||
<Input id="item_name" placeholder="z.B. Laptop" />
|
<Input id="item_name" placeholder="z.B. Laptop" />
|
||||||
</Field.Root>
|
</Field.Root>
|
||||||
|
<Field.Root>
|
||||||
|
<Field.Label>Schließfachnummer (immer zwei Zahlen)</Field.Label>
|
||||||
|
<Input id="lockerNumber" placeholder="Nummer 01 - 06" />
|
||||||
|
</Field.Root>
|
||||||
<Field.Root>
|
<Field.Root>
|
||||||
<Field.Label>Ausleih-Berechtigung (Rolle)</Field.Label>
|
<Field.Label>Ausleih-Berechtigung (Rolle)</Field.Label>
|
||||||
<Input
|
<Input
|
||||||
id="can_borrow_role"
|
id="can_borrow_role"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="Zahl (1 - 4)"
|
placeholder="Zahl (1 - 6)"
|
||||||
/>
|
/>
|
||||||
</Field.Root>
|
</Field.Root>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -53,10 +57,17 @@ const AddItemForm: React.FC<AddItemFormProps> = ({ onClose, alert }) => {
|
|||||||
(document.getElementById("can_borrow_role") as HTMLInputElement)
|
(document.getElementById("can_borrow_role") as HTMLInputElement)
|
||||||
?.value
|
?.value
|
||||||
);
|
);
|
||||||
|
const lockerValue = (
|
||||||
|
document.getElementById("lockerNumber") as HTMLInputElement
|
||||||
|
)?.value.trim();
|
||||||
|
|
||||||
|
const lockerNumber =
|
||||||
|
lockerValue === "" ? null : Number(lockerValue);
|
||||||
|
|
||||||
if (!name || Number.isNaN(role)) return;
|
if (!name || Number.isNaN(role)) return;
|
||||||
|
if (lockerNumber !== null && Number.isNaN(lockerNumber)) return;
|
||||||
|
|
||||||
const res = await createItem(name, role);
|
const res = await createItem(name, role, lockerNumber);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
alert(
|
alert(
|
||||||
"success",
|
"success",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ type Items = {
|
|||||||
item_name: string;
|
item_name: string;
|
||||||
can_borrow_role: string;
|
can_borrow_role: string;
|
||||||
in_safe: boolean;
|
in_safe: boolean;
|
||||||
|
safe_nr: string;
|
||||||
entry_created_at: string;
|
entry_created_at: string;
|
||||||
entry_updated_at: string;
|
entry_updated_at: string;
|
||||||
last_borrowed_person: string | null;
|
last_borrowed_person: string | null;
|
||||||
@@ -66,6 +67,12 @@ const ItemTable: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLockerNumberChange = (id: number, value: string) => {
|
||||||
|
setItems((prev) =>
|
||||||
|
prev.map((it) => (it.id === id ? { ...it, lockerNumber: value } : it))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const setError = (
|
const setError = (
|
||||||
status: "error" | "success",
|
status: "error" | "success",
|
||||||
message: string,
|
message: string,
|
||||||
@@ -201,6 +208,9 @@ const ItemTable: React.FC = () => {
|
|||||||
<Table.ColumnHeader>
|
<Table.ColumnHeader>
|
||||||
<strong>Im Schließfach</strong>
|
<strong>Im Schließfach</strong>
|
||||||
</Table.ColumnHeader>
|
</Table.ColumnHeader>
|
||||||
|
<Table.ColumnHeader>
|
||||||
|
<strong>Schließfachnummer</strong>
|
||||||
|
</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader>
|
<Table.ColumnHeader>
|
||||||
<strong>Eintrag erstellt am</strong>
|
<strong>Eintrag erstellt am</strong>
|
||||||
</Table.ColumnHeader>
|
</Table.ColumnHeader>
|
||||||
@@ -208,10 +218,10 @@ const ItemTable: React.FC = () => {
|
|||||||
<strong>Eintrag aktualisiert am</strong>
|
<strong>Eintrag aktualisiert am</strong>
|
||||||
</Table.ColumnHeader>
|
</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader>
|
<Table.ColumnHeader>
|
||||||
<strong>Letzte ausleihende Person</strong>
|
<strong>LaP *</strong>
|
||||||
</Table.ColumnHeader>
|
</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader>
|
<Table.ColumnHeader>
|
||||||
<strong>Derzeit ausgeliehen von</strong>
|
<strong>Dav **</strong>
|
||||||
</Table.ColumnHeader>
|
</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader>
|
<Table.ColumnHeader>
|
||||||
<strong>Aktionen</strong>
|
<strong>Aktionen</strong>
|
||||||
@@ -277,6 +287,16 @@ const ItemTable: React.FC = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
<Input
|
||||||
|
size="sm"
|
||||||
|
w="max-content"
|
||||||
|
onChange={(e) =>
|
||||||
|
handleLockerNumberChange(item.id, e.target.value)
|
||||||
|
}
|
||||||
|
value={item.safe_nr}
|
||||||
|
/>
|
||||||
|
</Table.Cell>
|
||||||
<Table.Cell>{formatDateTime(item.entry_created_at)}</Table.Cell>
|
<Table.Cell>{formatDateTime(item.entry_created_at)}</Table.Cell>
|
||||||
<Table.Cell>{formatDateTime(item.entry_updated_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>
|
||||||
@@ -287,6 +307,7 @@ const ItemTable: React.FC = () => {
|
|||||||
handleEditItems(
|
handleEditItems(
|
||||||
item.id,
|
item.id,
|
||||||
item.item_name,
|
item.item_name,
|
||||||
|
item.safe_nr,
|
||||||
item.can_borrow_role
|
item.can_borrow_role
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
@@ -333,6 +354,8 @@ const ItemTable: React.FC = () => {
|
|||||||
</Table.Body>
|
</Table.Body>
|
||||||
</Table.Root>
|
</Table.Root>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Text>* LaP = Letzte ausleihende Person</Text>
|
||||||
|
<Text>** Dav = Derzeit ausgeliehen von</Text>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -164,8 +164,10 @@ export const deleteItem = async (itemId: number) => {
|
|||||||
|
|
||||||
export const createItem = async (
|
export const createItem = async (
|
||||||
item_name: string,
|
item_name: string,
|
||||||
can_borrow_role: number
|
can_borrow_role: number,
|
||||||
|
lockerNumber: number | null
|
||||||
) => {
|
) => {
|
||||||
|
console.log(JSON.stringify({ item_name, can_borrow_role, lockerNumber }));
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${API_BASE}/api/admin/item-data/create-item`,
|
`${API_BASE}/api/admin/item-data/create-item`,
|
||||||
@@ -175,7 +177,7 @@ export const createItem = async (
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ item_name, can_borrow_role }),
|
body: JSON.stringify({ item_name, can_borrow_role, lockerNumber }),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -195,8 +197,10 @@ export const createItem = async (
|
|||||||
export const handleEditItems = async (
|
export const handleEditItems = async (
|
||||||
itemId: number,
|
itemId: number,
|
||||||
item_name: string,
|
item_name: string,
|
||||||
|
safe_nr: string | null,
|
||||||
can_borrow_role: string
|
can_borrow_role: string
|
||||||
) => {
|
) => {
|
||||||
|
const newSafeNr = Number(safe_nr || 0);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${API_BASE}/api/admin/item-data/edit-item/${itemId}`,
|
`${API_BASE}/api/admin/item-data/edit-item/${itemId}`,
|
||||||
@@ -206,7 +210,7 @@ export const handleEditItems = async (
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ item_name, can_borrow_role }),
|
body: JSON.stringify({ item_name, newSafeNr, can_borrow_role }),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ export const deleteItemById = async (itemId) => {
|
|||||||
return { success: false };
|
return { success: false };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createItem = async (item_name, can_borrow_role, in_safe) => {
|
export const createItem = async (item_name, can_borrow_role, lockerNumber) => {
|
||||||
const [result] = await pool.query(
|
const [result] = await pool.query(
|
||||||
"INSERT INTO items (item_name, can_borrow_role, in_safe) VALUES (?, ?, ?)",
|
"INSERT INTO items (item_name, can_borrow_role, in_safe, safe_nr) VALUES (?, ?, ?, ?)",
|
||||||
[item_name, can_borrow_role, true]
|
[item_name, can_borrow_role, true, lockerNumber]
|
||||||
);
|
);
|
||||||
if (result.affectedRows > 0) return { success: true };
|
if (result.affectedRows > 0) return { success: true };
|
||||||
return { success: false };
|
return { success: false };
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ router.delete("/delete-item/:id", authenticateAdmin, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.post("/create-item", authenticateAdmin, async (req, res) => {
|
router.post("/create-item", authenticateAdmin, async (req, res) => {
|
||||||
const { item_name, can_borrow_role } = req.body;
|
const { item_name, can_borrow_role, lockerNumber } = req.body;
|
||||||
const result = await createItem(item_name, can_borrow_role);
|
const result = await createItem(item_name, can_borrow_role, lockerNumber);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
return res.status(201).json({ message: "Item created successfully" });
|
return res.status(201).json({ message: "Item created successfully" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ CREATE TABLE items (
|
|||||||
item_name varchar(255) NOT NULL UNIQUE,
|
item_name varchar(255) NOT NULL UNIQUE,
|
||||||
can_borrow_role INT NOT NULL,
|
can_borrow_role INT NOT NULL,
|
||||||
in_safe bool NOT NULL DEFAULT true,
|
in_safe bool NOT NULL DEFAULT true,
|
||||||
safe_nr CHAR(2) DEFAULT NULL,
|
safe_nr CHAR(2) DEFAULT NULL UNIQUE,
|
||||||
entry_created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
|
entry_created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
entry_updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
entry_updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
last_borrowed_person varchar(255) DEFAULT NULL,
|
last_borrowed_person varchar(255) DEFAULT NULL,
|
||||||
|
|||||||
Reference in New Issue
Block a user