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
25 changes: 25 additions & 0 deletions e-commerce-app/src/api/discountCodesApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createApi } from '@reduxjs/toolkit/query/react';
import { fetchBaseQuery } from '@reduxjs/toolkit/dist/query/react';
import { IGetDiscountCodesResponse } from '../types/slicesTypes/DiscountCodesTypes/DiscountCodesApiTypes';

export const discountCodesApi = createApi({
reducerPath: 'discountCodesApi',
baseQuery: fetchBaseQuery({
baseUrl: `${process.env.REACT_APP_CTP_API_URL}/${process.env.REACT_APP_CTP_PROJECT_KEY}/discount-codes`,
}),
endpoints: (build) => ({
getDiscountCodes: build.query<IGetDiscountCodesResponse, string>({
query(token) {
return {
url: '',
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
},
};
},
}),
}),
});

export const { useGetDiscountCodesQuery, useLazyGetDiscountCodesQuery } = discountCodesApi;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { JSX } from 'react';
import { Box } from '@mui/system';
import Grid from '@mui/material/Grid';
import { Alert, Button, TextField } from '@mui/material';
import { Controller, SubmitHandler, useForm } from 'react-hook-form';
import { IAddDiscountCodeCart, ICartApiResponse } from '../../types/slicesTypes/cart';
import { useAppSelector } from '../../store/hooks';
import { getAccessToken } from '../../store/slices/userSlice';
import { selectCart, useUpdateCartMutation } from '../../api/cartApi';
import { IUpdateCartApiObjectRequest } from '../../types/slicesTypes/cart/updateCartApiTypes';

interface IDiscountForm {
discount: string;
}

const BasketDiscountForm = (): JSX.Element => {
const { control, handleSubmit } = useForm<IDiscountForm>();
const accessToken = useAppSelector(getAccessToken) as string;
const cartId = (useAppSelector(selectCart) as ICartApiResponse)?.id as string;
const cartVersion = (useAppSelector(selectCart) as ICartApiResponse)?.version as number;

const [updateCart, { isLoading, isError, error }] = useUpdateCartMutation();
const submitHandler: SubmitHandler<IDiscountForm> = (data) => {
if (!data.discount) return;
const actionObject: IAddDiscountCodeCart = {
action: 'addDiscountCode',
code: data.discount.toUpperCase(),
};
const queryObj: IUpdateCartApiObjectRequest = {
cartId,
token: accessToken,
data: { version: cartVersion, actions: [actionObject] },
};
updateCart(queryObj);
};
return (
<Box component={'form'} width={'100%'} onSubmit={handleSubmit(submitHandler)}>
<Grid container mt={4} height={30}>
<Grid item xs={9}>
<Controller
render={({ field }) => (
<TextField {...field} fullWidth size="small" label="Promo Code" />
)}
name={'discount'}
control={control}
defaultValue={''}
/>
</Grid>
<Grid item xs={3}>
<Button
sx={{ backgroundColor: 'beige' }}
color="success"
variant="outlined"
type={'submit'}
disabled={isLoading}
>
Apply
</Button>
</Grid>
</Grid>
{isError && error && <Alert severity="error">This discount code is not available</Alert>}
</Box>
);
};
export default BasketDiscountForm;
34 changes: 19 additions & 15 deletions e-commerce-app/src/components/BasketFull/BasketFull.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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 { Button } from '@mui/material';
import KeyboardBackspaceIcon from '@mui/icons-material/KeyboardBackspace';
import Paper from '@mui/material/Paper';
import { useNavigate } from 'react-router-dom';
Expand All @@ -12,13 +12,26 @@ import { useAppSelector } from '../../store/hooks';
import { getLineItemsInCart, selectCart } from '../../api/cartApi';
import BasketLineItem from '../BasketLineItem/BasketLineItem';
import { ICartApiResponse } from '../../types/slicesTypes/cart';
import CartClear from '../../requestsComponents/CartClear/CartClear';
import BasketDiscountForm from '../BasketDiscountForm/BasketDiscountForm';

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

const totalCurrencyEUR = cart.totalPrice.currencyCode;

const subTotalNumber =
cart.lineItems.reduce((accum, item) => {
return item.price.value.centAmount * item.quantity + accum;
}, 0) /
10 ** cart.totalPrice.fractionDigits;
const subTotalPriceEUR = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: totalCurrencyEUR,
}).format(subTotalNumber);

