From 1fa7050bbec1ec3b82b580577a976be4cf5ddfa9 Mon Sep 17 00:00:00 2001 From: AI Bot Date: Sat, 4 Jul 2026 17:27:18 +0000 Subject: [PATCH] feat: Besitzer-Zuweisung implementiert --- README.md | 1 + src/add_article.php | 36 +++++++++++++++++++++++++++++------- src/articles.php | 30 ++++++++++++++---------------- src/db_connect.php | 7 +++++++ src/edit_article.php | 36 +++++++++++++++++++++++++++++------- 5 files changed, 80 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index f5dfdd6..0d302ce 100644 --- a/README.md +++ b/README.md @@ -234,4 +234,5 @@ Das Projekt basiert auf bewährten Web-Standards: * **Notizen:** Neue Funktion "Notizen" hinzugefügt inkl. Rich-Text-Editor. * **Haushaltsfreigabe:** Notizen können mit dem Haushalt geteilt werden. * **Tags:** Artikel können nun mit Tags versehen werden, die im modernen Badge-Design angezeigt werden und nach denen in der Übersicht gefiltert werden kann. + * **Besitzer-Zuweisung:** Die Option "Für den gesamten Haushalt freigeben" wurde entfernt, da nun standardmäßig alle Artikel im Haushalt geteilt werden. Stattdessen kann nun jedem Artikel ein expliziter Besitzer (einzelnes Familienmitglied oder "Gemeinsam") zugewiesen werden. Die Artikelliste zeigt den Besitzer übersichtlich an. * **Design-Update:** Die Notizen-Übersichtsseite wurde an das Design der ToDo-Listen angepasst (Split-View, List-Groups, weiße Notizhintergründe). Der Texteditor bietet nun deutsche Tooltips. Beim Speichern einer Notiz wird nun besser zurück in die Übersicht navigiert. Das Layout der Metainformationen sowie der Buttons wurde weiter optimiert. Die Notizen werden in der Liste alphabetisch sortiert. Der Editor wurde aktualisiert: Er besitzt nun eine feste Höhe mit internem Scrollbereich, sodass die Formatierungsleiste immer sichtbar bleibt. Zudem wurde eine Tabellen-Funktion hinzugefügt. diff --git a/src/add_article.php b/src/add_article.php index 3bd9996..7959f33 100644 --- a/src/add_article.php +++ b/src/add_article.php @@ -40,10 +40,18 @@ if ($household_id_for_user) { if (!in_array($row['id'], $household_member_ids)) { $household_member_ids[] = $row['id']; } } $stmt_members->close(); + $stmt_members->close(); } $placeholders = implode(',', array_fill(0, count($household_member_ids), '?')); $types = str_repeat('i', count($household_member_ids)); +// Lade alle Haushaltsmitglieder für das Besitzer-Dropdown +$stmt_all_members = $conn->prepare("SELECT id, username FROM users WHERE id IN ($placeholders) ORDER BY username ASC"); +$stmt_all_members->bind_param($types, ...$household_member_ids); +$stmt_all_members->execute(); +$all_household_members = $stmt_all_members->get_result()->fetch_all(MYSQLI_ASSOC); +$stmt_all_members->close(); + // Lade Kategorien $stmt_cat_load = $conn->prepare("SELECT id, name FROM categories WHERE user_id IN ($placeholders) ORDER BY name ASC"); $stmt_cat_load->bind_param($types, ...$household_member_ids); @@ -130,8 +138,10 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { $name = trim($_POST['name']); $weight_grams = intval($_POST['weight_grams']); $consumable = isset($_POST['consumable']) ? 1 : 0; + $consumable = isset($_POST['consumable']) ? 1 : 0; $quantity_owned = ($consumable == 1) ? 1 : intval($_POST['quantity_owned']); - $is_household_item = isset($_POST['is_household_item']) && $household_id_for_user ? $household_id_for_user : NULL; + $is_household_item = $household_id_for_user ?: NULL; + $owner_id = !empty($_POST['owner_id']) ? intval($_POST['owner_id']) : NULL; $storage_location_id = !empty($_POST['storage_location_id']) ? intval($_POST['storage_location_id']) : NULL; $parent_article_id = !empty($_POST['parent_article_id']) ? intval($_POST['parent_article_id']) : NULL; $image_url_from_input = trim($_POST['image_url']); @@ -193,9 +203,9 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { if (empty($name) || $weight_grams < 0) { $message = ''; } else { - $stmt_insert_article = $conn->prepare("INSERT INTO articles (user_id, household_id, name, weight_grams, quantity_owned, category_id, consumable, image_url, product_url, manufacturer_id, product_designation, storage_location_id, parent_article_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + $stmt_insert_article = $conn->prepare("INSERT INTO articles (user_id, household_id, owner_id, name, weight_grams, quantity_owned, category_id, consumable, image_url, product_url, manufacturer_id, product_designation, storage_location_id, parent_article_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); if ($stmt_insert_article) { - $stmt_insert_article->bind_param("iisiiisssisii", $current_user_id, $is_household_item, $name, $weight_grams, $quantity_owned, $category_id, $consumable, $image_url_for_db, $product_url_for_db, $manufacturer_id, $product_designation_for_db, $storage_location_id, $parent_article_id); + $stmt_insert_article->bind_param("iiisiiisssisii", $current_user_id, $is_household_item, $owner_id, $name, $weight_grams, $quantity_owned, $category_id, $consumable, $image_url_for_db, $product_url_for_db, $manufacturer_id, $product_designation_for_db, $storage_location_id, $parent_article_id); if ($stmt_insert_article->execute()) { $new_article_id = $conn->insert_id; @@ -320,10 +330,22 @@ $conn->close();

