costco-grocery-list/backend/controllers/lists.controller.v2.js

325 lines
9.3 KiB
JavaScript

const List = require("../models/list.model.v2");
const { isValidItemType, isValidItemGroup, isValidZone } = require("../constants/classifications");
/**
* Get list items for household and store
* GET /households/:householdId/stores/:storeId/list
*/
exports.getList = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const items = await List.getHouseholdStoreList(householdId, storeId);
res.json({ items });
} catch (error) {
console.error("Error getting list:", error);
res.status(500).json({ message: "Failed to get list" });
}
};
/**
* Get specific item by name
* GET /households/:householdId/stores/:storeId/list/item
*/
exports.getItemByName = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const { item_name } = req.query;
if (!item_name) {
return res.status(400).json({ message: "Item name is required" });
}
const item = await List.getItemByName(householdId, storeId, item_name);
if (!item) {
return res.status(404).json({ message: "Item not found" });
}
res.json(item);
} catch (error) {
console.error("Error getting item:", error);
res.status(500).json({ message: "Failed to get item" });
}
};
/**
* Add or update item in household store list
* POST /households/:householdId/stores/:storeId/list/add
*/
exports.addItem = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const { item_name, quantity, notes } = req.body;
const userId = req.user.id;
if (!item_name || item_name.trim() === "") {
return res.status(400).json({ message: "Item name is required" });
}
// Get processed image if uploaded
const imageBuffer = req.processedImage?.buffer || null;
const mimeType = req.processedImage?.mimeType || null;
const result = await List.addOrUpdateItem(
householdId,
storeId,
item_name,
quantity || "1",
userId,
imageBuffer,
mimeType,
notes
);
// Add history record
await List.addHistoryRecord(result.listId, quantity || "1", userId);
res.json({
message: result.isNew ? "Item added" : "Item updated",
item: {
id: result.listId,
item_name: result.itemName,
quantity: quantity || "1",
bought: false
}
});
} catch (error) {
console.error("Error adding item:", error);
res.status(500).json({ message: "Failed to add item" });
}
};
/**
* Mark item as bought or unbought
* PATCH /households/:householdId/stores/:storeId/list/item
*/
exports.markBought = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const { item_name, bought, quantity_bought } = req.body;
if (!item_name) return res.status(400).json({ message: "Item name is required" });
const item = await List.getItemByName(householdId, storeId, item_name);
console.log('requesting mark ', { item, householdId, storeId, item_name, bought, quantity_bought });
if (!item) return res.status(404).json({ message: "Item not found" });
// Update bought status (with optional partial purchase)
await List.setBought(item.id, bought, quantity_bought);
res.json({ message: bought ? "Item marked as bought" : "Item unmarked" });
} catch (error) {
console.error("Error marking bought:", error);
res.status(500).json({ message: "Failed to update item" });
}
};
/**
* Update item details (quantity, notes)
* PUT /households/:householdId/stores/:storeId/list/item
*/
exports.updateItem = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const { item_name, quantity, notes } = req.body;
if (!item_name) {
return res.status(400).json({ message: "Item name is required" });
}
// Get the list item
const item = await List.getItemByName(householdId, storeId, item_name);
if (!item) {
return res.status(404).json({ message: "Item not found" });
}
// Update item
await List.updateItem(item.id, item_name, quantity, notes);
res.json({
message: "Item updated",
item: {
id: item.id,
item_name,
quantity,
notes
}
});
} catch (error) {
console.error("Error updating item:", error);
res.status(500).json({ message: "Failed to update item" });
}
};
/**
* Delete item from list
* DELETE /households/:householdId/stores/:storeId/list/item
*/
exports.deleteItem = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const { item_name } = req.body;
if (!item_name) {
return res.status(400).json({ message: "Item name is required" });
}
// Get the list item
const item = await List.getItemByName(householdId, storeId, item_name);
if (!item) {
return res.status(404).json({ message: "Item not found" });
}
await List.deleteItem(item.id);
res.json({ message: "Item deleted" });
} catch (error) {
console.error("Error deleting item:", error);
res.status(500).json({ message: "Failed to delete item" });
}
};
/**
* Get item suggestions based on query
* GET /households/:householdId/stores/:storeId/list/suggestions
*/
exports.getSuggestions = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const { query } = req.query;
const suggestions = await List.getSuggestions(query || "", householdId, storeId);
res.json(suggestions);
} catch (error) {
console.error("Error getting suggestions:", error);
res.status(500).json({ message: "Failed to get suggestions" });
}
};
/**
* Get recently bought items
* GET /households/:householdId/stores/:storeId/list/recent
*/
exports.getRecentlyBought = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const items = await List.getRecentlyBoughtItems(householdId, storeId);
res.json(items);
} catch (error) {
console.error("Error getting recent items:", error);
res.status(500).json({ message: "Failed to get recent items" });
}
};
/**
* Get item classification
* GET /households/:householdId/stores/:storeId/list/classification
*/
exports.getClassification = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const { item_name } = req.query;
if (!item_name) {
return res.status(400).json({ message: "Item name is required" });
}
// Get item ID from name
const item = await List.getItemByName(householdId, storeId, item_name);
if (!item) {
return res.json({ classification: null });
}
const classification = await List.getClassification(householdId, item.item_id);
res.json({ classification });
} catch (error) {
console.error("Error getting classification:", error);
res.status(500).json({ message: "Failed to get classification" });
}
};
/**
* Set/update item classification
* POST /households/:householdId/stores/:storeId/list/classification
*/
exports.setClassification = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const { item_name, classification } = req.body;
if (!item_name) {
return res.status(400).json({ message: "Item name is required" });
}
if (!classification) {
return res.status(400).json({ message: "Classification is required" });
}
// Validate classification
const validClassifications = ['produce', 'dairy', 'meat', 'bakery', 'frozen', 'pantry', 'snacks', 'beverages', 'household', 'other'];
if (!validClassifications.includes(classification)) {
return res.status(400).json({ message: "Invalid classification value" });
}
// Get item - add to master items if not exists
const item = await List.getItemByName(householdId, storeId, item_name);
let itemId;
if (!item) {
// Item doesn't exist in list, need to get from items table or create
const itemResult = await List.addOrUpdateItem(
householdId,
storeId,
item_name,
"1",
req.user.id,
null,
null
);
itemId = itemResult.itemId;
} else {
itemId = item.item_id;
}
// Set classification (using item_type field for simplicity)
await List.upsertClassification(householdId, itemId, {
item_type: classification,
item_group: null,
zone: null
});
res.json({ message: "Classification set", classification });
} catch (error) {
console.error("Error setting classification:", error);
res.status(500).json({ message: "Failed to set classification" });
}
};
/**
* Update item image
* POST /households/:householdId/stores/:storeId/list/update-image
*/
exports.updateItemImage = async (req, res) => {
try {
const { householdId, storeId } = req.params;
const { item_name, quantity } = req.body;
const userId = req.user.id;
// Get processed image
const imageBuffer = req.processedImage?.buffer || null;
const mimeType = req.processedImage?.mimeType || null;
if (!imageBuffer) {
return res.status(400).json({ message: "No image provided" });
}
// Update the item with new image
await List.addOrUpdateItem(householdId, storeId, item_name, quantity, userId, imageBuffer, mimeType);
res.json({ message: "Image updated successfully" });
} catch (error) {
console.error("Error updating image:", error);
res.status(500).json({ message: "Failed to update image" });
}
};