diff --git a/webapp/src/components/modals/ObizOrderProcessModal.tsx b/webapp/src/components/modals/ObizOrderProcessModal.tsx index 4461fd57..39523aa3 100644 --- a/webapp/src/components/modals/ObizOrderProcessModal.tsx +++ b/webapp/src/components/modals/ObizOrderProcessModal.tsx @@ -1,9 +1,6 @@ import { Box, Button, - Center, - Flex, - Heading, Icon, Modal, ModalBody, @@ -12,7 +9,6 @@ import { Text, } from "@chakra-ui/react"; import { Dispatch, SetStateAction, useState } from "react"; -import LoadingLoader from "~/components/LoadingLoader"; import DiscountAmountBlock from "~/components/obiz/DiscountAmountBlock"; import RecapOrder from "~/components/obiz/RecapOrder"; import BackButton from "~/components/ui/BackButton"; @@ -28,56 +24,91 @@ const ObizOfferVariableContent = ({ amount, setAmount, offer, - article, + articles, createOrder, + selectedArticles, + setSelectedArticles, }: { - step: "amount" | "summary" | "payment"; - setStep: Dispatch>; + step: Steps; + setStep: Dispatch>; amount: number; setAmount: Dispatch>; - article: OfferArticle; + articles: OfferArticle[]; + selectedArticles?: { reference: string; quantity: number }[]; + setSelectedArticles?: Dispatch< + SetStateAction<{ reference: string; quantity: number }[]> + >; offer: OfferIncluded; createOrder: () => void; }) => { switch (step) { case "amount": - const minimumPrice = article.minimumPrice ?? 0; - const maximumPrice = article.maximumPrice ?? 1000; - const isDisabled = - amount === 0 || - amount < minimumPrice || - amount > maximumPrice || - amount % 1 !== 0; + if (articles.length === 1 && articles[0].kind === "variable_price") { + const article = articles[0]; + const minimumPrice = article.minimumPrice ?? 0; + const maximumPrice = article.maximumPrice ?? 1000; + const isDisabled = + amount === 0 || + amount < minimumPrice || + amount > maximumPrice || + amount % 1 !== 0; - return ( - <> - - - - - - ); + return ( + <> + + + + + + ); + } else { + if (!selectedArticles || !setSelectedArticles) return null; + return ( + <> + + + + + + ); + } case "summary": if (!offer) return null; return ( <> @@ -114,13 +145,18 @@ type ObizOrderProcessModalProps = { offerId: number; }; +type Steps = "amount" | "summary" | "payment"; + export default function ObizOrderProcessModal( props: ObizOrderProcessModalProps ) { const { isOpen, onClose, offerId } = props; const [amount, setAmount] = useState(0); - const [step, setStep] = useState<"amount" | "summary" | "payment">("amount"); + const [step, setStep] = useState("amount"); + const [selectedArticles, setSelectedArticles] = useState< + { reference: string; quantity: number }[] + >([]); const { mutate: createTestOrder } = api.order.createOrder.useMutation({ onMutate: () => setStep("payment"), @@ -137,9 +173,8 @@ export default function ObizOrderProcessModal( if (!offer || !offer.articles) return; const availableArticles = offer.articles.filter((a) => !!a.available); - const article = availableArticles.find((a) => a.kind === "variable_price"); - if (!article) return; + if (availableArticles.length === 0) return; return ( @@ -161,14 +196,16 @@ export default function ObizOrderProcessModal( amount={amount} setAmount={setAmount} offer={offer} - article={article} + articles={availableArticles} createOrder={() => { createTestOrder({ offer_id: offer.id, - article_reference: article.reference, + article_reference: availableArticles[0].reference, input_value: amount, }); }} + selectedArticles={selectedArticles} + setSelectedArticles={setSelectedArticles} /> diff --git a/webapp/src/components/obiz/DiscountAmountBlock.tsx b/webapp/src/components/obiz/DiscountAmountBlock.tsx index 1b942172..96e6c69a 100644 --- a/webapp/src/components/obiz/DiscountAmountBlock.tsx +++ b/webapp/src/components/obiz/DiscountAmountBlock.tsx @@ -1,31 +1,56 @@ import { + Box, Center, Divider, Flex, FormControl, FormLabel, + Icon, + IconButton, Input, Text, } from "@chakra-ui/react"; import { Dispatch, SetStateAction } from "react"; +import { HiMiniMinus, HiMiniPlus } from "react-icons/hi2"; +import { OfferArticle } from "~/server/types"; -const DiscountAmountBlock = ({ - amount, - setAmount, - minAmount, - maxAmount, - discount, -}: { +type DefaultProps = { amount: number; setAmount: Dispatch>; +}; + +interface DiscountAmountBlockVariable extends DefaultProps { + kind: "variable_price"; minAmount: number; maxAmount: number; discount: number; -}) => { +} + +interface DiscountAmountBlockFixed extends DefaultProps { + kind: "fixed_price"; + articles: OfferArticle[]; + selectedArticles: { reference: string; quantity: number }[]; + setSelectedArticles: Dispatch< + SetStateAction<{ reference: string; quantity: number }[]> + >; +} + +type DiscountAmountBlockProps = + | DiscountAmountBlockVariable + | DiscountAmountBlockFixed; + +const DiscountAmountBlock = (props: DiscountAmountBlockProps) => { + const { kind, amount, setAmount } = props; + + const discount = + kind === "variable_price" + ? props.discount + : props.articles[0].reductionPercentage; const isDisabled = amount === 0; const isInvalid = + kind === "variable_price" && !isDisabled && - (amount < minAmount || amount > maxAmount || amount % 1 !== 0); + (amount < props.minAmount || amount > props.maxAmount || amount % 1 !== 0); return (
@@ -52,12 +77,16 @@ const DiscountAmountBlock = ({ py={6} autoComplete="off" step={1} + autoFocus={kind === "variable_price"} + value={kind === "fixed_price" ? amount : undefined} onChange={(e) => setAmount(e.target.value ? Number(e.target.value) : 0) } placeholder="0€" - min={minAmount} - max={maxAmount} + min={kind === "variable_price" ? props.minAmount : 0} + max={kind === "variable_price" ? props.maxAmount : 0} + isDisabled={kind === "fixed_price"} + _disabled={{ opacity: 1 }} /> @@ -110,6 +139,90 @@ const DiscountAmountBlock = ({
+ {kind === "fixed_price" && ( + + {props.articles.map((article, index) => { + const selectedArticle = props.selectedArticles.find( + (a) => a.reference === article.reference + ); + const quantity = selectedArticle?.quantity || 0; + return ( + + {index === 0 && } + + + Bon de {article.publicPrice}€ + + Valable jusqu'au{" "} + {new Date(article.validityTo).toLocaleDateString( + "fr-FR", + { + day: "2-digit", + month: "2-digit", + year: "numeric", + } + )} + + + Voir les infos + + + + } + borderRadius="full" + size="xs" + aria-label={`Minus the quantity of ${article.reference}`} + isDisabled={quantity === 0} + onClick={() => { + if (quantity === 0) return; + props.setSelectedArticles((prev) => + prev.map((a) => + a.reference === article.reference + ? { ...a, quantity: a.quantity - 1 } + : a + ) + ); + setAmount( + (prev) => prev - (article.publicPrice as number) + ); + }} + /> + + + {quantity} + + + } + borderRadius="full" + isDisabled={quantity === 5} + onClick={() => { + if (quantity === 5) return; + props.setSelectedArticles((prev) => [ + ...prev.filter( + (a) => a.reference !== article.reference + ), + { + reference: article.reference, + quantity: quantity + 1, + }, + ]); + setAmount( + (prev) => prev + (article.publicPrice as number) + ); + }} + /> + + + + + ); + })} + + )} ); };