Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 2x 2x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 17x 17x 17x 8x 8x 8x 8x 8x 6x 7x 2x 2x 8x 8x 8x 8x 8x 8x 17x 17x 17x 17x 1x 1x | export const dynamic = "force-dynamic";
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from "@/lib/prisma";
import {
withErrorHandling,
successResponse,
ApiSuccessResponse,
ApiErrorResponse } from "@/lib/api";
import type { PromotionType } from "@prisma/client";
/**
* GET /api/promotions/active
* Get all active public promotions (for display on promotions page)
*/
async function handleGet(
request: NextRequest
): Promise<NextResponse<ApiSuccessResponse<unknown> | ApiErrorResponse>> {
const searchParams = request.nextUrl.searchParams;
const type = searchParams.get("type") as PromotionType | null;
const limit = parseInt(searchParams.get("limit") || "20");
const now = new Date();
const where: {
isActive: boolean;
startDate: { lte: Date };
endDate: { gte: Date };
type?: PromotionType;
} = {
isActive: true,
startDate: { lte: now },
endDate: { gte: now }};
if (type) {
where.type = type;
}
const promotions = await prisma.promotion.findMany({
where,
select: {
id: true,
name: true,
displayName: true,
description: true,
type: true,
discountType: true,
discountValue: true,
startDate: true,
endDate: true,
minimumPurchase: true,
targetType: true,
targetProducts: {
select: {
product: {
select: {
id: true,
title: true,
images: { select: { url: true }, take: 1 }}}},
take: 5},
targetCategories: {
select: {
category: {
select: {
id: true,
title: true,
slug: true}}}},
flashSaleConfig: {
select: {
headline: true,
subheadline: true,
bannerImage: true,
countdown: true}}},
orderBy: [{ priority: "desc" }, { endDate: "asc" }],
take: limit});
// Add computed fields
const mapped = promotions.map((promo) => {
const endDate = new Date(promo.endDate);
const daysRemaining = Math.ceil((endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
let discountLabel = "";
if (promo.discountType === "PERCENTAGE") {
discountLabel = `${promo.discountValue}% OFF`;
} else {
discountLabel = `$${promo.discountValue.toFixed(2)} OFF`;
}
return {
...promo,
daysRemaining,
discountLabel,
isExpiringSoon: daysRemaining <= 3};
});
return successResponse(mapped);
}
export const GET = withErrorHandling(handleGet);
|