Initial commit
This commit is contained in:
235
server/controllers/productController.js
Normal file
235
server/controllers/productController.js
Normal file
@@ -0,0 +1,235 @@
|
||||
const Product = require('../models/Product');
|
||||
const Category = require('../models/Category');
|
||||
const Brand = require('../models/Brand');
|
||||
const { successResponse, errorResponse } = require('../utils/apiResponse');
|
||||
|
||||
// @desc Get all products with filtering, search, sorting & pagination
|
||||
// @route GET /api/products
|
||||
// @access Public
|
||||
const getProducts = async (req, res, next) => {
|
||||
try {
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = parseInt(req.query.limit) || 12;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const query = { isActive: true };
|
||||
|
||||
// Search query
|
||||
if (req.query.search) {
|
||||
const searchRegex = new RegExp(req.query.search, 'i');
|
||||
query.$or = [
|
||||
{ name: searchRegex },
|
||||
{ description: searchRegex },
|
||||
{ tags: searchRegex },
|
||||
{ sku: searchRegex }
|
||||
];
|
||||
}
|
||||
|
||||
// Category filter
|
||||
if (req.query.category) {
|
||||
const cat = await Category.findOne({ slug: req.query.category });
|
||||
if (cat) query.category = cat._id;
|
||||
}
|
||||
|
||||
// Brand filter
|
||||
if (req.query.brand) {
|
||||
const brand = await Brand.findOne({ slug: req.query.brand });
|
||||
if (brand) query.brand = brand._id;
|
||||
}
|
||||
|
||||
// Price range filter
|
||||
if (req.query.minPrice || req.query.maxPrice) {
|
||||
query.price = {};
|
||||
if (req.query.minPrice) query.price.$gte = Number(req.query.minPrice);
|
||||
if (req.query.maxPrice) query.price.$lte = Number(req.query.maxPrice);
|
||||
}
|
||||
|
||||
// Guitar specific filters
|
||||
if (req.query.guitarType) {
|
||||
query['specs.guitarType'] = req.query.guitarType;
|
||||
}
|
||||
if (req.query.bodyShape) {
|
||||
query['specs.bodyShape'] = req.query.bodyShape;
|
||||
}
|
||||
if (req.query.handOrientation) {
|
||||
query['specs.handOrientation'] = req.query.handOrientation;
|
||||
}
|
||||
if (req.query.numberOfStrings) {
|
||||
query['specs.numberOfStrings'] = Number(req.query.numberOfStrings);
|
||||
}
|
||||
if (req.query.isFeatured) {
|
||||
query.isFeatured = true;
|
||||
}
|
||||
if (req.query.isNewArrival) {
|
||||
query.isNewArrival = true;
|
||||
}
|
||||
if (req.query.isBestSeller) {
|
||||
query.isBestSeller = true;
|
||||
}
|
||||
|
||||
// Sorting
|
||||
let sortOption = { createdAt: -1 };
|
||||
if (req.query.sort === 'price_asc') sortOption = { price: 1 };
|
||||
if (req.query.sort === 'price_desc') sortOption = { price: -1 };
|
||||
if (req.query.sort === 'rating') sortOption = { rating: -1 };
|
||||
if (req.query.sort === 'bestselling') sortOption = { salesCount: -1 };
|
||||
|
||||
const total = await Product.countDocuments(query);
|
||||
const products = await Product.find(query)
|
||||
.populate('category', 'name slug')
|
||||
.populate('brand', 'name slug logo')
|
||||
.sort(sortOption)
|
||||
.skip(skip)
|
||||
.limit(limit);
|
||||
|
||||
return successResponse(res, 'Products retrieved successfully', {
|
||||
products,
|
||||
pagination: {
|
||||
total,
|
||||
page,
|
||||
pages: Math.ceil(total / limit),
|
||||
limit
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// @desc Guitar Finder Recommendation Tool
|
||||
// @route POST /api/products/guitar-finder
|
||||
// @access Public
|
||||
const getGuitarRecommendations = async (req, res, next) => {
|
||||
try {
|
||||
const { skillLevel, acousticElectric, musicStyle, maxBudget, handOrientation } = req.body;
|
||||
|
||||
const query = { isActive: true };
|
||||
|
||||
if (acousticElectric && acousticElectric !== 'Any') {
|
||||
query['specs.guitarType'] = acousticElectric;
|
||||
}
|
||||
|
||||
if (maxBudget) {
|
||||
query.price = { $lte: Number(maxBudget) };
|
||||
}
|
||||
|
||||
if (handOrientation) {
|
||||
query['specs.handOrientation'] = handOrientation;
|
||||
}
|
||||
|
||||
const recommendations = await Product.find(query)
|
||||
.populate('category', 'name slug')
|
||||
.populate('brand', 'name slug logo')
|
||||
.sort({ rating: -1, salesCount: -1 })
|
||||
.limit(6);
|
||||
|
||||
return successResponse(res, 'Guitar recommendations generated', recommendations);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// @desc Get single product by slug
|
||||
// @route GET /api/products/:slug
|
||||
// @access Public
|
||||
const getProductBySlug = async (req, res, next) => {
|
||||
try {
|
||||
const product = await Product.findOne({ slug: req.params.slug })
|
||||
.populate('category', 'name slug')
|
||||
.populate('brand', 'name slug logo description');
|
||||
|
||||
if (!product) {
|
||||
return errorResponse(res, 'Product not found', [], 404);
|
||||
}
|
||||
|
||||
// Related products (same category)
|
||||
const relatedProducts = await Product.find({
|
||||
category: product.category._id,
|
||||
_id: { $ne: product._id },
|
||||
isActive: true
|
||||
})
|
||||
.limit(4)
|
||||
.select('name slug price salePrice images rating brand');
|
||||
|
||||
return successResponse(res, 'Product retrieved successfully', {
|
||||
product,
|
||||
relatedProducts
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// @desc Compare products by array of IDs
|
||||
// @route POST /api/products/compare
|
||||
// @access Public
|
||||
const compareProducts = async (req, res, next) => {
|
||||
try {
|
||||
const { productIds } = req.body;
|
||||
if (!productIds || !Array.isArray(productIds)) {
|
||||
return errorResponse(res, 'Please provide an array of product IDs', [], 400);
|
||||
}
|
||||
|
||||
const products = await Product.find({ _id: { $in: productIds } })
|
||||
.populate('brand', 'name logo')
|
||||
.populate('category', 'name');
|
||||
|
||||
return successResponse(res, 'Comparison data retrieved', products);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// @desc Create new product (Admin)
|
||||
// @route POST /api/products
|
||||
// @access Private/Admin
|
||||
const createProduct = async (req, res, next) => {
|
||||
try {
|
||||
const product = new Product(req.body);
|
||||
const createdProduct = await product.save();
|
||||
return successResponse(res, 'Product created successfully', createdProduct, 201);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// @desc Update product (Admin)
|
||||
// @route PUT /api/products/:id
|
||||
// @access Private/Admin
|
||||
const updateProduct = async (req, res, next) => {
|
||||
try {
|
||||
const product = await Product.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true });
|
||||
if (!product) {
|
||||
return errorResponse(res, 'Product not found', [], 404);
|
||||
}
|
||||
return successResponse(res, 'Product updated successfully', product);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// @desc Delete product (Admin)
|
||||
// @route DELETE /api/products/:id
|
||||
// @access Private/Admin
|
||||
const deleteProduct = async (req, res, next) => {
|
||||
try {
|
||||
const product = await Product.findByIdAndDelete(req.params.id);
|
||||
if (!product) {
|
||||
return errorResponse(res, 'Product not found', [], 404);
|
||||
}
|
||||
return successResponse(res, 'Product deleted successfully');
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getProducts,
|
||||
getGuitarRecommendations,
|
||||
getProductBySlug,
|
||||
compareProducts,
|
||||
createProduct,
|
||||
updateProduct,
|
||||
deleteProduct
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user