diff --git a/backend/controllers/lists.controller.js b/backend/controllers/lists.controller.js index bf772c3..348f0f8 100644 --- a/backend/controllers/lists.controller.js +++ b/backend/controllers/lists.controller.js @@ -6,6 +6,12 @@ exports.getList = async (req, res) => { res.json(items); }; +exports.getItemByName = async (req, res) => { + const { itemName } = req.query; + const item = await List.getItemByName(itemName); + res.json(item); +} + exports.addItem = async (req, res) => { const { itemName, quantity } = req.body; diff --git a/backend/controllers/users.controller.js b/backend/controllers/users.controller.js index 8af2598..ef9bc1d 100644 --- a/backend/controllers/users.controller.js +++ b/backend/controllers/users.controller.js @@ -14,10 +14,10 @@ exports.getAllUsers = async (req, res) => { exports.updateUserRole = async (req, res) => { try { - const { id } = req.params; - const { role } = req.body; + const { id, role } = req.body; - if (!Object.values(ROLES).includes(role)) + console.log(`Updating user ${id} to role ${role}`); + if (!Object.values(User.ROLES).includes(role)) return res.status(400).json({ error: "Invalid role" }); const updated = await User.updateUserRole(id, role); diff --git a/backend/models/list.model.js b/backend/models/list.model.js index 072cc9d..565d73b 100644 --- a/backend/models/list.model.js +++ b/backend/models/list.model.js @@ -8,23 +8,37 @@ exports.getUnboughtItems = async () => { return result.rows; }; - -exports.addOrUpdateItem = async (item_name, quantity) => { +exports.getItemByName = async (itemName) => { const result = await pool.query( - "SELECT id, bought FROM grocery_list WHERE item_name = $1", - [item_name] + "SELECT * FROM grocery_list WHERE item_name ILIKE $1", + [itemName] + ); + + return result.rows[0] || null; +}; + + +exports.addOrUpdateItem = async (itemName, quantity) => { + const result = await pool.query( + "SELECT id, bought FROM grocery_list WHERE item_name ILIKE $1", + [itemName] ); if (result.rowCount > 0) { await pool.query( - "UPDATE grocery_list SET quantity = $1, bought = FALSE WHERE id = $2", + `UPDATE grocery_list + SET quantity = $1, + bought = FALSE + WHERE id = $2`, [quantity, result.rows[0].id] ); return result.rows[0].id; } else { const insert = await pool.query( - "INSERT INTO grocery_list (item_name, quantity) VALUES ($1, $2) RETURNING id", - [item_name, quantity] + `INSERT INTO grocery_list + (item_name, quantity) + VALUES ($1, $2) RETURNING id`, + [itemName, quantity] ); return insert.rows[0].id; } diff --git a/backend/routes/list.routes.js b/backend/routes/list.routes.js index bf881ec..e73b2b8 100644 --- a/backend/routes/list.routes.js +++ b/backend/routes/list.routes.js @@ -7,8 +7,9 @@ const User = require("../models/user.model"); -router.get("/", auth, requireRole(ROLES.VIEWER, ROLES.EDITOR, ROLES.ADMIN), controller.getList); -router.get("/suggest", auth, requireRole(ROLES.VIEWER, ROLES.EDITOR, ROLES.ADMIN), controller.getSuggestions); +router.get("/", auth, requireRole(...Object.values(ROLES)), controller.getList); +router.get("/item-by-name", auth, requireRole(...Object.values(ROLES)), controller.getItemByName); +router.get("/suggest", auth, requireRole(...Object.values(ROLES)), controller.getSuggestions); router.post("/add", auth, requireRole(ROLES.EDITOR, ROLES.ADMIN), controller.addItem); diff --git a/frontend/src/api/list.js b/frontend/src/api/list.js index d84a94e..9f01cd6 100644 --- a/frontend/src/api/list.js +++ b/frontend/src/api/list.js @@ -1,6 +1,7 @@ import api from "./axios"; export const getList = () => api.get("/list"); -export const addItem = (itemName, quantiy) => api.post("/list/add", { itemName, quantiy }); +export const getItemByName = (itemName) => api.get("/list/item-by-name", { params: { itemName: itemName } }); +export const addItem = (itemName, quantity) => api.post("/list/add", { itemName, quantity }); export const markBought = (id) => api.post("/list/mark-bought", { id }); export const getSuggestions = (query) => api.get("/list/suggest", { params: { query: query } }); \ No newline at end of file diff --git a/frontend/src/api/users.js b/frontend/src/api/users.js index 89c8d58..bbadb6c 100644 --- a/frontend/src/api/users.js +++ b/frontend/src/api/users.js @@ -1,6 +1,6 @@ import api from "./axios"; export const getAllUsers = () => api.get("/admin/users"); -export const updateRole = (id, role) => api.put(`/admin/users/${id}/role`, { role }); +export const updateRole = (id, role) => api.put(`/admin/users`, { id, role }); export const deleteUser = (id) => api.delete(`/admin/users/${id}`); export const checkIfUserExists = (username) => api.get(`/users/exists`, { params: { username: username } }); \ No newline at end of file diff --git a/frontend/src/pages/AdminPanel.jsx b/frontend/src/pages/AdminPanel.jsx index 31ef718..de7aff0 100644 --- a/frontend/src/pages/AdminPanel.jsx +++ b/frontend/src/pages/AdminPanel.jsx @@ -17,7 +17,8 @@ export default function AdminPanel() { const changeRole = async (id, role) => { const updated = await updateRole(id, role); - setUsers(users.map(u => (u.id === id ? updated.data : u))) + if (updated.status !== 200) return; + loadUsers(); } return ( diff --git a/frontend/src/pages/GroceryList.jsx b/frontend/src/pages/GroceryList.jsx index 1c00749..0de1353 100644 --- a/frontend/src/pages/GroceryList.jsx +++ b/frontend/src/pages/GroceryList.jsx @@ -1,5 +1,5 @@ import { useContext, useEffect, useState } from "react"; -import { addItem, getList, getSuggestions, markBought } from "../api/list"; +import { addItem, getItemByName, getList, getSuggestions, markBought } from "../api/list"; import { ROLES } from "../constants/roles"; import { AuthContext } from "../context/AuthContext"; import "../styles/GroceryList.css"; @@ -12,6 +12,7 @@ export default function GroceryList() { const [sortMode, setSortMode] = useState("az"); + const [showSuggestions, setShowSuggestions] = useState(false); const [itemName, setItemName] = useState(""); const [quantity, setQuantity] = useState(1); const [suggestions, setSuggestions] = useState([]); @@ -57,10 +58,8 @@ export default function GroceryList() { } try { - console.log("Getting suggestions for:", text); let suggestions = await getSuggestions(text); suggestions = suggestions.data.map(s => s.item_name); - console.log(`Suggestions: ${suggestions}`); setSuggestions(suggestions); } catch { setSuggestions([]); @@ -70,8 +69,20 @@ export default function GroceryList() { const handleAdd = async (e) => { e.preventDefault(); if (!itemName.trim()) return; + let newQuantity = quantity; - await addItem(itemName, quantity); + const item = await getItemByName(itemName); + if (item) { + let currentQuantity = item.data.quantity; + const yes = window.confirm( + `Item "${itemName}" already exists in the list. Do you want to update its quantity from ${currentQuantity} to ${currentQuantity + newQuantity}?` + ); + if (!yes) return; + + newQuantity += currentQuantity; + } + + await addItem(itemName, newQuantity); setItemName(""); setQuantity(1); @@ -93,7 +104,6 @@ export default function GroceryList() { return (