I was working on Product Page with its nested children. On the page we can see a specific product data, add item to the cart and set quantity which we want to add. The codebase is nasty as I only finished, but at least seems everything is working properly, so I’m looking for potential improvements including SOLID and other common patterns which I can use for ProductPage and QuantitySelector.
Screen:
ProductPage:
export const ProductPage = () => {
const [selectedQ, setSelectedQ] = useState<number>();
const [totalPrice, setTotalPrice] = useState<number>();
const { category, name } = useParams();
const { data: products, isLoading } = useGetProductsQuery(category);
const selectedProduct = useMemo(
() =>
Array.isArray(products?.data) &&
products.data.find((product: Product) => product.name === name),
[products, name]
);
const { handleAddToCart } = useAddToCart({
selectedProduct,
selectedQ,
totalPrice,
});
return (
<>
{!isLoading ? (
<>
<Notification />
<Box
display="flex"
flexDirection={{ xs: "column", sm: "column", md: "row" }}
justifyContent="space-between"
marginTop={10}
padding={{
xs: "0 16px 48px 16px",
sm: "0 24px 48px 24px",
md: "0 48px 48px 48px",
}}
>
<Box
width={{ xs: "100%", sm: "100%", md: "50%" }}
display="flex"
alignItems="center"
justifyContent="center"
>
<Box
component="img"
src={selectedProduct.imgURL}
alt={selectedProduct.name}
width={{ xs: "15rem", sm: "18rem", md: "20rem" }}
height={{ xs: "15rem", sm: "18rem", md: "20rem" }}
/>
</Box>
<Box
height="100%"
width={{ xs: "100%", sm: "100%", md: "50%" }}
display="flex"
flexDirection="column"
alignItems={{ xs: "flex-start", sm: "flex-start", md: "center" }}
justifyContent="center"
gap="2rem"
>
<Box
width="100%"
display="flex"
alignItems="center"
justifyContent="space-between"
>
<Typography variant="h1">{selectedProduct.name}</Typography>
<QuantitySelector
quantity={selectedProduct.quantity}
units={selectedProduct.units}
price={selectedProduct.price}
onQuantityChange={(q, totalPrice) => {
setSelectedQ(q);
setTotalPrice(totalPrice);
}}
/>
</Box>
<Typography variant="subtitle1">
{selectedProduct.about}
</Typography>
<Box
width="100%"
display="flex"
alignItems="center"
justifyContent="space-between"
fontWeight={600}
>
<Box display="flex" alignItems="center" gap="1rem">
<span>⭐️</span>
<span>4.5</span>
</Box>
<Box display="flex" alignItems="center" gap="1rem">
<span>????</span>
<span>{selectedProduct.calories} Kcal</span>
</Box>
<Box display="flex" alignItems="center" gap="1rem">
<span>⏰</span>
<span>10-15 Min</span>
</Box>
</Box>
<Categories title="Related Items" />
<Button sx={{ width: "100%" }} onClick={handleAddToCart}>
Add To Cart
</Button>
</Box>
</Box>
</>
) : (
<PageLoader />
)}
</>
);
};
QuantitySelector:
export const QuantitySelector = ({
quantity,
units,
price,
onQuantityChange,
}: QuantitySelectorProps) => {
const theme = useTheme();
const [q, setQ] = useState(quantity);
const [totalPrice, setTotalPrice] = useState(price * (quantity / 1000));
const formattedQuantity = useFormatQuantity(q, units);
useEffect(() => {
setTotalPrice((q / 1000) * price);
onQuantityChange(q, totalPrice);
}, [q, price, onQuantityChange]);
const handleIncrementQ = () =>
units === "g-kg"
? setQ((prevQ) => prevQ + 200)
: setQ((prefQ) => prefQ + 1);
const handleDecrementQ = () => {
if (units === "g-kg" && q > 200) {
setQ((prevQ) => prevQ - 200);
} else if (units !== "g-kg" && q > 1) {
setQ((prevQ) => prevQ - 1);
}
};
return (
<Box display="flex" alignItems="center" justifyContent="center" gap="1rem">
<SelectButton onClick={handleIncrementQ}>
<AddIcon sx={{ fontSize: "1.2rem" }} />
</SelectButton>
<Box
width="3.5rem"
height="3rem"
color={theme.palette.primary.main}
bgcolor="rgba(46, 171, 92, 0.1)"
borderRadius="0.3rem"
display="flex"
alignItems="center"
justifyContent="center"
fontWeight={600}
>
{formattedQuantity}
</Box>
<SelectButton onClick={handleDecrementQ}>
<RemoveIcon sx={{ fontSize: "1.2rem" }} />
</SelectButton>
</Box>
);
};