Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions e-commerce-app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions e-commerce-app/src/api/cartApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,11 @@ export const findProductInCart = (state: RootStateType, productId: string) =>
cartApi.endpoints.getMyActiveCart
.select(state.user.access_token as string)(state)
.data?.lineItems.find((item) => item.productId === productId);

export const getTotalQuantityLineItemsInCart = (state: RootStateType) =>
cartApi.endpoints.getMyActiveCart.select(state.user.access_token as string)(state).data
?.totalLineItemQuantity;

export const getLineItemsInCart = (state: RootStateType) =>
cartApi.endpoints.getMyActiveCart.select(state.user.access_token as string)(state).data
?.lineItems;
33 changes: 33 additions & 0 deletions e-commerce-app/src/components/BasketEmpty/BasketEmpty.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { JSX } from 'react';
import { Box } from '@mui/system';
import Typography from '@mui/material/Typography';
import { Button } from '@mui/material';
import KeyboardBackspaceIcon from '@mui/icons-material/KeyboardBackspace';
import EmptyBasket from '../../assets/images/BasketPageImg.png';
import { useNavigate } from 'react-router-dom';
import { lightGreen } from '@mui/material/colors';

const BasketEmpty = (): JSX.Element => {
const navigate = useNavigate();
return (
<>
<Box className="cart-empty">
<Typography variant="h5" mt="5%">
Your cart is empty
</Typography>
<Button
className="start-shopping"
variant="outlined"
sx={{ backgroundColor: lightGreen[100], color: 'green', mt: '5%' }}
color="success"
onClick={() => navigate('/products')}
>
<KeyboardBackspaceIcon width="20" />
<Box component="span">Start shopping</Box>
</Button>
</Box>
<img src={EmptyBasket} alt="EmptyBasket" width={300} />
</>
);
};
export default BasketEmpty;
132 changes: 132 additions & 0 deletions e-commerce-app/src/components/BasketFull/BasketFull.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { JSX } from 'react';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import Divider from '@mui/material/Divider';
import { Box } from '@mui/system';
import { Button, TextField } from '@mui/material';
import KeyboardBackspaceIcon from '@mui/icons-material/KeyboardBackspace';
import Paper from '@mui/material/Paper';
import { useNavigate } from 'react-router-dom';
import { lightGreen } from '@mui/material/colors';
import { useAppSelector } from '../../store/hooks';
import { getLineItemsInCart, selectCart } from '../../api/cartApi';
import BasketLineItem from '../BasketLineItem/BasketLineItem';
import { ICartApiResponse } from '../../types/slicesTypes/cart';

const BasketFull = (): JSX.Element => {
const navigate = useNavigate();
const cartLineItems = useAppSelector(getLineItemsInCart);
const cart = useAppSelector(selectCart) as ICartApiResponse;

const totalCurrencyEUR = cart.totalPrice.currencyCode;
const totalNumberEUR = cart.totalPrice.centAmount / 10 ** cart.totalPrice.fractionDigits;
const totalPriceEUR = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: totalCurrencyEUR,
}).format(totalNumberEUR);

return (
<Paper
sx={{
p: 2,
margin: 'auto',
maxWidth: 800,
flexGrow: 1,
}}
>
<Grid container className="titles" textAlign="start">
<Grid item xs={7}>
<Typography variant="h6">Product</Typography>
</Grid>
<Grid item xs={1.8}>
<Typography variant="h6">Price</Typography>
</Grid>
<Grid item xs={2.2}>
<Typography variant="h6">Quantity</Typography>
</Grid>
<Grid item xs={1}>
<Typography variant="h6">Total</Typography>
</Grid>
</Grid>
<Divider />

{cartLineItems &&
cartLineItems.map((lineItem) => <BasketLineItem item={lineItem} key={lineItem.id} />)}

<Divider />

<Grid container justifyContent="space-between" mt="5%">
<Grid item xs={12} md={2} mb={3}>
<Button className="clear-cart" sx={{ height: '40px' }} variant="outlined" color="error">
Clear Cart
</Button>
</Grid>
<Grid item xs={12} md={7}>
<Grid container className="cart-checkout">
<Grid className="subtotal" container fontWeight={700} fontSize="large">
<Grid item xs={6} textAlign="left">
Subtotal
</Grid>
<Grid item xs={6} textAlign="right">
{totalPriceEUR}
</Grid>
</Grid>
<Typography mt="3px" fontSize="small">
Taxes and shipping calculated at checkout
</Typography>

<Grid container mt={4} height={30}>
<Grid item xs={9}>
<TextField fullWidth size="small" label="Promo Code" />
</Grid>
<Grid item xs={3}>
<Button sx={{ backgroundColor: 'beige', color: 'green' }}>Apply</Button>
</Grid>
</Grid>

<Grid
className="subtotal"
container
fontWeight={700}
fontSize="20px"
mt={3}
color="green"
>
<Grid item xs={9} textAlign="left">
Discounted Price
</Grid>
<Grid item xs={3} textAlign="right">
19.00
</Grid>
</Grid>

<Grid container mt={2}>
<Grid item xs={12} mb={5}>
<Button
sx={{ width: '100%', backgroundColor: lightGreen[100], mt: '5%' }}
variant="outlined"
color="success"
>
Check out
</Button>
</Grid>
<Grid item xs={12}>
<Button
className="continue-shopping"
sx={{ border: '1px solid grey', fontSize: '10px' }}
color="success"
>
<KeyboardBackspaceIcon width="20" />
<Box component="span" onClick={() => navigate('/products')}>
Continue shopping
</Box>
</Button>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Paper>
);
};
export default BasketFull;
87 changes: 87 additions & 0 deletions e-commerce-app/src/components/BasketLineItem/BasketLineItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { FC } from 'react';
import Grid from '@mui/material/Grid';
import { Box } from '@mui/system';
import { Divider, Stack } from '@mui/material';
import Typography from '@mui/material/Typography';
import CartAddLineItem from '../../requestsComponents/CartAddLineItem/CartAddLineItem';
import CartModifyQuantity from '../../requestsComponents/CartModifyQuantity/CartModifyQuantity';
import { styled } from '@mui/material/styles';
import { ICartLineItem } from '../../types/slicesTypes/cart';
import { Link } from 'react-router-dom';

