Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,22 @@ model Notifications {

//------------------------------------BANNERS PROMOCIONALES----------------------------------------
model Banners {
id_banner Int @id @default(autoincrement())
title String @db.VarChar(120)
description String? @db.Text
image_url String @db.VarChar(500)
link_url String? @db.VarChar(500)
start_at DateTime @db.Timestamptz
end_at DateTime? @db.Timestamptz
is_active Boolean @default(true)
status Boolean @default(true)
created_at DateTime @default(now()) @db.Timestamptz
updated_at DateTime @updatedAt @db.Timestamptz
id_banner Int @id @default(autoincrement())
fk_store Int?
title String @db.VarChar(120)
description String? @db.Text
image_url String? @db.VarChar(500)
link_url String? @db.VarChar(500)
start_at DateTime @db.Timestamptz
end_at DateTime? @db.Timestamptz
is_active Boolean @default(true)
approval_status ApprovalStatus @default(PENDING)
rejection_reason String? @db.Text
status Boolean @default(true)
created_at DateTime @default(now()) @db.Timestamptz
updated_at DateTime @updatedAt @db.Timestamptz

store Stores? @relation(fields: [fk_store], references: [id_store])
}

//------------------------------------COMERCIOS----------------------------------------
Expand Down Expand Up @@ -103,6 +108,7 @@ model Stores {
carts Carts[]
order Orders[]
business_hours StoreBusinessHours[]
banner_requests Banners[]
//wishlist_items WishlistItems[]
}

Expand Down
2 changes: 2 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import wishlistRoutes from "./modules/users/wishlist/wishlist.routes.js";
import cartRoutes from "./modules/users/cart/cart.routes.js";

import { orderRouter, userOrderRouter } from "./modules/users/orders/order.routes.js";
import bannerRequestRoutes from "./modules/commerce/banner-requests/banner-request.routes.js";

import { errorHandler } from "./middlewares/errorHandler.js";
import { NotFoundError } from "./lib/errors.js";
Expand Down Expand Up @@ -94,6 +95,7 @@ app.use("/api/commerces", commerceBusinessHoursRoutes);
app.use("/api/deliveries", deliveryRouter);
app.use("/api/stores", storeDeliveryRouter);
app.use("/api/stores", storeDeliveryAssignmentRoutes);
app.use("/api/stores/:storeId/banner-requests", bannerRequestRoutes);

//Desde aqui pueden usarse dos endpoints, para productos /api/categories/products, y /api/categories/stores
//Se encuentra indexado
Expand Down
94 changes: 94 additions & 0 deletions src/docs/schemas/commerce/banner-request.schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* @swagger
* components:
* schemas:
* BannerRequestCreateBody:
* type: object
* required:
* - title
* - startAt
* properties:
* title:
* type: string
* maxLength: 120
* description: Título del banner
* description:
* type: string
* description: Descripción o texto del banner
* imageUrl:
* type: string
* maxLength: 500
* description: URL de la imagen del banner
* linkUrl:
* type: string
* maxLength: 500
* description: Enlace relacionado al banner
* startAt:
* type: string
* format: date-time
* description: Fecha de inicio de la promoción
* endAt:
* type: string
* format: date-time
* nullable: true
* description: Fecha de fin de la promoción (opcional)
*
* BannerRequestResponse:
* type: object
* properties:
* id:
* type: integer
* storeId:
* type: integer
* title:
* type: string
* description:
* type: string
* nullable: true
* imageUrl:
* type: string
* nullable: true
* linkUrl:
* type: string
* nullable: true
* startAt:
* type: string
* format: date-time
* endAt:
* type: string
* format: date-time
* nullable: true
* approvalStatus:
* type: string
* enum: [PENDING, ACTIVE, REJECTED]
* rejectionReason:
* type: string
* nullable: true
* createdAt:
* type: string
* format: date-time
* updatedAt:
* type: string
* format: date-time
*
* BannerRequestListResponse:
* type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/BannerRequestResponse'
* pagination:
* type: object
* properties:
* total:
* type: integer
* page:
* type: integer
* limit:
* type: integer
* totalPages:
* type: integer
*/

export const bannerRequestSchemas = {};
3 changes: 2 additions & 1 deletion src/docs/schemas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export * from "./commerce/product.schema.js";
export * from "./commerce/product-report.schema.js";
export * from "./commerce/review-report.schema.js";
export * from "./commerce/category-request.schema.js";
export * from "./commerce/delivery-assignment.schema.js";
export * from "./commerce/banner-request.schema.js";
export * from "./commerce/delivery-assignment.schema.js";
export * from "./delivery.schema.js";
export * from "./order.schema.js";
export * from "./banner.schema.js";
Expand Down
22 changes: 22 additions & 0 deletions src/modules/admin/banners/admin-banners.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
getAdminBannersService,
toggleAdminBannerActiveService,
updateAdminBannerService,
getBannerRequestsService,
processBannerRequestService,
} from "./admin-banners.service.js";

