-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #40 from atlp-rwanda/187354255-create-product
Feature for enabling product to be created by seller
- Loading branch information
Showing
19 changed files
with
1,109 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Request, Response } from 'express'; | ||
import { Category, CategoryCreationAttributes } from '../database/models/Category'; | ||
import logger from '../logs/config'; | ||
|
||
export const createCategory = async (req: Request, res: Response) => { | ||
try { | ||
const { name, description } = req.body as CategoryCreationAttributes; | ||
await Category.create({ | ||
name, | ||
description, | ||
}); | ||
res.status(201).json({ ok: true, message: 'New category created successully!' }); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
logger.error(error.message); | ||
} | ||
res.status(500).json({ error: 'Failed to create category' }); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import { Request, Response } from 'express'; | ||
import uploadImage from '../helpers/claudinary'; | ||
import { sendInternalErrorResponse } from '../validations'; | ||
import { Product, ProductAttributes } from '../database/models/Product'; | ||
import { Size, SizeAttributes } from '../database/models/Size'; | ||
|
||
export const createProduct = async (req: Request, res: Response) => { | ||
try { | ||
const { name, description, colors } = req.body as ProductAttributes; | ||
const { categoryId } = req.params; | ||
const seller = (await req.user) as any; | ||
const sellerId = seller.id; | ||
|
||
// when products exists | ||
const thisProductExists = await Product.findOne({ where: { name } }); | ||
|
||
if (thisProductExists) { | ||
return res.status(400).json({ | ||
ok: false, | ||
message: 'This Product already exists, You can update the stock levels instead.', | ||
data: thisProductExists, | ||
}); | ||
} | ||
// handle images | ||
const productImages = ['asdf', 'asdf', 'asdf', 'asdf']; | ||
const images: unknown = req.files; | ||
if (images instanceof Array && images.length > 3) { | ||
for (const image of images) { | ||
const imageBuffer: Buffer = image.buffer; | ||
const url = await uploadImage(imageBuffer); | ||
productImages.push(url); | ||
} | ||
} else { | ||
return res.status(400).json({ | ||
message: 'Product should have at least 4 images', | ||
}); | ||
} | ||
|
||
// create product | ||
await Product.create({ | ||
sellerId, | ||
name, | ||
description, | ||
categoryId, | ||
colors, | ||
images: productImages, | ||
}); | ||
|
||
res.status(201).json({ | ||
ok: true, | ||
message: 'Thank you for adding new product in the store!', | ||
}); | ||
} catch (error) { | ||
sendInternalErrorResponse(res, error); | ||
} | ||
}; | ||
|
||
export const createSize = async (req: Request, res: Response) => { | ||
try { | ||
const { productId } = req.params; | ||
const { size, price, discount, expiryDate } = req.body as SizeAttributes; | ||
await Size.create({ size, price, discount, expiryDate, productId }); | ||
res.status(201).json({ | ||
ok: true, | ||
message: 'Product size added successfully', | ||
}); | ||
} catch (error) { | ||
sendInternalErrorResponse(res, error); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
'use strict'; | ||
|
||
const sequelize = require('sequelize'); | ||
|
||
/** @type {import('sequelize-cli').Migration} */ | ||
module.exports = { | ||
async up(queryInterface, Sequelize) { | ||
await queryInterface.createTable('categories', { | ||
id: { | ||
allowNull: false, | ||
primaryKey: true, | ||
type: Sequelize.UUID, | ||
defaultValue: sequelize.UUIDV4, | ||
unique: true, | ||
}, | ||
name: { | ||
type: Sequelize.STRING, | ||
allowNull: false, | ||
}, | ||
description: { | ||
type: Sequelize.TEXT, | ||
allowNull: true, | ||
}, | ||
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('categories'); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
'use strict'; | ||
|
||
const sequelize = require('sequelize'); | ||
|
||
/** @type {import('sequelize-cli').Migration} */ | ||
module.exports = { | ||
async up(queryInterface, Sequelize) { | ||
await queryInterface.createTable('products', { | ||
id: { | ||
allowNull: false, | ||
primaryKey: true, | ||
type: Sequelize.UUID, | ||
defaultValue: sequelize.UUIDV4, | ||
}, | ||
name: { | ||
type: Sequelize.STRING, | ||
allowNull: false, | ||
}, | ||
description: { | ||
type: Sequelize.TEXT, | ||
allowNull: false, | ||
}, | ||
images: { | ||
type: Sequelize.ARRAY(Sequelize.STRING), | ||
allowNull: false, | ||
}, | ||
colors: { | ||
type: Sequelize.ARRAY(Sequelize.STRING), | ||
allowNull: true, | ||
}, | ||
sellerId: { | ||
type: Sequelize.UUID, | ||
references: { | ||
model: 'Users', | ||
key: 'id', | ||
}, | ||
onDelete: 'CASCADE', | ||
onUpdate: 'CASCADE', | ||
allowNull: false, | ||
}, | ||
categoryId: { | ||
type: Sequelize.UUID, | ||
allowNull: false, | ||
references: { | ||
model: 'categories', | ||
key: 'id', | ||
}, | ||
onUpdate: 'CASCADE', | ||
onDelete: 'CASCADE', | ||
}, | ||
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('product_sizes'); | ||
await queryInterface.dropTable('products'); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
'use strict'; | ||
|
||
const sequelize = require('sequelize'); | ||
|
||
/** @type {import('sequelize-cli').Migration} */ | ||
module.exports = { | ||
async up(queryInterface, Sequelize) { | ||
await queryInterface.createTable('sizes', { | ||
id: { | ||
allowNull: false, | ||
primaryKey: true, | ||
type: Sequelize.UUID, | ||
defaultValue: sequelize.UUIDV4, | ||
}, | ||
size: { | ||
type: Sequelize.STRING, | ||
allowNull: true, | ||
}, | ||
price: { | ||
type: Sequelize.FLOAT, | ||
allowNull: false, | ||
}, | ||
quantity: { | ||
type: Sequelize.INTEGER, | ||
defaultValue: 1, | ||
}, | ||
discount: { | ||
type: Sequelize.FLOAT, | ||
allowNull: true, | ||
}, | ||
expiryDate: { | ||
type: Sequelize.DATE, | ||
allowNull: true, | ||
}, | ||
productId: { | ||
type: Sequelize.UUID, | ||
references: { | ||
model: 'products', | ||
key: 'id', | ||
}, | ||
onDelete: 'CASCADE', | ||
onUpdate: 'CASCADE', | ||
allowNull: false, | ||
}, | ||
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('sizes'); | ||
}, | ||
}; |
Oops, something went wrong.