Fix suggestion feature
This commit is contained in:
parent
4501c47849
commit
963240ceb1
@ -44,8 +44,4 @@ app.use("/admin", adminRoutes);
|
|||||||
const usersRoutes = require("./routes/users.routes");
|
const usersRoutes = require("./routes/users.routes");
|
||||||
app.use("/users", usersRoutes);
|
app.use("/users", usersRoutes);
|
||||||
|
|
||||||
const suggestController = require("./routes/suggest.routes");
|
|
||||||
app.get("/suggest", suggestController);
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
@ -15,14 +15,20 @@ exports.register = async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.login = async (req, res) => {
|
exports.login = async (req, res) => {
|
||||||
const { username, password } = req.body;
|
let { username, password } = req.body;
|
||||||
console.log(`Login attempt for user: ${username} with password: ${password}`);
|
|
||||||
|
|
||||||
|
username = username.toLowerCase();
|
||||||
const user = await User.findByUsername(username);
|
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);
|
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(
|
const token = jwt.sign(
|
||||||
{ id: user.id, role: user.role },
|
{ id: user.id, role: user.role },
|
||||||
|
|||||||
@ -22,3 +22,10 @@ exports.markBought = async (req, res) => {
|
|||||||
await List.setBought(req.body.id);
|
await List.setBought(req.body.id);
|
||||||
res.json({ message: "Item marked bought" });
|
res.json({ message: "Item marked bought" });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.getSuggestions = async (req, res) => {
|
||||||
|
const { query } = req.query || "";
|
||||||
|
const suggestions = await List.getSuggestions(query);
|
||||||
|
res.json(suggestions);
|
||||||
|
};
|
||||||
@ -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");
|
|
||||||
};
|
|
||||||
@ -44,7 +44,8 @@ exports.addHistoryRecord = async (itemId, quantity) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.getHistory = async (query) => {
|
|
||||||
|
exports.getSuggestions = async (query) => {
|
||||||
const result = await pool.query(
|
const result = await pool.query(
|
||||||
`SELECT DISTINCT item_name
|
`SELECT DISTINCT item_name
|
||||||
FROM grocery_list
|
FROM grocery_list
|
||||||
@ -52,9 +53,7 @@ exports.getHistory = async (query) => {
|
|||||||
LIMIT 10`,
|
LIMIT 10`,
|
||||||
[`%${query}%`]
|
[`%${query}%`]
|
||||||
);
|
);
|
||||||
console.log("QUERY:");
|
res = result.rows;
|
||||||
console.log(result.query);
|
|
||||||
return result.rows;
|
return result.rows;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ const User = require("../models/user.model");
|
|||||||
|
|
||||||
|
|
||||||
router.get("/", auth, requireRole(ROLES.VIEWER, ROLES.EDITOR, ROLES.ADMIN), controller.getList);
|
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);
|
router.post("/add", auth, requireRole(ROLES.EDITOR, ROLES.ADMIN), controller.addItem);
|
||||||
|
|||||||
@ -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;
|
|
||||||
@ -2,7 +2,6 @@ import api from "./axios";
|
|||||||
|
|
||||||
export const loginRequest = async (username, password) => {
|
export const loginRequest = async (username, password) => {
|
||||||
const res = await api.post("/auth/login", { username, password });
|
const res = await api.post("/auth/login", { username, password });
|
||||||
alert(`Response data: ${JSON.stringify(res.data)}`);
|
|
||||||
return res.data;
|
return res.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,4 @@ import api from "./axios";
|
|||||||
export const getList = () => api.get("/list");
|
export const getList = () => api.get("/list");
|
||||||
export const addItem = (itemName, quantiy) => api.post("/list/add", { itemName, quantiy });
|
export const addItem = (itemName, quantiy) => api.post("/list/add", { itemName, quantiy });
|
||||||
export const markBought = (id) => api.post("/list/mark-bought", { id });
|
export const markBought = (id) => api.post("/list/mark-bought", { id });
|
||||||
export const suggest = (query) => {
|
export const getSuggestions = (query) => api.get("/list/suggest", { params: { query: query } });
|
||||||
console.log("API SUGGEST QUERY:", query);
|
|
||||||
api.get("/suggest", { query });
|
|
||||||
};
|
|
||||||
@ -2,4 +2,5 @@ export const ROLES = {
|
|||||||
VIEWER: "viewer",
|
VIEWER: "viewer",
|
||||||
EDITOR: "editor",
|
EDITOR: "editor",
|
||||||
ADMIN: "admin",
|
ADMIN: "admin",
|
||||||
|
UP_TO_ADMIN: ["viewer", "editor", "admin"],
|
||||||
};
|
};
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { useContext, useEffect, useState } from "react";
|
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 { AuthContext } from "../context/AuthContext";
|
||||||
import "../styles/GroceryList.css";
|
import "../styles/GroceryList.css";
|
||||||
|
|
||||||
@ -56,7 +57,11 @@ export default function GroceryList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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 {
|
} catch {
|
||||||
setSuggestions([]);
|
setSuggestions([]);
|
||||||
}
|
}
|
||||||
@ -89,8 +94,7 @@ export default function GroceryList() {
|
|||||||
<div className="glist-body">
|
<div className="glist-body">
|
||||||
<div className="glist-container">
|
<div className="glist-container">
|
||||||
|
|
||||||
<h1 className="glist-title">Costco Grocery List - {username}[{role}]</h1>
|
<h1 className="glist-title">Costco Grocery List</h1>
|
||||||
<p><strong>{username}</strong> ({role})</p>
|
|
||||||
|
|
||||||
{/* Sorting dropdown */}
|
{/* Sorting dropdown */}
|
||||||
<select
|
<select
|
||||||
@ -105,7 +109,7 @@ export default function GroceryList() {
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
{/* Add Item form (editor/admin only) */}
|
{/* Add Item form (editor/admin only) */}
|
||||||
{(role === "editor" || role === "admin") && showAddForm && (
|
{[ROLES.ADMIN, ROLES.VIEWER].includes(role) && showAddForm && (
|
||||||
<>
|
<>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@ -163,7 +167,7 @@ export default function GroceryList() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Floating Button (editor/admin only) */}
|
{/* Floating Button (editor/admin only) */}
|
||||||
{(role === "editor" || role === "admin") && (
|
{[ROLES.ADMIN, ROLES.VIEWER].includes(role) && (
|
||||||
<button
|
<button
|
||||||
className="glist-fab"
|
className="glist-fab"
|
||||||
onClick={() => setShowAddForm(!showAddForm)}
|
onClick={() => setShowAddForm(!showAddForm)}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export default function Login() {
|
|||||||
login(data);
|
login(data);
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError("Invalid username or password");
|
setError(err.response?.data?.message || "Login failed");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -55,13 +55,17 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
width: calc(100% - 2em);
|
border-radius: 8px;
|
||||||
left: 1em;
|
box-shadow: 0 0 10px rgba(0,0,0,0.08);
|
||||||
right: 1em;
|
padding: 1em;
|
||||||
|
width: calc(100% - 8em);
|
||||||
|
max-width: 440px;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glist-suggest-item {
|
.glist-suggest-item {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
padding-inline: 2em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user