export const createAdminBanner = async (req, res, next) => {
Expand Down Expand Up @@ -44,3 +46,23 @@ export const toggleAdminBannerActive = async (req, res, next) => {
next(error);
}
};

export const getBannerRequests = async (req, res, next) => {
try {
const { search, approval_status } = req.query;
const result = await getBannerRequestsService({ search, approval_status }, req.pagination);
return res.status(200).json(result);
} catch (error) {
next(error);
}
};

export const processBannerRequest = async (req, res, next) => {
try {
const id = validateId(req.params.id);
const result = await processBannerRequestService(id, req.body);
return res.status(200).json(result);
} catch (error) {
next(error);
}
};
84 changes: 84 additions & 0 deletions src/modules/admin/banners/admin-banners.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
getAdminBanners,
toggleAdminBannerActive,
updateAdminBanner,
getBannerRequests,
processBannerRequest,
} from "./admin-banners.controller.js";

const router = Router();
Expand Down Expand Up @@ -163,4 +165,86 @@ router.put("/:id", authenticate, requireRole(ROLES.ADMIN), updateAdminBanner);
*/
router.patch("/:id/active", authenticate, requireRole(ROLES.ADMIN), toggleAdminBannerActive);

/**
* @swagger
* /api/admin/banners/requests:
* get:
* summary: Listar solicitudes de banners por comercios (Admin)
* tags: [Admin]
* security:
* - cookieAuth: []
* parameters:
* - in: query
* name: approval_status
* schema:
* type: string
* enum: [PENDING, ACTIVE, REJECTED]
* description: Filtrar por estado (por defecto PENDING)
* - in: query
* name: search
* schema:
* type: string
* description: Buscar por título o nombre del comercio
* - in: query
* name: page
* schema:
* type: integer
* - in: query
* name: limit
* schema:
* type: integer
* responses:
* 200:
* description: Lista de solicitudes
* 401:
* description: No autenticado
* 403:
* description: Sin permisos de administrador
*/
router.get("/requests", authenticate, requireRole(ROLES.ADMIN), parsePagination, getBannerRequests);

/**
* @swagger
* /api/admin/banners/requests/{id}:
* patch:
* summary: Aprobar o rechazar solicitud de banner (Admin)
* tags: [Admin]
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: ID de la solicitud de banner
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - decision
* properties:
* decision:
* type: string
* enum: [APPROVE, REJECT]
* rejectionReason:
* type: string
* description: Requerido si decision es REJECT
* responses:
* 200:
* description: Solicitud procesada correctamente
* 400:
* description: Decisión inválida o solicitud ya procesada
* 401:
* description: No autenticado
* 403:
* description: Sin permisos de administrador
* 404:
* description: Solicitud no encontrada
*/
router.patch("/requests/:id", authenticate, requireRole(ROLES.ADMIN), processBannerRequest);

export { router as adminBannersRoutes };
Loading
Loading