Fix suggestion feature

This commit is contained in:
Nico 2025-11-22 22:21:36 -08:00
parent 4501c47849
commit 963240ceb1
13 changed files with 42 additions and 46 deletions

View File

@ -44,8 +44,4 @@ app.use("/admin", adminRoutes);
const usersRoutes = require("./routes/users.routes");
app.use("/users", usersRoutes);
const suggestController = require("./routes/suggest.routes");
app.get("/suggest", suggestController);
module.exports = app;

View File

@ -15,14 +15,20 @@ exports.register = async (req, res) => {
};
exports.login = async (req, res) => {
const { username, password } = req.body;
console.log(`Login attempt for user: ${username} with password: ${password}`);
let { username, password } = req.body;
username = username.toLowerCase();
const user = await User.findByUsername(username);
if (!user) return res.status(401).json({ message: "Invalid credentials" });
if (!user) {
console.log(`⚠️ Login attempt -> No user found: ${username}`);
return res.status(401).json({ message: "User not found" });
}
const valid = await bcrypt.compare(password, user.password);
if (!valid) return res.status(401).json({ message: "Invalid credentials" });
if (!valid) {
console.log(`⛔ Login attempt for user ${username} with password ${password}`);
return res.status(401).json({ message: "Invalid credentials" });
}
const token = jwt.sign(
{ id: user.id, role: user.role },

View File

@ -22,3 +22,10 @@ exports.markBought = async (req, res) => {
await List.setBought(req.body.id);
res.json({ message: "Item marked bought" });
};
exports.getSuggestions = async (req, res) => {
const { query } = req.query || "";
const suggestions = await List.getSuggestions(query);
res.json(suggestions);
};

View File

@ -1,9 +0,0 @@
const List = require("../models/list.model");
exports.getHistory = async (req, res) => {
console.log("GET /suggest called");
const { query } = req.query;
const items = await List.getHistory(query);
res.json("asdf");
};

View File

@ -44,7 +44,8 @@ exports.addHistoryRecord = async (itemId, quantity) => {
);
};
exports.getHistory = async (query) => {
exports.getSuggestions = async (query) => {
const result = await pool.query(
`SELECT DISTINCT item_name
FROM grocery_list
@ -52,9 +53,7 @@ exports.getHistory = async (query) => {
LIMIT 10`,
[`%${query}%`]
);
console.log("QUERY:");
console.log(result.query);
res = result.rows;
return result.rows;
};

View File

@ -8,6 +8,7 @@ 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.post("/add", auth, requireRole(ROLES.EDITOR, ROLES.ADMIN), controller.addItem);

View File

@ -1,9 +0,0 @@
const router = require("express").Router();
const controller = require("../controllers/suggest.controller");
const auth = require("../middleware/auth");
const requireRole = require("../middleware/rbac");
const { ROLES } = require("../models/user.model");
router.get("/", auth, requireRole(ROLES.VIEWER, ROLES.EDITOR, ROLES.ADMIN), controller.getHistory);
module.exports = router;

View File

@ -2,7 +2,6 @@ import api from "./axios";
export const loginRequest = async (username, password) => {
const res = await api.post("/auth/login", { username, password });
alert(`Response data: ${JSON.stringify(res.data)}`);
return res.data;
};

View File

@ -3,7 +3,4 @@ import api from "./axios";
export const getList = () => api.get("/list");
export const addItem = (itemName, quantiy) => api.post("/list/add", { itemName, quantiy });
export const markBought = (id) => api.post("/list/mark-bought", { id });
export const suggest = (query) => {
console.log("API SUGGEST QUERY:", query);
api.get("/suggest", { query });
};
export const getSuggestions = (query) => api.get("/list/suggest", { params: { query: query } });

View File

@ -2,4 +2,5 @@ export const ROLES = {
VIEWER: "viewer",
EDITOR: "editor",
ADMIN: "admin",
UP_TO_ADMIN: ["viewer", "editor", "admin"],
};

View File

@ -1,5 +1,6 @@
import { useContext, useEffect, useState } from "react";
import { addItem, getList, markBought, suggest } from "../api/list";
import { addItem, getList, getSuggestions, markBought } from "../api/list";
import { ROLES } from "../constants/roles";
import { AuthContext } from "../context/AuthContext";
import "../styles/GroceryList.css";
@ -56,7 +57,11 @@ export default function GroceryList() {
}
try {
setSuggestions(suggest(text).data.map((i) => i.item_name));
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([]);
}
@ -89,8 +94,7 @@ export default function GroceryList() {
<div className="glist-body">
<div className="glist-container">
<h1 className="glist-title">Costco Grocery List - {username}[{role}]</h1>
<p><strong>{username}</strong> ({role})</p>
<h1 className="glist-title">Costco Grocery List</h1>
{/* Sorting dropdown */}
<select
@ -105,7 +109,7 @@ export default function GroceryList() {
</select>
{/* Add Item form (editor/admin only) */}
{(role === "editor" || role === "admin") && showAddForm && (
{[ROLES.ADMIN, ROLES.VIEWER].includes(role) && showAddForm && (
<>
<input
type="text"
@ -163,7 +167,7 @@ export default function GroceryList() {
</div>
{/* Floating Button (editor/admin only) */}
{(role === "editor" || role === "admin") && (
{[ROLES.ADMIN, ROLES.VIEWER].includes(role) && (
<button
className="glist-fab"
onClick={() => setShowAddForm(!showAddForm)}

View File

@ -16,7 +16,7 @@ export default function Login() {
login(data);
window.location.href = "/";
} catch (err) {
setError("Invalid username or password");
setError(err.response?.data?.message || "Login failed");
}
};

View File

@ -54,14 +54,18 @@
max-height: 150px;
overflow-y: auto;
position: absolute;
z-index: 999;
width: calc(100% - 2em);
left: 1em;
right: 1em;
z-index: 999;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.08);
padding: 1em;
width: calc(100% - 8em);
max-width: 440px;
margin: 0 auto;
}
.glist-suggest-item {
padding: 0.5em;
padding-inline: 2em;
cursor: pointer;
}