Skip to content

Commit

Permalink
Merge branch 'main' into feat/order-has-issue
Browse files Browse the repository at this point in the history
  • Loading branch information
ClementNumericite authored Nov 7, 2024
2 parents 2dc68dd + cf22782 commit 4b8c7be
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 124 deletions.
13 changes: 13 additions & 0 deletions webapp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## [0.55.2](https://github.com/SocialGouv/carte-jeune-engage/compare/v0.55.1...v0.55.2) (2024-11-07)


### Bug Fixes

* add tooltip on the order detail page ([bde5490](https://github.com/SocialGouv/carte-jeune-engage/commit/bde5490aa52ab3605e001e53a7bead96e4a904a6))

## [0.55.1](https://github.com/SocialGouv/carte-jeune-engage/compare/v0.55.0...v0.55.1) (2024-11-07)

### Bug Fixes

- history mixed types issue ([9cf172d](https://github.com/SocialGouv/carte-jeune-engage/commit/9cf172de1fb396bf4b5cd6ff324a5cc3a6ca2fac))

# [0.55.0](https://github.com/SocialGouv/carte-jeune-engage/compare/v0.54.3...v0.55.0) (2024-11-07)

### Features
Expand Down
2 changes: 1 addition & 1 deletion webapp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webapp",
"version": "0.55.0",
"version": "0.55.2",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
335 changes: 212 additions & 123 deletions webapp/src/components/obiz/DiscountAmountBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,129 @@ import {
IconButton,
Input,
Text,
Tooltip,
useOutsideClick,
} from "@chakra-ui/react";
import { Dispatch, SetStateAction } from "react";
import { Dispatch, SetStateAction, useMemo, useRef, useState } from "react";
import { HiMiniMinus, HiMiniPlus } from "react-icons/hi2";
import { useDebounceValue } from "usehooks-ts";
import { OfferArticle } from "~/server/types";

type DiscountArticleBlockProps = {
article: OfferArticle;
index: number;
selectedArticles: DiscountAmountBlockFixed["selectedArticles"];
setSelectedArticles: DiscountAmountBlockFixed["setSelectedArticles"];
setAmount: Dispatch<SetStateAction<number>>;
};

const DiscountArticleBlock = ({
article,
setAmount,
selectedArticles,
setSelectedArticles,
index,
}: DiscountArticleBlockProps) => {
const selectedArticle = selectedArticles.find(
(a) => a.article.reference === article.reference
);
const quantity = selectedArticle?.quantity || 0;
let [isMaximumQuantity, setIsMaximumQuantity] = useState(false);
let ref = useRef(null);

useOutsideClick({
ref,
handler: () => setIsMaximumQuantity(false),
});

return (
<Flex key={article.reference} flexDir="column" w="full">
{index === 0 && <Divider borderColor="cje-gray.100" />}
<Flex alignItems="center" mt={2}>
<Flex flexDir="column">
<Text fontWeight={500}>Bon de {article.publicPrice}</Text>
<Text fontSize={14} color="disabled">
Valable jusqu'au{" "}
{new Date(article.validityTo).toLocaleDateString("fr-FR", {
day: "2-digit",
month: "2-digit",
year: "numeric",
})}
</Text>
<Text fontSize={14} fontWeight={700}>
Voir les infos
</Text>
</Flex>
<Flex alignItems="center" ml="auto" gap={2}>
<IconButton
icon={<Icon as={HiMiniMinus} />}
borderRadius="full"
size="xs"
aria-label={`Minus the quantity of ${article.reference}`}
isDisabled={quantity === 0}
onClick={() => {
if (quantity === 0) return;
setIsMaximumQuantity(false);
setSelectedArticles((prev) =>
prev.map((a) =>
a.article.reference === article.reference
? { ...a, quantity: a.quantity - 1 }
: a
)
);
setAmount((prev) => prev - (article.publicPrice as number));
}}
/>
<Box bgColor="bgGray" borderRadius="xl" py={2} px={6}>
<Text fontSize={18} fontWeight={800}>
{quantity}
</Text>
</Box>
<Tooltip
isOpen={quantity === 5 && isMaximumQuantity}
label="Maximum"
bg="error"
borderRadius="2.5xl"
hasArrow
arrowSize={8}
mt={2.5}
placement="bottom-start"
>
<IconButton
size="xs"
aria-label={`Plus the quantity of ${article.reference}`}
icon={<Icon as={HiMiniPlus} />}
borderRadius="full"
isDisabled={quantity === 5 && isMaximumQuantity}
ref={ref}
onClick={() => {
if (quantity === 5) {
setIsMaximumQuantity(true);
return;
}
setSelectedArticles((prev) => [
...prev.filter(
(a) => a.article.reference !== article.reference
),
{
article,
quantity: quantity + 1,
},
]);
setAmount((prev) => prev + (article.publicPrice as number));
}}
/>
</Tooltip>
</Flex>
</Flex>
<Divider
borderColor="cje-gray.100"
mt={quantity === 5 && isMaximumQuantity ? 8 : 2}
/>
</Flex>
);
};

type DefaultProps = {
amount: number;
setAmount: Dispatch<SetStateAction<number>>;
Expand Down Expand Up @@ -52,6 +170,20 @@ const DiscountAmountBlock = (props: DiscountAmountBlockProps) => {
!isDisabled &&
(amount < props.minAmount || amount > props.maxAmount || amount % 1 !== 0);

const [isInvalidDebounce] = useDebounceValue(isInvalid, 250);
const [isDisabledDebounce] = useDebounceValue(isDisabled, 250);

const tooltipText = useMemo(() => {
if (kind === "variable_price" && isInvalid) {
if (amount < props.minAmount)
return `Le montant minimum est ${props.minAmount}€`;
if (amount > props.maxAmount)
return `Le montant maximum est ${props.maxAmount}€`;
if (amount % 1 !== 0) return "Les centimes ne sont pas pris en compte.";
}
return "";
}, [amount]);

return (
<Center flexDir="column">
<FormControl display="flex" flexDir="column" alignItems="center" mx={10}>
Expand All @@ -65,29 +197,40 @@ const DiscountAmountBlock = (props: DiscountAmountBlockProps) => {
>
Choisissez le montant du bon
</FormLabel>
<Input
type="number"
variant="unstyled"
bgColor={isInvalid ? "errorLight" : "bgGray"}
borderRadius="2.5xl"
fontWeight={800}
textAlign="center"
fontSize="52px"
w="200px"
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={kind === "variable_price" ? props.minAmount : 0}
max={kind === "variable_price" ? props.maxAmount : 0}
isDisabled={kind === "fixed_price"}
_disabled={{ opacity: 1 }}
/>
<Tooltip
isOpen={kind === "variable_price" && isInvalidDebounce}
placement="bottom"
label={tooltipText}
bgColor="error"
hasArrow
arrowSize={18}
mt={-1}
borderRadius="2xl"
>
<Input
type="number"
variant="unstyled"
bgColor={isInvalidDebounce ? "errorLight" : "bgGray"}
borderRadius="2.5xl"
fontWeight={800}
textAlign="center"
fontSize="52px"
w="200px"
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={kind === "variable_price" ? props.minAmount : 0}
max={kind === "variable_price" ? props.maxAmount : 0}
isDisabled={kind === "fixed_price"}
_disabled={{ opacity: 1 }}
/>
</Tooltip>
</FormControl>
<Flex alignItems="center" mt={6}>
<Center flexDir="column">
Expand Down Expand Up @@ -119,108 +262,54 @@ const DiscountAmountBlock = (props: DiscountAmountBlockProps) => {
</Flex>
</Center>
<Divider orientation="vertical" h="50px" mx={6} />
<Center flexDir="column">
<Text fontSize={14} fontWeight={500}>
Vous économisez
</Text>
<Text
bgColor={isDisabled ? "black" : "primaryShades.100"}
color={isDisabled ? "white" : "primary"}
borderRadius="2xl"
px={2}
fontSize={24}
fontWeight={800}
opacity={isDisabled ? 0.25 : 1}
>
{(amount * (discount / 100))
.toFixed(isDisabled ? 0 : 2)
.replace(".", ",")}
</Text>
</Center>
<Tooltip
isOpen={
kind === "variable_price" &&
!isDisabledDebounce &&
!isInvalidDebounce
}
placement="bottom"
label="Pas mal !"
hasArrow
arrowSize={18}
borderRadius="2xl"
ml={1.5}
mt={1}
transform="rotate(-5deg)!important"
>
<Center flexDir="column">
<Text fontSize={14} fontWeight={500}>
Vous économisez
</Text>
<Text
bgColor={isDisabled ? "black" : "primaryShades.100"}
color={isDisabled ? "white" : "primary"}
borderRadius="2xl"
px={2}
fontSize={24}
fontWeight={800}
opacity={isDisabled ? 0.25 : 1}
>
{(amount * (discount / 100))
.toFixed(isDisabled ? 0 : 2)
.replace(".", ",")}
</Text>
</Center>
</Tooltip>
</Flex>
{kind === "fixed_price" && (
<Box mt={8} w="full">
{props.articles.map((article, index) => {
const selectedArticle = props.selectedArticles.find(
(a) => a.article.reference === article.reference
);
const quantity = selectedArticle?.quantity || 0;
return (
<Flex key={article.reference} flexDir="column" w="full">
{index === 0 && <Divider borderColor="cje-gray.100" />}
<Flex alignItems="center" mt={2}>
<Flex flexDir="column">
<Text fontWeight={500}>Bon de {article.publicPrice}</Text>
<Text fontSize={14} color="disabled">
Valable jusqu'au{" "}
{new Date(article.validityTo).toLocaleDateString(
"fr-FR",
{
day: "2-digit",
month: "2-digit",
year: "numeric",
}
)}
</Text>
<Text fontSize={14} fontWeight={700}>
Voir les infos
</Text>
</Flex>
<Flex alignItems="center" ml="auto" gap={2}>
<IconButton
icon={<Icon as={HiMiniMinus} />}
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.article.reference === article.reference
? { ...a, quantity: a.quantity - 1 }
: a
)
);
setAmount(
(prev) => prev - (article.publicPrice as number)
);
}}
/>
<Box bgColor="bgGray" borderRadius="xl" py={2} px={6}>
<Text fontSize={18} fontWeight={800}>
{quantity}
</Text>
</Box>
<IconButton
size="xs"
aria-label={`Plus the quantity of ${article.reference}`}
icon={<Icon as={HiMiniPlus} />}
borderRadius="full"
isDisabled={quantity === 5}
onClick={() => {
if (quantity === 5) return;
props.setSelectedArticles((prev) => [
...prev.filter(
(a) => a.article.reference !== article.reference
),
{
article,
quantity: quantity + 1,
},
]);
setAmount(
(prev) => prev + (article.publicPrice as number)
);
}}
/>
</Flex>
</Flex>
<Divider borderColor="cje-gray.100" mt={2} />
</Flex>
);
})}
{props.articles.map((article, index) => (
<DiscountArticleBlock
key={article.reference}
article={article}
selectedArticles={props.selectedArticles}
setSelectedArticles={props.setSelectedArticles}
setAmount={setAmount}
index={index}
/>
))}
</Box>
)}
</Center>
Expand Down

0 comments on commit 4b8c7be

Please sign in to comment.