const totalNumberEUR = cart.totalPrice.centAmount / 10 ** cart.totalPrice.fractionDigits;
const totalPriceEUR = new Intl.NumberFormat('en-IN', {
style: 'currency',
Expand Down Expand Up @@ -57,9 +70,7 @@ const BasketFull = (): JSX.Element => {

<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>
<CartClear />
</Grid>
<Grid item xs={12} md={7}>
<Grid container className="cart-checkout">
Expand All @@ -68,21 +79,14 @@ const BasketFull = (): JSX.Element => {
Subtotal
</Grid>
<Grid item xs={6} textAlign="right">
{totalPriceEUR}
{subTotalPriceEUR}
</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>
<BasketDiscountForm />

<Grid
className="subtotal"
Expand All @@ -93,10 +97,10 @@ const BasketFull = (): JSX.Element => {
color="green"
>
<Grid item xs={9} textAlign="left">
Discounted Price
EST. TOTAL:
</Grid>
<Grid item xs={3} textAlign="right">
19.00
{totalPriceEUR}
</Grid>
</Grid>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.price {
&__sale {
text-decoration: line-through;
text-decoration-color: gray;
}
&__marked {
color: rgb(221, 65, 65);
}
}
77 changes: 71 additions & 6 deletions e-commerce-app/src/components/BasketLineItem/BasketLineItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import CartModifyQuantity from '../../requestsComponents/CartModifyQuantity/Cart
import { styled } from '@mui/material/styles';
import { ICartLineItem } from '../../types/slicesTypes/cart';
import { Link } from 'react-router-dom';
import styles from './BasketLineItem.module.scss';

const Img = styled('img')({
margin: 'auto',
Expand All @@ -28,7 +29,29 @@ const BasketLineItem: FC<IBasketLineItemProps> = ({ item }) => {
currency: currencyEUR,
}).format(numberEUR);

let discountedNumberEUR = numberEUR;
if (item.price.discounted) {
discountedNumberEUR =
item.price.discounted.value.centAmount / 10 ** item.price.discounted.value.fractionDigits;
}
if (item.discountedPrice) {
discountedNumberEUR =
item.discountedPrice.value.centAmount / 10 ** item.discountedPrice.value.fractionDigits;
}
const discountedPriceEUR = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: currencyEUR,
}).format(discountedNumberEUR);

const totalCurrencyEUR = item.totalPrice.currencyCode;

const subTotalNumberEUR =
(item.price.value.centAmount * item.quantity) / 10 ** item.price.value.fractionDigits;
const subTotalPriceEUR = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: totalCurrencyEUR,
}).format(subTotalNumberEUR);

const totalNumberEUR = item.totalPrice.centAmount / 10 ** item.totalPrice.fractionDigits;
const totalPriceEUR = new Intl.NumberFormat('en-IN', {
style: 'currency',
Expand Down Expand Up @@ -58,19 +81,61 @@ const BasketLineItem: FC<IBasketLineItemProps> = ({ item }) => {
</Grid>

<Grid item xs={2} alignSelf="center">
<Typography variant="subtitle1" component="div">
{priceEUR}
</Typography>
{discountedNumberEUR !== numberEUR ? (
<>
<Typography
className={styles.price__marked}
variant="subtitle1"
component="p"
fontWeight={500}
>
{discountedPriceEUR}
</Typography>
<Typography
className={styles.price__sale}
variant="subtitle1"
component="p"
fontWeight={500}
>
{priceEUR}
</Typography>
</>
) : (
<Typography variant="subtitle1" component="p" fontWeight={500}>
{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>
{subTotalNumberEUR !== totalNumberEUR ? (
<>
<Typography
className={styles.price__marked}
variant="subtitle1"
component="p"
fontWeight={700}
>
{totalPriceEUR}
</Typography>
<Typography
className={styles.price__sale}
variant="subtitle1"
component="p"
fontWeight={700}
>
{subTotalPriceEUR}
</Typography>
</>
) : (
<Typography variant="subtitle1" component="p" fontWeight={700}>
{totalPriceEUR}
</Typography>
)}
</Grid>
</Grid>
<CartAddLineItem
Expand Down
76 changes: 38 additions & 38 deletions e-commerce-app/src/components/Footer/Footer.module.scss
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
@mixin animate($animation,$duration,$method){
animation: $animation $duration $method;
@mixin animate($animation,$duration,$method) {
animation: $animation $duration $method;
}
@mixin keyframes($name){
@keyframes #{$name}{
@content;
}

@mixin keyframes($name) {
@keyframes #{$name}{
@content;
}
}

.footer {
background-color: beige;
background-color: beige;

.footer_container {
justify-content: end;
&_container {
justify-content: end;

&_title {
color: green;
}

.footer_title {
color: green;
}

.span {
color: green;
font-weight: 500;
}
.span {
color: green;
font-weight: 500;
}
}

&_img {
background-image: url(../../assets/images/FooterImg.png);
background-repeat: repeat-x;
background-size: contain;
background-position: center;
width: 140%;
height: 150px;
margin-left: -20%;
overflow: hidden;
transition: all 2s;
@include keyframes(fade-in){
0%{
opacity: 0;
transform: translateX(30px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
@include animate(fade-in, 2s, ease-out);
&_img {
background-image: url(../../assets/images/FooterImg.png);
background-repeat: repeat-x;
background-size: contain;
background-position: center;
height: 150px;
margin-left: -20%;
overflow: hidden;
transition: all 2s;
@include keyframes(fade-in) {
0% {
opacity: 0;
transform: translateX(30px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
@include animate(fade-in, 2s, ease-out);
}
}
Loading