From ce07575d5ed8122ce0dd938039e8493f3778cc19 Mon Sep 17 00:00:00 2001 From: Jurgen Rentinck Date: Sat, 29 Aug 2026 19:53:28 +0200 Subject: [PATCH] Favorieten toegevoegd aan de navigatiebalk en sidebar. --- controllers/FavoriteController.js | 51 +++++++++++++++++++++++ controllers/ProductController.js | 11 +++-- controllers/ShoppingListController.js | 35 ++++++++++++++++ database/migrate.js | 35 ++++++++++++++-- middleware/auth.js | 11 ++--- middleware/user.js | 20 +++++---- repositories/FavoriteRepository.js | 55 +++++++++++++++++++++++++ repositories/ShoppingListRepository.js | 31 ++++++++++++++ routes/web.js | 32 ++++++++++++++ services/FavoriteService.js | 35 ++++++++++++++++ tmp-favorite-route.sqlite | Bin 0 -> 69632 bytes tmp-favorites-check.sqlite | Bin 0 -> 69632 bytes tmp-household-favorites.sqlite | Bin 0 -> 69632 bytes views/favorites/index.ejs | 26 ++++++++++++ views/lists/show.ejs | 18 ++++++-- views/partials/navbar.ejs | 1 + views/partials/sidebar.ejs | 6 +++ views/products/edit.ejs | 2 +- views/products/index.ejs | 15 +++++-- 19 files changed, 355 insertions(+), 29 deletions(-) create mode 100644 controllers/FavoriteController.js create mode 100644 repositories/FavoriteRepository.js create mode 100644 services/FavoriteService.js create mode 100644 tmp-favorite-route.sqlite create mode 100644 tmp-favorites-check.sqlite create mode 100644 tmp-household-favorites.sqlite create mode 100644 views/favorites/index.ejs diff --git a/controllers/FavoriteController.js b/controllers/FavoriteController.js new file mode 100644 index 0000000..3c5dcd6 --- /dev/null +++ b/controllers/FavoriteController.js @@ -0,0 +1,51 @@ +const FavoriteService = require("../services/FavoriteService"); + +class FavoriteController { + index(req, res) { + const favorites = FavoriteService.getFavorites(req.user.id, req.user.household_id); + + res.locals.title = "Favorieten"; + res.render("favorites/index", { + favorites, + user: req.user + }); + } + + toggle(req, res) { + const productId = Number.parseInt(req.params.id, 10); + + if (!Number.isInteger(productId)) { + req.flash("error", "Product niet gevonden."); + return res.redirect("/products"); + } + + const isFavorite = FavoriteService.toggleFavorite(req.user.id, productId, req.user.household_id); + if (!isFavorite && !FavoriteService.getFavoriteIds(req.user.id, req.user.household_id).has(productId)) { + req.flash("error", "Product niet gevonden."); + return res.redirect("/products"); + } + + req.flash("success", isFavorite ? "Product toegevoegd aan favorieten." : "Product verwijderd uit favorieten."); + res.redirect("/products"); + } + + remove(req, res) { + const productId = Number.parseInt(req.params.productId, 10); + + if (!Number.isInteger(productId)) { + req.flash("error", "Product niet gevonden."); + return res.redirect("/favorites"); + } + + const removed = FavoriteService.removeFavorite(req.user.id, productId, req.user.household_id); + if (!removed) { + req.flash("error", "Product niet gevonden in favorieten."); + return res.redirect("/favorites"); + } + + req.flash("success", "Product verwijderd uit favorieten."); + res.redirect("/favorites"); + } +} + +module.exports = new FavoriteController(); diff --git a/controllers/ProductController.js b/controllers/ProductController.js index cab9126..5bcdd5c 100644 --- a/controllers/ProductController.js +++ b/controllers/ProductController.js @@ -2,6 +2,7 @@ const ProductService = require("../services/ProductService"); const CategoryService = require("../services/CategoryService"); +const FavoriteService = require("../services/FavoriteService"); const parseCsv = require("../utils/parseCsv"); @@ -10,16 +11,18 @@ class ProductController { index(req, res) { - const products = - ProductService.getProducts(req.user.household_id); - + const products = ProductService.getProducts(req.user.household_id); + const favoriteIds = FavoriteService.getFavoriteIds(req.user.id, req.user.household_id); const categories = CategoryService.getCategories(req.user.household_id); res.render( "products/index", { title:"Producten", - products, + products: products.map(product => ({ + ...product, + is_favorite: favoriteIds.has(product.id) + })), categories } diff --git a/controllers/ShoppingListController.js b/controllers/ShoppingListController.js index ee82fa3..3ca6b40 100644 --- a/controllers/ShoppingListController.js +++ b/controllers/ShoppingListController.js @@ -1,5 +1,6 @@ const ProductRepository = require("../repositories/ProductRepository"); const ShoppingListRepository = require("../repositories/ShoppingListRepository"); +const FavoriteService = require("../services/FavoriteService"); class ShoppingListController { index(req, res) { @@ -63,6 +64,40 @@ class ShoppingListController { res.redirect(`/lists/${list.id}`); } + clearItems(req, res) { + const list = ShoppingListRepository.findById(req.params.id, req.user.household_id); + if (!list) return res.status(404).render("errors/404"); + + ShoppingListRepository.clearItems(list.id); + this.broadcastUpdate(req, list.id); + req.flash("success", "Alle producten zijn verwijderd uit de lijst."); + res.redirect(`/lists/${list.id}`); + } + + addFavoriteProducts(req, res) { + const list = ShoppingListRepository.findById(req.params.id, req.user.household_id); + if (!list) return res.status(404).render("errors/404"); + + const favorites = FavoriteService.getFavorites(req.user.id, req.user.household_id); + const items = favorites.map((favorite) => ({ + list_id: list.id, + product_id: favorite.id, + amount: 1, + checked: 0, + sort_order: 0 + })); + + if (items.length === 0) { + req.flash("info", "Je hebt nog geen favorieten om toe te voegen."); + return res.redirect(`/lists/${list.id}`); + } + + ShoppingListRepository.addItems(list.id, items); + this.broadcastUpdate(req, list.id); + req.flash("success", `${items.length} favoriete producten toegevoegd aan de lijst.`); + res.redirect(`/lists/${list.id}`); + } + deleteItem(req, res) { const list = ShoppingListRepository.findById(req.params.id, req.user.household_id); if (!list) return res.status(404).json({ error: "Lijst niet gevonden" }); diff --git a/database/migrate.js b/database/migrate.js index 239ab67..e5c18f0 100644 --- a/database/migrate.js +++ b/database/migrate.js @@ -163,18 +163,18 @@ CREATE TABLE IF NOT EXISTS product_favorites ( id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER NOT NULL, + household_id INTEGER NOT NULL, product_id INTEGER NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - UNIQUE(user_id, product_id), + UNIQUE(household_id, product_id), - FOREIGN KEY(user_id) - REFERENCES users(id) + FOREIGN KEY(household_id) + REFERENCES households(id) ON DELETE CASCADE, @@ -217,8 +217,35 @@ const addColumnIfMissing = (table, column, definition) => { } }; +const migrateFavoriteTableToHouseholdScope = () => { + const columns = db.prepare(`PRAGMA table_info(product_favorites)`).all(); + const hasHouseholdId = columns.some(column => column.name === "household_id"); + + if (!hasHouseholdId) { + db.exec(`ALTER TABLE product_favorites ADD COLUMN household_id INTEGER`); + db.exec(`UPDATE product_favorites + SET household_id = ( + SELECT products.household_id + FROM products + WHERE products.id = product_favorites.product_id + ) + WHERE household_id IS NULL`); + + db.exec(`DELETE FROM product_favorites + WHERE id NOT IN ( + SELECT MIN(id) + FROM product_favorites + GROUP BY household_id, product_id + )`); + + db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_product_favorites_household_product + ON product_favorites (household_id, product_id)`); + } +}; + addColumnIfMissing("categories", "household_id", "INTEGER REFERENCES households(id)"); addColumnIfMissing("products", "household_id", "INTEGER REFERENCES households(id)"); +migrateFavoriteTableToHouseholdScope(); const defaultHousehold = db.prepare("SELECT id FROM households ORDER BY id LIMIT 1").get(); if (defaultHousehold) { diff --git a/middleware/auth.js b/middleware/auth.js index 2b957d4..adea771 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -1,17 +1,18 @@ module.exports = function(req, res, next) { - - if (!req.session.userId) { - + if (!req.session || !req.session.userId || !req.user) { if (req.originalUrl.startsWith("/api/")) { return res.status(401).json({ error: "Je bent niet ingelogd" }); } + if (req.session && req.session.userId) { + delete req.session.userId; + } + + req.flash("info", "Je sessie is verlopen. Log opnieuw in."); return res.redirect("/login"); - } - next(); }; diff --git a/middleware/user.js b/middleware/user.js index a05f64c..9af10b0 100644 --- a/middleware/user.js +++ b/middleware/user.js @@ -4,17 +4,21 @@ require("../repositories/UserRepository"); module.exports = function userMiddleware(req, res, next) { + req.user = null; - if (req.session.userId) { - - req.user = - UserRepository.findById( - req.session.userId - ); - + if (!req.session || !req.session.userId) { + return next(); } + req.user = UserRepository.findById(req.session.userId); - next(); + if (!req.user) { + delete req.session.userId; + if (typeof req.session.destroy === "function") { + return req.session.destroy(() => next()); + } + } + + return next(); }; \ No newline at end of file diff --git a/repositories/FavoriteRepository.js b/repositories/FavoriteRepository.js new file mode 100644 index 0000000..103b965 --- /dev/null +++ b/repositories/FavoriteRepository.js @@ -0,0 +1,55 @@ +const db = require("../config/database"); + +class FavoriteRepository { + getByHousehold(householdId) { + return db.prepare(` + SELECT products.id, + products.name, + products.category_id, + products.barcode, + categories.name AS category_name, + categories.icon AS category_icon + FROM product_favorites + JOIN products ON products.id = product_favorites.product_id + LEFT JOIN categories ON categories.id = products.category_id + WHERE product_favorites.household_id = ? + AND products.household_id = ? + ORDER BY products.name ASC + `).all(householdId, householdId); + } + + getIdsByHousehold(householdId) { + return new Set( + db.prepare(` + SELECT product_favorites.product_id + FROM product_favorites + JOIN products ON products.id = product_favorites.product_id + WHERE product_favorites.household_id = ? + AND products.household_id = ? + `).all(householdId, householdId).map(row => row.product_id) + ); + } + + find(householdId, productId) { + return db.prepare(` + SELECT * FROM product_favorites + WHERE household_id = ? AND product_id = ? + `).get(householdId, productId); + } + + add(householdId, productId) { + return db.prepare(` + INSERT OR IGNORE INTO product_favorites (household_id, product_id) + VALUES (?, ?) + `).run(householdId, productId); + } + + remove(householdId, productId) { + return db.prepare(` + DELETE FROM product_favorites + WHERE household_id = ? AND product_id = ? + `).run(householdId, productId); + } +} + +module.exports = new FavoriteRepository(); diff --git a/repositories/ShoppingListRepository.js b/repositories/ShoppingListRepository.js index 06426ff..c15088f 100644 --- a/repositories/ShoppingListRepository.js +++ b/repositories/ShoppingListRepository.js @@ -72,6 +72,37 @@ class ShoppingListRepository { `).run(listId); } + clearItems(listId) { + return db.prepare(` + DELETE FROM shopping_list_items + WHERE list_id = ? + `).run(listId); + } + + addItems(listId, items) { + if (!items.length) return 0; + + const insert = db.prepare(` + INSERT INTO shopping_list_items (list_id, product_id, amount, checked, sort_order) + VALUES (?, ?, ?, ?, ?) + `); + + const transaction = db.transaction(() => { + for (const item of items) { + insert.run( + listId, + item.product_id, + item.amount ?? 1, + item.checked ?? 0, + item.sort_order ?? 0 + ); + } + }); + + transaction(); + return items.length; + } + deleteItem(listId, itemId) { return db.prepare(` DELETE FROM shopping_list_items WHERE id = ? AND list_id = ? diff --git a/routes/web.js b/routes/web.js index dfd0b9a..aec90df 100644 --- a/routes/web.js +++ b/routes/web.js @@ -34,6 +34,8 @@ require("../controllers/CategoryController"); const ShoppingListController = require("../controllers/ShoppingListController"); +const FavoriteController = require("../controllers/FavoriteController"); + const MenuController = require("../controllers/MenuController"); @@ -119,6 +121,24 @@ router.post( ProductController.remove.bind(ProductController) ); +router.post( + "/products/:id/favorite", + auth, + FavoriteController.toggle.bind(FavoriteController) +); + +router.get( + "/favorites", + auth, + FavoriteController.index.bind(FavoriteController) +); + +router.post( + "/favorites/:productId/delete", + auth, + FavoriteController.remove.bind(FavoriteController) +); + router.get( "/lists", auth, @@ -149,6 +169,18 @@ router.post( ShoppingListController.create.bind(ShoppingListController) ); +router.post( + "/lists/:id/favorites/add", + auth, + ShoppingListController.addFavoriteProducts.bind(ShoppingListController) +); + +router.post( + "/lists/:id/items/clear", + auth, + ShoppingListController.clearItems.bind(ShoppingListController) +); + router.get( "/lists/:id", auth, diff --git a/services/FavoriteService.js b/services/FavoriteService.js new file mode 100644 index 0000000..4c5ce8e --- /dev/null +++ b/services/FavoriteService.js @@ -0,0 +1,35 @@ +const FavoriteRepository = require("../repositories/FavoriteRepository"); +const ProductRepository = require("../repositories/ProductRepository"); + +class FavoriteService { + getFavorites(userId, householdId) { + return FavoriteRepository.getByHousehold(householdId); + } + + getFavoriteIds(userId, householdId) { + return FavoriteRepository.getIdsByHousehold(householdId); + } + + toggleFavorite(userId, productId, householdId) { + const product = ProductRepository.findById(productId, householdId); + if (!product) return false; + + const current = FavoriteRepository.find(householdId, productId); + if (current) { + FavoriteRepository.remove(householdId, productId); + return false; + } + + FavoriteRepository.add(householdId, productId); + return true; + } + + removeFavorite(userId, productId, householdId) { + const product = ProductRepository.findById(productId, householdId); + if (!product) return false; + + return FavoriteRepository.remove(householdId, productId).changes > 0; + } +} + +module.exports = new FavoriteService(); diff --git a/tmp-favorite-route.sqlite b/tmp-favorite-route.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..67e1efccee207b6ef045769cc680b0ffac5e41b8 GIT binary patch literal 69632 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 literal 0 HcmV?d00001 diff --git a/views/favorites/index.ejs b/views/favorites/index.ejs new file mode 100644 index 0000000..e81ee47 --- /dev/null +++ b/views/favorites/index.ejs @@ -0,0 +1,26 @@ + + +
+
+ <% if (favorites.length === 0) { %> +

Je hebt nog geen favoriete producten.

+ <% } else { %> +
+ <% favorites.forEach(product => { %> +
+
+ <%= product.category_icon || "" %> <%= product.name %> +
<%= product.category_name || "Geen categorie" %>
+
+ +
+ +
+
+ <% }); %> +
+ <% } %> +
+
diff --git a/views/lists/show.ejs b/views/lists/show.ejs index cbfba20..2fa2169 100644 --- a/views/lists/show.ejs +++ b/views/lists/show.ejs @@ -2,10 +2,20 @@ ← Terug naar lijsten

<%= list.name %>

-
- -
+
+
+ +
+
+ +
+
+ +
+
diff --git a/views/partials/navbar.ejs b/views/partials/navbar.ejs index 2c2b7d9..0430e79 100644 --- a/views/partials/navbar.ejs +++ b/views/partials/navbar.ejs @@ -27,6 +27,7 @@ Dashboard Lijsten Producten + Favorieten Categorieën Huishouden Uitloggen diff --git a/views/partials/sidebar.ejs b/views/partials/sidebar.ejs index 591e6a7..4ac69fc 100644 --- a/views/partials/sidebar.ejs +++ b/views/partials/sidebar.ejs @@ -26,6 +26,12 @@ + + +💙 Favorieten + + + diff --git a/views/products/edit.ejs b/views/products/edit.ejs index 1998ccc..918a389 100644 --- a/views/products/edit.ejs +++ b/views/products/edit.ejs @@ -32,7 +32,7 @@
- +
Annuleren diff --git a/views/products/index.ejs b/views/products/index.ejs index 7edd87a..ddb6a35 100644 --- a/views/products/index.ejs +++ b/views/products/index.ejs @@ -23,9 +23,18 @@ <%= product.category_icon || "" %> <%= product.name %>
<%= product.category_name || "Geen categorie" %>
- - Wijzigen - +
+
+ +
+ + Wijzigen + +
<% }); %>