Files
Guitar-Ecommerce/server/controllers/productController.js
2026-09-16 17:36:46 +05:30

236 lines
6.6 KiB
JavaScript

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
};