-
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
14 changed files
with
751 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,71 @@ | ||
/* 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,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'); | ||
}, | ||
}; |
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,42 @@ | ||
import { Model, Optional, DataTypes, UUIDV4 } from 'sequelize'; | ||
import sequelize from './index'; | ||
|
||
interface CategoryAttributes { | ||
id: number; | ||
name: string; | ||
description?: string; | ||
} | ||
|
||
export interface CategoryCreationAttributes extends Optional<CategoryAttributes, 'id'> {} | ||
|
||
export class Category extends Model<CategoryAttributes, CategoryCreationAttributes> implements CategoryAttributes { | ||
public id!: number; | ||
public name!: string; | ||
public description!: string; | ||
public sizes!: string[]; | ||
} | ||
|
||
Category.init( | ||
{ | ||
id: { | ||
type: DataTypes.UUID, | ||
defaultValue: UUIDV4, | ||
primaryKey: true, | ||
autoIncrement: true, | ||
}, | ||
name: { | ||
type: DataTypes.STRING, | ||
allowNull: false, | ||
}, | ||
description: { | ||
type: DataTypes.TEXT, | ||
allowNull: true, | ||
}, | ||
}, | ||
{ | ||
sequelize, | ||
modelName: 'Category', | ||
tableName: 'categories', | ||
timestamps: true, | ||
} | ||
); |
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,78 @@ | ||
import { Model, DataTypes } from 'sequelize'; | ||
import sequelize from './index'; | ||
import { UUIDV4 } from 'sequelize'; | ||
import { Category } from './Category'; | ||
import { Size } from './Size'; | ||
|
||
export interface ProductAttributes { | ||
id?: string; | ||
sellerId: string; | ||
name: string; | ||
description: string; | ||
images: string[]; | ||
colors?: string[]; | ||
categoryId: string; | ||
createdAt?: Date; | ||
updatedAt?: Date; | ||
} | ||
|
||
export class Product extends Model<ProductAttributes> implements ProductAttributes { | ||
public id!: string; | ||
public sellerId!: string; | ||
public name!: string; | ||
public description!: string; | ||
public categoryId!: string; | ||
public images!: string[]; | ||
public colors!: string[]; | ||
public readonly createdAt!: Date | undefined; | ||
public readonly updatedAt!: Date | undefined; | ||
} | ||
|
||
Product.init( | ||
{ | ||
id: { | ||
type: DataTypes.UUID, | ||
defaultValue: UUIDV4, | ||
primaryKey: true, | ||
unique: true, | ||
}, | ||
name: { | ||
type: DataTypes.STRING, | ||
allowNull: false, | ||
}, | ||
description: { | ||
type: DataTypes.TEXT, | ||
allowNull: false, | ||
}, | ||
colors: { | ||
type: DataTypes.ARRAY(DataTypes.STRING), | ||
allowNull: true, | ||
}, | ||
images: { | ||
type: DataTypes.ARRAY(DataTypes.STRING), | ||
allowNull: false, | ||
}, | ||
sellerId: { | ||
type: DataTypes.UUID, | ||
references: { | ||
model: 'Users', | ||
key: 'id', | ||
}, | ||
onDelete: 'CASCADE', | ||
onUpdate: 'CASCADE', | ||
allowNull: false, | ||
}, | ||
categoryId: { | ||
type: DataTypes.UUID, | ||
references: { | ||
model: 'Category', | ||
key: 'id', | ||
}, | ||
}, | ||
}, | ||
{ sequelize: sequelize, timestamps: true, modelName: 'Product', tableName: 'products' } | ||
); | ||
|
||
Product.belongsTo(Category, { foreignKey: 'categoryId' }); | ||
|
||
Product.hasMany(Size, { foreignKey: 'productId' }); |
Oops, something went wrong.