Skip to content

Commit

Permalink
Merge pull request #139 from bluewave-labs/banner-backend
Browse files Browse the repository at this point in the history
Banner backend
  • Loading branch information
erenfn authored Aug 13, 2024
2 parents 235d906 + 910c4ad commit b8d515c
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 19 deletions.
2 changes: 2 additions & 0 deletions backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const userRoutes = require('./src/routes/user.routes');
const mocks = require('./src/routes/mocks.routes');
const popup = require('./src/routes/popup.routes');
const popup_log = require('./src/routes/popup_log.routes');
const banner = require('./src/routes/banner.routes');
// const tourRoutes = require('./src/routes/tour.routes');

const app = express();
Expand Down Expand Up @@ -40,6 +41,7 @@ app.use('/users', userRoutes);
app.use('/mock/', mocks);
app.use('/popup', popup);
app.use('/popup_log', popup_log);
app.use('/banner', banner);
// app.use('/tours', tourRoutes);

app.use((err, req, res, next) => {
Expand Down
166 changes: 166 additions & 0 deletions backend/src/controllers/banner.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
const bannerService = require("../service/banner.service.js");
const { internalServerError } = require("../utils/errors");
const db = require("../models");
const Banner = db.Banner;

const validatePosition = (value) => {
const validPositions = ["top", "bottom"];
return validPositions.includes(value);
};

const validateCloseButtonAction = (value) => {
const validActions = ["no-action", "open-url", "open-url-new-tab"];
return validActions.includes(value);
};

const validateColorCode = (value) => {
const colorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
return colorRegex.test(value);
};

class BannerController {
async addBanner(req, res) {
const userId = req.user.id;
const { position, closeButtonAction, fontColor, backgroundColor } = req.body;

if (!position || !closeButtonAction) {
return res
.status(400)
.json({
errors: [{ msg: "position and closeButtonAction are required" }],
});
}

if (!validatePosition(position) || !validateCloseButtonAction(closeButtonAction)) {
return res
.status(400)
.json({
errors: [{ msg: "Invalid value entered" }],
});
}

const colorFields = { fontColor, backgroundColor };
for (const [field, value] of Object.entries(colorFields)) {
if (value && !validateColorCode(value)) {
return res
.status(400)
.json({
errors: [{ msg: `${field} must be a valid hex color code` }],
});
}
}

try {
const newBannerData = { ...req.body, createdBy: userId };
const newBanner = await bannerService.createBanner(newBannerData);
res.status(201).json(newBanner);
} catch (err) {
console.log(err);
const { statusCode, payload } = internalServerError(
"CREATE_BANNER_ERROR",
err.message,
);
res.status(statusCode).json(payload);
}
}

async deleteBanner(req, res) {
try {
const { id } = req.params;

if (isNaN(id) || id.trim() === "") {
return res.status(400).json({ errors: [{ msg: "Invalid id" }] });
}

const deletionResult = await bannerService.deleteBanner(id);

if (!deletionResult) {
return res
.status(400)
.json({
errors: [{ msg: "Banner with the specified id does not exist" }],
});
}

res
.status(200)
.json({ message: `Banner with ID ${id} deleted successfully` });
} catch (err) {
const { statusCode, payload } = internalServerError(
"DELETE_BANNER_ERROR",
err.message,
);
res.status(statusCode).json(payload);
}
}

async editBanner(req, res) {
try {
const { id } = req.params;

if (!req.body.position || !req.body.closeButtonAction) {
return res
.status(400)
.json({
errors: [{ msg: "position and closeButtonAction are required" }],
});
}

const positionColumn = Banner.tableAttributes.position;

if (!positionColumn.validate.isIn[0].includes(req.body.position)) {
return res
.status(400)
.json({ errors: [{ msg: "Invalid value for position" }] });
}

const closeButtonActionColumn = Banner.tableAttributes.closeButtonAction;

if (
!closeButtonActionColumn.validate.isIn[0].includes(
req.body.closeButtonAction,
)
) {
return res
.status(400)
.json({ errors: [{ msg: "Invalid value for closeButtonAction" }] });
}

const colorFields = ["fontColor", "backgroundColor"];
const colorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
for (const field of colorFields) {
if (req.body[field] && !colorRegex.test(req.body[field])) {
return res
.status(400)
.json({
errors: [{ msg: `${field} must be a valid hex color code` }],
});
}
}

const updatedBanner = await bannerService.updateBanner(id, req.body);
res.status(200).json(updatedBanner);
} catch (err) {
const { statusCode, payload } = internalServerError(
"EDIT_BANNER_ERROR",
err.message,
);
res.status(statusCode).json(payload);
}
}

async getAllBanners(req, res) {
try {
const banners = await bannerService.getAllBanners();
res.status(200).json(banners);
} catch (err) {
const { statusCode, payload } = internalServerError(
"GET_ALL_BANNERS_ERROR",
err.message,
);
res.status(statusCode).json(payload);
}
}
}

module.exports = new BannerController();
52 changes: 52 additions & 0 deletions backend/src/models/Banner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module.exports = (sequelize, DataTypes) => {
const Banner = sequelize.define('Banner', {
closeButtonAction: {
type: DataTypes.STRING,
allowNull: false,
validate: {
isIn: [["no-action", "open-url", "open-url-new-tab"]],
},
},
position: {
type: DataTypes.STRING,
allowNull: false,
validate: {
isIn: [["top", "bottom"]],
},
},
url: {
type: DataTypes.STRING,
allowNull: true,
},
fontColor: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: "#FFFFFF",
},
backgroundColor: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: "#FFFFFF",
},

createdBy: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: "users",
key: "id",
},
},
},
{
tableName: "banners",
timestamps: false,
},
);

