Skip to content

Commit

Permalink
Merge pull request #90 from atlp-rwanda/ft-order-management
Browse files Browse the repository at this point in the history
ft(order): A buyer and seller  should be able to watch their orders
  • Loading branch information
teerenzo authored Jul 19, 2024
2 parents e9d0346 + f51a1b8 commit 0df8206
Show file tree
Hide file tree
Showing 13 changed files with 372 additions and 4 deletions.
48 changes: 48 additions & 0 deletions src/controllers/getOrderController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Request, Response } from "express"
import { getOrderBuyer, updateOrderStatusService } from "../services/OrderService"
import * as mailService from "../services/mail.service";
import { SUBJECTS, UserId } from "../types";
import { orderStatusTemplate } from "../email-templates/orderStatus";
import Order from "../sequelize/models/orders";
import User from "../sequelize/models/users";


export const getOrderController = async (req: Request, res: Response) => {
try {
const response = await getOrderBuyer(req, res)
return res.status(200).json({
status: 200,
length: response?.length,
orders: response?.length === 0? "You have not order yet" : response
})
} catch (error) {
console.log(error);
res.status(500).json({
status: 500,
message: `error accured during fetching order ${error}`
})
}
}

export const updateOrderStatus = async (req: Request, res: Response) => {

try {
const { id: userId} = req.user as UserId;
const { id: orderId } = req.params;
const { status } = req.body;
const buyerId = await Order.findByPk(orderId)
const dataValues = await User.findByPk(buyerId?.buyerId)

const updatedItems = await updateOrderStatusService(userId, orderId, status);
if (updatedItems) {
// @ts-ignore
await mailService.sendNotification(dataValues?.dataValues.email, SUBJECTS.ORDER_STATUS_UPDATED, orderStatusTemplate(dataValues?.dataValues.username, buyerId?.id, buyerId?.createdAt, status))

return res.json({ message: 'Order items status updated', updatedItems });
}

} catch (error: any) {
res.status(500).json({ message: error.message });
}
};

74 changes: 74 additions & 0 deletions src/docs/orders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { required } from "joi";

export const StatusSchema = {
type: "object",
properties: {
status: {
type: "string",
},
},
};

export const buyerAndSellerOrder = {
tags: ["orders"],
security: [{ bearerAuth: [] }],
summary: "Orders for seller and buyers",
responses: {
200: {
description: "Success",
},
400: {
description: "Bad request",
},
401: {
description: "Unauthorized",
},
500: {
description: "Internal server error",
},
},
};

export const statusUpdate = {
tags: ["orders"],
security: [{ bearerAuth: [] }],
summary: "Status update for seller",
parameters: [
{
name: "id",
in: "path",
required: true,
description: "order Id",
schema: {
type: "number",
},
},
],
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
status: {
type: "string",
},
},
required: ["status"],
},
},
},
},
responses: {
201: {
description: "Upgrade successfully",
},
404: {
description: "Review not found",
},
500: {
description: "Internal server error.",
},
},
};
11 changes: 10 additions & 1 deletion src/docs/swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ import { homepage } from "./home";
import { payment } from "./payments";
import { createReviewProduct, deleteReview, getReviewProduct, reviewSchema, updateReviewProduct } from "./reviews";
import { getAdProducts } from "./products";
import { PrivateChatSchema, getAllUserPrivateChats, getUserToUserPrivateMessages, createPrivateChat } from "./privateChatDoc";
import {PrivateChatSchema, getAllUserPrivateChats, getUserToUserPrivateMessages, createPrivateChat } from "./privateChatDoc"
import { StatusSchema, buyerAndSellerOrder, statusUpdate } from "./orders";

const docRouter = express.Router();

Expand Down Expand Up @@ -79,6 +80,7 @@ const options = {
{ name: "Carts", description: "Endpoints related to Cart" },
{ name: "Payments", description: "Endpoints related to payments" },
{ name: "PrivateChat", description: "Endpoints related to Private Chat" },
{name: "orders", description: "Endpoints related to orders"}
],

