diff --git a/src/controllers/productsController.ts b/src/controllers/productsController.ts index 61464c1b..2a711b05 100644 --- a/src/controllers/productsController.ts +++ b/src/controllers/productsController.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Request, Response } from 'express'; import uploadImage from '../helpers/claudinary'; diff --git a/src/controllers/wishlistController.ts b/src/controllers/wishlistController.ts new file mode 100644 index 00000000..a0fda075 --- /dev/null +++ b/src/controllers/wishlistController.ts @@ -0,0 +1,111 @@ +import { Request, Response } from 'express'; +import Wishlist from '../database/models/wishlist'; +import { Product } from '../database/models/Product'; +import { Size } from '../database/models/Size'; +import { sendInternalErrorResponse } from '../validations'; +import User from '../database/models/user'; + +export const addToWishlist = async (req: Request, res: Response) => { + try { + const { id } = req.params; + const user = req.user as User; + + const itemExist = await Wishlist.findOne({ + where: { + userId: user.id, + sizeId: id, + }, + }); + if (itemExist) { + return res.status(400).json({ + ok: false, + message: 'Product already added to wishlist', + }); + } + await Wishlist.create({ + userId: user.id, + sizeId: id, + }); + + return res.status(201).json({ + ok: true, + message: 'Product added to wishlist successfully', + }); + } catch (err) { + return sendInternalErrorResponse(res, err); + } +}; +export const getWishlist = async (req: Request, res: Response) => { + try { + const userId = (req.user as User).id; + + const wishlistItems = await Wishlist.findAll({ + where: { + userId, + }, + include: { + model: Size, + attributes: ['productId', 'price', 'quantity', 'id'], + }, + }); + const productIds = wishlistItems.map((item: any) => item.Size.productId); + + const wishlistproducts = await Product.findAll({ + where: { id: productIds }, + }); + + const combinedResponse = wishlistItems.map((wishlistItem: any) => { + const matchingProduct = wishlistproducts.find((product: any) => product.id === wishlistItem.Size.productId); + return { + id: wishlistItem.dataValues.id, + name: matchingProduct?.dataValues.name, + image: matchingProduct?.dataValues.images[0], + productId: wishlistItem.dataValues.Size.dataValues.id, + price: wishlistItem.dataValues.Size.dataValues.price, + }; + }); + + return res.status(200).json({ + ok: true, + message: 'Wishlist fetched successfully', + data: combinedResponse, + }); + } catch (err) { + return sendInternalErrorResponse(res, err); + } +}; + +export const clearWishList = async (req: Request, res: Response) => { + try { + const userId = (req.user as User).id; + + const rowsAffected = await Wishlist.destroy({ where: { userId } }); + + if (rowsAffected > 0) { + return res.status(200).json({ + ok: true, + message: 'Wishlist cleared successfully', + }); + } else { + return res.status(404).json({ + ok: false, + message: 'No wishlist items found for deletion', + }); + } + } catch (err) { + return sendInternalErrorResponse(res, err); + } +}; +export const deleteWishlistItem = async (req: Request, res: Response) => { + const user = req.user as User; + const { id } = req.params; + try { + await Wishlist.destroy({ where: { userId: user.id, id } }); + return res.status(200).json({ + ok: true, + message: 'Wishlist item deleted successfully', + }); + } catch (err) { + return sendInternalErrorResponse(res, err); + } +}; diff --git a/src/database/migrations/20240508175113-create-wishlist.js b/src/database/migrations/20240508175113-create-wishlist.js new file mode 100644 index 00000000..d5ec4333 --- /dev/null +++ b/src/database/migrations/20240508175113-create-wishlist.js @@ -0,0 +1,47 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +// const sequelize=require('sequelize'); +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Wishlists', { + id: { + allowNull: false, + primaryKey: true, + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + unique: true, + }, + sizeId: { + type: Sequelize.UUID, + allowNull: false, + foreignKey: true, + references: { + model: 'sizes', + key: 'id', + }, + }, + userId: { + type: Sequelize.UUID, + allowNull: false, + foreignKey: true, + references: { + model: 'Users', + key: 'id', + }, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('NOW()'), + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + }); + }, + async down(queryInterface) { + await queryInterface.dropTable('Wishlists'); + }, +}; diff --git a/src/database/models/Category.ts b/src/database/models/Category.ts index 02aa27c3..9063f94a 100644 --- a/src/database/models/Category.ts +++ b/src/database/models/Category.ts @@ -13,7 +13,6 @@ export class Category extends Model { + id?: string; +} + +class Wishlist extends Model implements WishlistAttributes { + public id!: string; + public userId!: string; + public sizeId!: string; + + public static associate(models: { Size: typeof Size }): void { + Wishlist.belongsTo(models.Size, { + foreignKey: 'sizeId', + as: 'size', + }); + } +} + +Wishlist.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + userId: DataTypes.UUID, + sizeId: DataTypes.UUID, + }, + { + sequelize, + modelName: 'Wishlist', + tableName: 'Wishlists', // Corrected table name to match convention + timestamps: true, + } +); + +Wishlist.belongsTo(Size, { foreignKey: 'sizeId' }); +Size.hasMany(Wishlist, { foreignKey: 'sizeId' }); +export default Wishlist; diff --git a/src/docs/wishlist.yaml b/src/docs/wishlist.yaml new file mode 100644 index 00000000..a0dd9ec2 --- /dev/null +++ b/src/docs/wishlist.yaml @@ -0,0 +1,138 @@ +tags: + - name: Wishlist + description: Buyer Wishlist + +paths: + /api/wishlist/add-wishlist/{id}: + post: + summary: Add product to wishlist + tags: + - Wishlist + security: + - bearerAuth: [] + parameters: + - in: path + name: id + require: true + type: string + responses: + 201: + description: Product added to wishlist successfully + schema: + type: object + properties: + ok: + type: boolean + message: + type: string + data: + $ref: '#/definitions/WishlistItem' + 400: + description: Product not found + schema: + $ref: '#/definitions/Error' + 500: + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + + /api/wishlist/get-wishlist: + get: + summary: Get user's wishlist + tags: + - Wishlist + security: + - bearerAuth: [] + responses: + 200: + description: Wishlist fetched successfully + schema: + type: object + properties: + ok: + type: boolean + message: + type: string + wishlistItems: + type: array + items: + $ref: '#/definitions/WishlistItem' + 500: + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + + /api/wishlist: + delete: + summary: Clear user's wishlist + tags: + - Wishlist + security: + - bearerAuth: [] + responses: + 200: + description: Wishlist cleared successfully + schema: + type: object + properties: + ok: + type: boolean + message: + type: string + 500: + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + + /api/wishlist/{id}: + delete: + summary: Delete a product from wishlist + tags: + - Wishlist + security: + - bearerAuth: [] + parameters: + - in: path + name: id + description: wishList item id + required: true + type: string + responses: + 200: + description: Wishlist item deleted successfully + schema: + type: object + properties: + ok: + type: boolean + message: + type: string + 500: + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + +definitions: + WishlistItem: + type: object + properties: + id: + type: string + userId: + type: string + productId: + type: string + images: + type: array + items: + type: string + name: + type: string + price: + type: number + + Error: + type: object + properties: + message: + type: string \ No newline at end of file diff --git a/src/routes/index.ts b/src/routes/index.ts index 26b69b2e..b1fc8e77 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -5,6 +5,7 @@ import authRoute from './authRoute'; import roleRoute from './roleRoute'; import { productRouter } from './productRoutes'; import { categoryRouter } from './categoryRouter'; +import wishlistRoute from './wishlistRoute'; const router = Router(); @@ -13,4 +14,5 @@ router.use('/auth', authRoute); router.use('/roles', roleRoute); router.use('/products', productRouter); router.use('/category', categoryRouter); +router.use('/wishlist', wishlistRoute); export default router; diff --git a/src/routes/wishlistRoute.ts b/src/routes/wishlistRoute.ts new file mode 100644 index 00000000..e479f2da --- /dev/null +++ b/src/routes/wishlistRoute.ts @@ -0,0 +1,12 @@ +import { Router } from 'express'; +import { addToWishlist, getWishlist, clearWishList, deleteWishlistItem } from '../controllers/wishlistController'; +import { isAuthenticated, checkUserRoles } from '../middlewares/authMiddlewares'; + +const router = Router(); + +router.post('/add-wishlist/:id', isAuthenticated, checkUserRoles('buyer'), addToWishlist); +router.get('/get-wishlist', isAuthenticated, checkUserRoles('buyer'), getWishlist); +router.delete('/:id', isAuthenticated, checkUserRoles('buyer'), deleteWishlistItem); +router.delete('/', isAuthenticated, checkUserRoles('buyer'), clearWishList); + +export default router;