Banner.associate = (models) => {
Banner.belongsTo(models.User, { foreignKey: "createdBy", as: "creator" });
};
return Banner;
};

38 changes: 19 additions & 19 deletions backend/src/models/PopupLog.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
module.exports = (sequelize, DataTypes) => {
const PopupLog = sequelize.define('PopupLog', {
popupType: {
type: DataTypes.ENUM('guide', 'tooltip', 'hotspot', 'checklist'),
allowNull: false,
},
userId: {
type: DataTypes.STRING,
allowNull: false,
},
showingTime: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
completed: {
type: DataTypes.BOOLEAN,
defaultValue: false,
}
popupType: {
type: DataTypes.ENUM('guide', 'tooltip', 'hotspot', 'checklist'),
allowNull: false,
},
userId: {
type: DataTypes.STRING,
allowNull: false,
},
showingTime: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
completed: {
type: DataTypes.BOOLEAN,
defaultValue: false,
}
}, {
timestamps: false,
tableName: 'popup_logs'
timestamps: false,
tableName: 'popup_logs'
});
return PopupLog;
return PopupLog;
};
4 changes: 4 additions & 0 deletions backend/src/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ db.User = require("./User.js")(sequelize, Sequelize.DataTypes);
db.Popup = require("./Popup.js")(sequelize, Sequelize.DataTypes);
db.Token = require("./Token.js")(sequelize, Sequelize.DataTypes);
db.PopupLog = require("./PopupLog.js")(sequelize, Sequelize.DataTypes);
db.Banner = require("./Banner.js")(sequelize, Sequelize.DataTypes);

// Define associations here
db.User.hasMany(db.Popup, { foreignKey: "createdBy", as: "popups" });
db.Popup.belongsTo(db.User, { foreignKey: "createdBy", as: "creator" });

db.User.hasMany(db.Banner, { foreignKey: "createdBy", as: "banners" });
db.Banner.belongsTo(db.User, { foreignKey: "createdBy", as: "creator" });

module.exports = db;
12 changes: 12 additions & 0 deletions backend/src/routes/banner.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const express = require("express");
const bannerController = require("../controllers/banner.controller.js");
const authenticateJWT = require("../middleware/auth.middleware");

const router = express.Router();

router.post("/add_banner", authenticateJWT, bannerController.addBanner);
router.delete("/delete_banner/:id", authenticateJWT, bannerController.deleteBanner);
router.put("/edit_banner/:id", authenticateJWT, bannerController.editBanner);
router.get("/all_banners", authenticateJWT, bannerController.getAllBanners);

module.exports = router;
39 changes: 39 additions & 0 deletions backend/src/service/banner.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const db = require("../models");
const Banner = db.Banner;

class BannerService {
async getAllBanners() {
return await Banner.findAll({
include: [{ model: db.User, as: "creator" }],
});
}

async createBanner(data) {
return await Banner.create(data);
}

async deleteBanner(id) {
const rowsAffected = await Banner.destroy({ where: { id } });

if (rowsAffected === 0) {
return false;
}

return true;
}

async updateBanner(id, data) {
const [affectedRows, updatedBanners] = await Banner.update(data, {
where: { id },
returning: true,
});

if (affectedRows === 0) {
return null;
}

return updatedBanners[0];
}
}

module.exports = new BannerService();

0 comments on commit b8d515c

Please sign in to comment.