paths: {
Expand Down Expand Up @@ -198,6 +200,12 @@ const options = {
post: createPrivateChat,
get: getUserToUserPrivateMessages,
},
"/api/v1/orders": {
get: buyerAndSellerOrder
},
"/api/v1/orders/{id}/status": {
patch: statusUpdate
}
},
components: {
schemas: {
Expand All @@ -211,6 +219,7 @@ const options = {
Wish: wishSchema,
Review: reviewSchema,
PrivateChat: PrivateChatSchema,
status: StatusSchema
},
securitySchemes: {
bearerAuth: {
Expand Down
63 changes: 63 additions & 0 deletions src/email-templates/orderStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
export const orderStatusTemplate = (username: string, orderId: number, createdAt: Date, status: string) => {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Order Status</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Poppins', sans-serif;
background-color: #f8f9fa;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
width: 80%;
max-width: 400px;
margin: auto;
padding: 30px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
text-align: center;
}
.title {
color: #333333;
font-size: 24px;
margin-bottom: 20px;
}
.content {
color: #666666;
font-size: 16px;
line-height: 1.6;
margin-bottom: 20px;
}
.status {
color: black;
}
.footer {
font-style: italic;
color: #999999;
}
.status{
font-weigth: 800;
}
</style>
</head>
<body>
<div class="container">
<h1 class="title">Order Status Updated 😍</h1>
<p class="content">Dear ${username}, Your product status with this order id #${orderId} has been successfully to <span class="status"> ${status}</span> .</p>
<div class="status">Status updated at ${createdAt}</div> <br />
<div class="footer">I hope you liked our services.</div>
</div>
</body>
</html>
`;
};
2 changes: 2 additions & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import cartRoutes from "./cartRoutes";
import notificationRoutes from "./notificationRoutes";
import paymentRouter from "./paymentRoutes";
import PrivateChatRoutes from "./privateChatRoutes";
import orderRouter from "./orderRoute";
const appROutes = Router();

appROutes.use("/chats", PrivateChatRoutes);
Expand All @@ -19,4 +20,5 @@ appROutes.use("/messages", joinChatRoomRoutes);
appROutes.use("/carts", cartRoutes);
appROutes.use("/notifications", notificationRoutes);
appROutes.use("/payment", paymentRouter);
appROutes.use("/orders", orderRouter)
export default appROutes;
12 changes: 12 additions & 0 deletions src/routes/orderRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import express from "express"
import { getOrderController, updateOrderStatus, } from "../controllers/getOrderController";
import { isLoggedIn } from "../middlewares/isLoggedIn";
import { isAseller } from "../middlewares/sellerAuth";


const orderRouter = express.Router()

orderRouter.get('/', isLoggedIn, getOrderController)
orderRouter.patch("/:id/status", isLoggedIn, updateOrderStatus)

export default orderRouter;
6 changes: 6 additions & 0 deletions src/schemas/signUpSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ export const profileSchemas = Joi.object({
country: Joi.string()
.optional()
})

export const OrderStatus = Joi.object({
status: Joi.string()
.valid("Pending", "Delivered", "Cancelled")
.required()
})
31 changes: 31 additions & 0 deletions src/sequelize/migrations/x20240711191430-modify-order-items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module.exports = {
async up(queryInterface, Sequelize) {
return Promise.all([
queryInterface.addColumn(
'orderItems',
'userId',
{
type: Sequelize.INTEGER,
allowNull: true,
},
),
queryInterface.addColumn(
'orderItems',
'status',
{
type: Sequelize.ENUM,
allowNull: false,
values: ["Pending", "Delivered", "Cancelled"],
defaultValue: 'Pending'
},
),
]);
},

down(queryInterface, Sequelize) {
return Promise.all([
queryInterface.removeColumn('orderItems', 'status'),
queryInterface.removeColumn('orderItems', 'userId'),
]);
},
};
8 changes: 8 additions & 0 deletions src/sequelize/models/orderItems.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { DataTypes, Model } from "sequelize";
import sequelize from "../../config/dbConnection";
import Product from "./products";
import Order from "./orders";
import User from "./users"
import Profile from "./profiles";


export interface orderItemAttributes {
Expand Down Expand Up @@ -56,5 +59,10 @@ OrderItem.belongsTo(Product, {
foreignKey: "productId",
as: "product"
});
OrderItem.belongsTo(User,{
foreignKey: "userId",
as: "user"
})


export default OrderItem;
6 changes: 5 additions & 1 deletion src/sequelize/models/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Model, DataTypes } from "sequelize";
import sequelize from "../../config/dbConnection";
import User from "./users";
import OrderItem from "./orderItems";
import Product from "./products";


export interface OrderAttributes {
Expand Down Expand Up @@ -38,7 +39,7 @@ Order.init({
},
status: {
type: DataTypes.ENUM,
values: ["pending", "delivered", "canceled"],
values: ["Pending", "Delivered", "Cancelled"],
defaultValue: "pending",
allowNull: false,
},
Expand All @@ -62,5 +63,8 @@ Order.init({
Order.belongsTo(User, { foreignKey: "buyerId", as: "buyer" });
User.hasMany(Order, { foreignKey: "buyerId", as: "orders" });
Order.hasMany(OrderItem, { foreignKey: "orderId", as: "items" });
Order.hasMany(OrderItem, { foreignKey: "status", as: "statuses"})
Order.hasMany(Product, { foreignKey: "id", as: "products"})
OrderItem.belongsTo(Order, { foreignKey: 'orderId', as: 'order' });

export default Order
Loading

0 comments on commit 0df8206

Please sign in to comment.