-
- -
- +
+
+
+
+ +
+ + +
+ +
diff --git a/src/articles.php b/src/articles.php index 71ba4c6..67c4aa6 100644 --- a/src/articles.php +++ b/src/articles.php @@ -80,14 +80,15 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['delete_article_id'])) $articles = []; $placeholders = implode(',', array_fill(0, count($household_member_ids), '?')); $types = str_repeat('i', count($household_member_ids)); -$sql = "SELECT + $sql = "SELECT a.id, a.name, a.weight_grams, a.quantity_owned, a.product_url, a.consumable, a.image_url, a.user_id, a.parent_article_id, - u.username as creator_name, a.household_id, a.product_designation, + u.username as creator_name, a.household_id, a.product_designation, a.owner_id, o.username as owner_name, c.id AS category_id, c.name AS category_name, c.color AS category_color, m.id AS manufacturer_id, m.name AS manufacturer_name, a.storage_location_id, l2.parent_id AS loc_parent_id, l2.name AS location_level2_name, l1.name AS location_level1_name FROM articles a JOIN users u ON a.user_id = u.id + LEFT JOIN users o ON a.owner_id = o.id LEFT JOIN categories c ON a.category_id = c.id LEFT JOIN manufacturers m ON a.manufacturer_id = m.id LEFT JOIN storage_locations l2 ON a.storage_location_id = l2.id @@ -284,8 +285,7 @@ $conn->close(); Anzahl Kategorie Lagerort - Besitzer - Status + Besitzer Aktionen @@ -623,15 +623,15 @@ document.addEventListener('DOMContentLoaded', function () { function generateCardHTML(article) { const imagePath = article.image_url ? article.image_url : 'assets/images/keinbild.png'; const productLink = article.product_url ? `` : ''; - const householdBadge = article.household_id ? `` : ``; + const ownerBadge = article.owner_id ? ` ${article.owner_name}` : ` Gemeinsam`; const consumableIcon = article.consumable == 1 ? ` ` : ''; const quantityBadge = article.consumable == 1 ? `` : `${article.quantity_owned}x`; let actionButtons = ''; - const isOwner = (article.user_id == currentUserId); + const isOwnerOrCreator = (article.owner_id == currentUserId || article.user_id == currentUserId); const isHouseholdArticle = (article.household_id && article.household_id == currentUserHouseholdId); - if (isOwner || isHouseholdArticle) { + if (isOwnerOrCreator || isHouseholdArticle) { actionButtons = `
`; @@ -654,7 +654,7 @@ document.addEventListener('DOMContentLoaded', function () { return `
- ${householdBadge} ${consumableIcon} + ${ownerBadge} ${consumableIcon}
${article.name} @@ -676,16 +676,15 @@ document.addEventListener('DOMContentLoaded', function () { let html = ''; const imagePath = article.image_url ? article.image_url : 'assets/images/keinbild.png'; const productLink = article.product_url ? `` : ''; - const creatorBadge = article.user_id != currentUserId ? `${article.creator_name}` : `Ich`; - const householdBadge = article.household_id ? `` : ``; + const ownerBadge = article.owner_id ? ` ${article.owner_name}` : ` Gemeinsam`; const consumableIcon = article.consumable == 1 ? ` ` : ''; const quantityBadge = article.consumable == 1 ? `` : `${article.quantity_owned}`; let actionButtons = ''; - const isOwner = (article.user_id == currentUserId); + const isOwnerOrCreator = (article.owner_id == currentUserId || article.user_id == currentUserId); const isHouseholdArticle = (article.household_id && article.household_id == currentUserHouseholdId); - if (isOwner || isHouseholdArticle) { + if (isOwnerOrCreator || isHouseholdArticle) { actionButtons = `
`; actionButtons += `
`; @@ -705,7 +704,7 @@ document.addEventListener('DOMContentLoaded', function () { } const nameCellContent = `
${treePrefix}
${article.name}${tagsHtml}
`; - const checkboxHtml = (isOwner || isHouseholdArticle) ? `` : ''; + const checkboxHtml = (isOwnerOrCreator || isHouseholdArticle) ? `` : ''; html += ` ${checkboxHtml} @@ -715,11 +714,10 @@ document.addEventListener('DOMContentLoaded', function () { ${article.manufacturer_name || '---'} ${article.product_designation || '---'} ${new Intl.NumberFormat('de-DE').format(article.weight_grams)} g - ${quantityBadge} + ${quantityBadge}${consumableIcon} ${article.category_name || '---'} ${article.location_level1_name ? `${article.location_level1_name} ${article.location_level2_name}` : '---'} - ${creatorBadge} - ${householdBadge}${consumableIcon} + ${ownerBadge} ${actionButtons} `; diff --git a/src/db_connect.php b/src/db_connect.php index db45217..6829066 100644 --- a/src/db_connect.php +++ b/src/db_connect.php @@ -83,6 +83,13 @@ $conn->query("CREATE TABLE IF NOT EXISTS notes ( FOREIGN KEY (household_id) REFERENCES households(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci"); +// Ensure owner_id exists in articles +$check_owner_id = $conn->query("SHOW COLUMNS FROM articles LIKE 'owner_id'"); +if ($check_owner_id && $check_owner_id->num_rows == 0) { + $conn->query("ALTER TABLE articles ADD COLUMN owner_id INT DEFAULT NULL"); + $conn->query("ALTER TABLE articles ADD CONSTRAINT fk_article_owner FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE SET NULL"); +} + // Ensure tags table exists $conn->query("CREATE TABLE IF NOT EXISTS tags ( id INT AUTO_INCREMENT PRIMARY KEY, diff --git a/src/edit_article.php b/src/edit_article.php index 75b515b..21611f8 100644 --- a/src/edit_article.php +++ b/src/edit_article.php @@ -130,6 +130,7 @@ if ($can_edit) { $display_image_url_in_input = htmlspecialchars($article_data['image_url']); } } + $owner_id = $article_data['owner_id']; $household_member_ids = [$current_user_id]; if ($household_id_for_user) { @@ -144,6 +145,14 @@ if ($can_edit) { } $placeholders = implode(',', array_fill(0, count($household_member_ids), '?')); $types = str_repeat('i', count($household_member_ids)); + + // Lade alle Haushaltsmitglieder für das Besitzer-Dropdown + $stmt_all_members = $conn->prepare("SELECT id, username FROM users WHERE id IN ($placeholders) ORDER BY username ASC"); + $stmt_all_members->bind_param($types, ...$household_member_ids); + $stmt_all_members->execute(); + $all_household_members = $stmt_all_members->get_result()->fetch_all(MYSQLI_ASSOC); + $stmt_all_members->close(); + $stmt_cat_load = $conn->prepare("SELECT id, name FROM categories WHERE user_id IN ($placeholders) ORDER BY name ASC"); $stmt_cat_load->bind_param($types, ...$household_member_ids); $stmt_cat_load->execute(); @@ -201,7 +210,8 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" && $can_edit) { $product_designation_for_db = !empty(trim($_POST['product_designation'])) ? trim($_POST['product_designation']) : NULL; $pasted_image_data = $_POST['pasted_image_data'] ?? ''; $image_url_from_input = trim($_POST['image_url']); - $is_household_item = isset($_POST['is_household_item']) && $household_id_for_user ? $household_id_for_user : NULL; + $is_household_item = $household_id_for_user ?: NULL; + $owner_id = !empty($_POST['owner_id']) ? intval($_POST['owner_id']) : NULL; $storage_location_id = !empty($_POST['storage_location_id']) ? intval($_POST['storage_location_id']) : NULL; $parent_article_id = !empty($_POST['parent_article_id']) ? intval($_POST['parent_article_id']) : NULL; @@ -260,9 +270,9 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" && $can_edit) { if (empty($name) || $weight_grams < 0) { $message .= ''; } else { - $stmt_update = $conn->prepare("UPDATE articles SET name = ?, weight_grams = ?, quantity_owned = ?, category_id = ?, consumable = ?, image_url = ?, product_url = ?, manufacturer_id = ?, product_designation = ?, household_id = ?, storage_location_id = ?, parent_article_id = ? WHERE id = ?"); + $stmt_update = $conn->prepare("UPDATE articles SET name = ?, weight_grams = ?, quantity_owned = ?, category_id = ?, consumable = ?, image_url = ?, product_url = ?, manufacturer_id = ?, product_designation = ?, household_id = ?, owner_id = ?, storage_location_id = ?, parent_article_id = ? WHERE id = ?"); if ($stmt_update) { - $stmt_update->bind_param("siiisssisiiii", $name, $weight_grams, $quantity_owned, $category_id, $consumable, $image_url_for_db, $product_url_for_db, $manufacturer_id, $product_designation_for_db, $is_household_item, $storage_location_id, $parent_article_id, $article_id); + $stmt_update->bind_param("siiisssisiiiii", $name, $weight_grams, $quantity_owned, $category_id, $consumable, $image_url_for_db, $product_url_for_db, $manufacturer_id, $product_designation_for_db, $is_household_item, $owner_id, $storage_location_id, $parent_article_id, $article_id); if ($stmt_update->execute()) { // Alte Tags löschen $stmt_del_tags = $conn->prepare("DELETE FROM article_tags WHERE article_id = ?"); @@ -390,10 +400,22 @@ $conn->close();

-
>
- -
>
- +
+
+
>
+
+ +
+ + +
+ +