const Img = styled('img')({
margin: 'auto',
display: 'block',
maxWidth: '100%',
maxHeight: '100%',
});

interface IBasketLineItemProps {
item: ICartLineItem;
}

const BasketLineItem: FC<IBasketLineItemProps> = ({ item }) => {
const currencyEUR = item.price.value.currencyCode;
const numberEUR = item.price.value.centAmount / 10 ** item.price.value.fractionDigits;
const priceEUR = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: currencyEUR,
}).format(numberEUR);

const totalCurrencyEUR = item.totalPrice.currencyCode;
const totalNumberEUR = item.totalPrice.centAmount / 10 ** item.totalPrice.fractionDigits;
const totalPriceEUR = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: totalCurrencyEUR,
}).format(totalNumberEUR);

return (
<>
<Grid container spacing={2} textAlign="start" my={4}>
<Grid item sx={{ display: { xs: 'none', sm: 'block' } }}>
<Box sx={{ width: 128, height: 128 }}>
<Img alt="complex" src={item.variant.images[0].url} />
</Box>
</Grid>
<Grid item xs={12} sm>
<Stack spacing={5}>
<Grid container>
<Grid item xs={6} container direction="column" spacing={2} alignSelf="center" pl={2}>
<Stack spacing={2}>
<Typography gutterBottom variant="subtitle1" component="div">
{item.name.en}
</Typography>
<Link to={`/products/${item.productId}`} style={{ textDecoration: 'none' }}>
<Typography variant="body2">More Details...</Typography>
</Link>
</Stack>
</Grid>

<Grid item xs={2} alignSelf="center">
<Typography variant="subtitle1" component="div">
{priceEUR}
</Typography>
</Grid>

<Grid item xs={2.8} alignSelf="center" display="flex">
<CartModifyQuantity productId={item.productId} />
</Grid>

<Grid item xs={1.2} alignSelf="center">
<Typography variant="subtitle1" component="div" fontWeight="700">
{totalPriceEUR}
</Typography>
</Grid>
</Grid>
<CartAddLineItem
productId={item.productId}
props={{ variant: 'text', color: 'warning' }}
/>
</Stack>
</Grid>
</Grid>
<Divider />
</>
);
};
export default BasketLineItem;
11 changes: 10 additions & 1 deletion e-commerce-app/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import MenuLinks from '../MenuLinks/MenuLinks';
import { navigationRoutes } from '../../routes/navigation';
import UserMenu from '../UserMenu/UserMenu';
import { Link } from 'react-router-dom';
import UserCart from '../UserCart/UserCart';
import { Grid } from '@mui/material';

export const Header: React.FC = () => {
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);
Expand Down Expand Up @@ -107,7 +109,14 @@ export const Header: React.FC = () => {
<NavLinks />
</Box>

<UserMenu />
<Grid container spacing={2} sx={{ maxWidth: 'max-content' }} alignItems={'center'}>
<Grid item>
<UserCart />
</Grid>
<Grid item>
<UserMenu />
</Grid>
</Grid>
</Toolbar>
</Container>
</AppBar>
Expand Down
5 changes: 1 addition & 4 deletions e-commerce-app/src/components/ProductCard/ProductCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ export const ProductCard: FC<ICardProps> = ({ item }) => {
const navigate = useNavigate();

const clickOnCardHandler = (e: React.MouseEvent<HTMLDivElement>) => {
console.log(e.target instanceof HTMLButtonElement);
if (e.target instanceof HTMLButtonElement) {
console.log(`here, add to cart ${item.id}`);
} else {
if (!(e.target instanceof HTMLButtonElement)) {
navigate(`/products/${item.id}`);
}
};
Expand Down
23 changes: 23 additions & 0 deletions e-commerce-app/src/components/UserCart/UserCart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { JSX } from 'react';
import { useAppSelector } from '../../store/hooks';
import { getTotalQuantityLineItemsInCart } from '../../api/cartApi';
import { Badge, IconButton } from '@mui/material';
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
import { useNavigate } from 'react-router-dom';

const UserCart = (): JSX.Element => {
const totalQuantity = useAppSelector(getTotalQuantityLineItemsInCart);
const navigate = useNavigate();
const clickHandler = () => {
navigate('/basket');
};

return (
<IconButton aria-label="cart" onClick={clickHandler}>
<Badge badgeContent={totalQuantity} color="success" invisible={!totalQuantity}>
<ShoppingCartIcon />
</Badge>
</IconButton>
);
};
export default UserCart;
Loading