Skip to content

Commit

Permalink
feat: coupon api security & design disable button
Browse files Browse the repository at this point in the history
  • Loading branch information
ClementNumericite committed Nov 26, 2024
1 parent 9df9670 commit 02b4134
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 13 deletions.
1 change: 1 addition & 0 deletions webapp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### Bug Fixes

- router push dashboard before ([4651d31](https://github.com/SocialGouv/carte-jeune-engage/commit/4651d31d7a442a549565c2756f3d07bcd5169892))

* wallet history button disappear when wallet is empty ([f0477c4](https://github.com/SocialGouv/carte-jeune-engage/commit/f0477c44279b5ddb5d85deff09d5eb095cf97574))

## [0.70.25](https://github.com/SocialGouv/carte-jeune-engage/compare/v0.70.24...v0.70.25) (2024-11-22)
Expand Down
27 changes: 16 additions & 11 deletions webapp/src/components/offer/page/OfferContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import {
import { push } from "@socialgouv/matomo-next";
import Image from "next/image";
import { useMemo, useRef, useState } from "react";
import { HiBookmark, HiMiniEye, HiOutlineBookmark } from "react-icons/hi2";
import {
HiArrowPathRoundedSquare,
HiBookmark,
HiMiniEye,
HiOutlineBookmark,
} from "react-icons/hi2";
import { getItemsTermsOfUse } from "~/payload/components/CustomSelectTermsOfUse";
import { CouponIncluded } from "~/server/api/routers/coupon";
import { OfferIncluded } from "~/server/api/routers/offer";
Expand Down Expand Up @@ -94,29 +99,29 @@ const OfferContent = (props: OfferContentProps) => {
) : (
<Button
fontSize={14}
isDisabled={!!cooldownInMinutes}
w="full"
colorScheme={cooldownInMinutes ? "gray" : "blackBtn"}
size="md"
isLoading={isLoadingValidateOffer}
onClick={() => {
push(["trackEvent", "Inactive", "J'active mon offre"]);
handleValidateOffer(offer.id);
}}
leftIcon={
cooldownInMinutes ? undefined : (
cooldownInMinutes ? (
<Icon as={HiArrowPathRoundedSquare} w={5} h={5} />
) : (
<Icon as={HiMiniEye} w={5} h={5} />
)
}
lineHeight={"xl"}
style={{
pointerEvents: cooldownInMinutes ? "none" : "auto",
}}
>
{cooldownInMinutes ? (
<>
Nouveau code disponible dans <br />{" "}
{formatMinutesDisplay(cooldownInMinutes)}
</>
) : (
"Voir mon code"
)}
{cooldownInMinutes
? `Prochain code dans ${formatMinutesDisplay(cooldownInMinutes)}`
: "Voir mon code"}
</Button>
)}
</Box>
Expand Down
48 changes: 47 additions & 1 deletion webapp/src/server/api/routers/coupon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { Coupon, Media, Offer, Partner, User } from "~/payload/payload-types";
import { createTRPCRouter, userProtectedProcedure } from "~/server/api/trpc";
import { payloadWhereOfferIsValid } from "~/utils/tools";
import { isOlderThan24Hours, payloadWhereOfferIsValid } from "~/utils/tools";

export interface CouponIncluded extends Coupon {
offer: Offer & { partner: Partner & { icon: Media } };
Expand Down Expand Up @@ -67,6 +67,52 @@ export const couponRouter = createTRPCRouter({
message: "Offer not found",
});

const userOfferCoupon = await ctx.payload.find({
collection: "coupons",
sort: "-usedAt",
where: {
and: [
{ offer: { equals: offer_id } },
{ user: { equals: ctx.session.id } },
],
},
});

if (!!userOfferCoupon.docs.length) {
if (currentOffer.cumulative) {
const hasUnusedCoupon = userOfferCoupon.docs.some(
(coupon) => !coupon.used
);
if (hasUnusedCoupon) {
throw new TRPCError({
code: "FORBIDDEN",
message:
"User must used his coupon before taking another one on a cumulative offer",
});
}

const lastUsedCoupon = userOfferCoupon.docs.find(
(coupon) => coupon.used
);
if (
lastUsedCoupon &&
lastUsedCoupon.assignUserAt &&
!isOlderThan24Hours(lastUsedCoupon.assignUserAt)
) {
throw new TRPCError({
code: "FORBIDDEN",
message: "User last used coupon is not older than 24 hours",
});
}
} else {
throw new TRPCError({
code: "FORBIDDEN",
message:
"User can't take several coupons on a non cumulative offer",
});
}
}

let availableCoupon;

if (currentOffer.kind === "voucher" || currentOffer.kind === "code") {
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/utils/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export const formatMinutesDisplay = (minutes: number) => {
const remainingMinutes = minutes % 60;

if (hours > 0) {
return `${hours} heure${hours > 1 ? "s" : ""} ${remainingMinutes > 0 ? `et ${remainingMinutes} minute${remainingMinutes > 1 ? "s" : ""}` : ""}`;
return `${hours}h${remainingMinutes > 0 ? `${remainingMinutes}min` : ""}`;
}

return `${remainingMinutes} minute${remainingMinutes > 1 ? "s" : ""}`;
Expand Down

0 comments on commit 02b4134

Please sign in to comment.