Skip to content

Commit

Permalink
Merge pull request #40 from atlp-rwanda/187354255-create-product
Browse files Browse the repository at this point in the history
Feature for enabling product to be created by seller
  • Loading branch information
niyontwali authored and P-Rwirangira committed May 10, 2024
2 parents 6ea4466 + 436c65b commit 6f1e030
Show file tree
Hide file tree
Showing 19 changed files with 1,109 additions and 1 deletion.
19 changes: 19 additions & 0 deletions src/controllers/categoriesController.ts
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' });
}
};
72 changes: 72 additions & 0 deletions src/controllers/productsController.ts
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);
}
};
111 changes: 111 additions & 0 deletions src/controllers/wishlistController.ts
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);
}
};
40 changes: 40 additions & 0 deletions src/database/migrations/20240426195145-create-category.js
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');
},
};
68 changes: 68 additions & 0 deletions src/database/migrations/20240429115230-create-product.js
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');
},
};
61 changes: 61 additions & 0 deletions src/database/migrations/20240501004030-create-size.js
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');
},
};
Loading

0 comments on commit 6f1e030

Please sign in to comment.