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

export const taxApi = createApi({
reducerPath: 'taxApi',
baseQuery: fetchBaseQuery({
baseUrl: `${process.env.REACT_APP_CTP_API_URL}/${process.env.REACT_APP_CTP_PROJECT_KEY}/tax-categories`,
}),
endpoints: (build) => ({
getAllTaxes: build.query<IGetAllTaxesResponse, string>({
query(token: string) {
return {
url: '',
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
},
};
}
})
})
});

export const { useGetAllTaxesQuery } = taxApi;
123 changes: 65 additions & 58 deletions e-commerce-app/src/pages/ProductPage/ProductPage.module.scss
Original file line number Diff line number Diff line change
@@ -1,82 +1,89 @@
.left {
display: flex;
align-items: center;
flex: 1;
gap: 15px;

.images {
display: flex;
align-items: center;
flex: 1;
min-width: max-content;
gap: 15px;

img {
width: 100%;
height: 100px;
margin-bottom: 15px;
.images {
flex: 1;
min-width: max-content;

img {
width: 100%;
height: 100px;
margin-bottom: 15px;

}
}
}
}

.img {
&_small {
object-fit: contain;
cursor: pointer;
}
&_small {
object-fit: contain;
cursor: pointer;
}

&_big {
width: 100%;
object-fit: contain;
max-height: 50vh;
cursor: pointer;
}
&_big {
width: 100%;
object-fit: contain;
max-height: 50vh;
cursor: pointer;
}
}

.price {
font-weight: 500;
color: green;
font-weight: 500;
color: green;

&_full {
text-decoration: line-through;
color: green;
text-decoration-color: gray;
}
}

.quantity {
display: flex;
align-items: center;
gap: 20px;
display: flex;
align-items: center;
gap: 20px;

button {
background-color: greenyellow;
cursor: pointer;
}
button {
background-color: greenyellow;
cursor: pointer;
}
}

.btn {
button {
width: 250px;
padding: 10px;
display: flex;
justify-content: center;
font-weight: 700;
background-color: green;
color: white;
border: 1px solid green;
border-radius: 10px;
cursor: pointer;
button {
width: 250px;
padding: 10px;
display: flex;
justify-content: center;
font-weight: 700;
background-color: green;
color: white;
border: 1px solid green;
border-radius: 10px;
cursor: pointer;

&:hover {
color: green;
border: 1px solid green;
&:hover {
color: green;
border: 1px solid green;
}
}
}
}

.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 2px solid #000;
max-height: 70vh;
max-width: 70vw;
&__img {
max-height: 60vh;
object-fit: contain;
}
}
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 2px solid #000;
max-height: 70vh;
max-width: 70vw;

&__img {
max-height: 60vh;
object-fit: contain;
}
}
52 changes: 40 additions & 12 deletions e-commerce-app/src/pages/ProductPage/ProductPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { useParams } from 'react-router-dom';
import { useAppSelector } from '../../store/hooks';
import { getAccessToken } from '../../store/slices/userSlice';
import LoadingProgress from '../../components/LoadingProgress/LoadingProgress';
import { getTaxes } from '../../store/slices/taxesSlice';
import { ITaxApiResponse } from '../../types/slicesTypes/taxApiTypes';

const style = {
bgcolor: 'background.paper',
Expand All @@ -29,14 +31,24 @@ const styleArrows = {
export const ProductPage: FC = () => {
const { productId } = useParams();
const authToken = useAppSelector(getAccessToken);
const taxesArray = useAppSelector(getTaxes);

const { data, isSuccess, isLoading, isFetching } = useGetProductByIdQuery({
productId: productId as string,
token: authToken as string,
});

const [tax, setTax] = useState(0);

useEffect(() => {
if (isSuccess) {
console.log(data);
taxesArray
.filter((item) => item.id === data.taxCategory.id)
.flatMap((elem) => elem.rates)
.filter((rate: ITaxApiResponse) => rate.country === 'DE')
.forEach((rate: ITaxApiResponse) => {
setTax(rate.amount);
});
}
}, [isSuccess]);

Expand All @@ -59,18 +71,19 @@ export const ProductPage: FC = () => {
const description = data.masterData.current.metaDescription.en;

const currencyCommon = data.masterData.current.masterVariant.prices[0].value.currencyCode;
const priceNumber =
data.masterData.current.masterVariant.prices[0].value.centAmount /
10 ** data.masterData.current.masterVariant.prices[0].value.fractionDigits;
const priceCommon = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: currencyCommon,
}).format(
data.masterData.current.masterVariant.prices[0].value.centAmount /
10 ** data.masterData.current.masterVariant.prices[0].value.fractionDigits,
);
}).format(priceNumber);

const discountPrice = new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: currencyCommon,
}).format(priceNumber - priceNumber * tax);

console.log(
data.masterData.current.masterVariant.prices[0].value.centAmount,
10 ** data.masterData.current.masterVariant.prices[0].value.fractionDigits,
);
return (
<Grid container px={5} py={7} spacing={2} alignItems={'center'}>
<Grid item xs={12} md={6}>
Expand Down Expand Up @@ -148,9 +161,24 @@ export const ProductPage: FC = () => {
<Grid item xs={12} md={6}>
<Stack spacing={4} className="right">
<Typography variant="h2">{title}</Typography>
<Typography variant="h4" className={styles.price}>
{priceCommon}
</Typography>

{tax !== 0 ? (
<>
<Typography variant="h4" className={styles.price}>
Discount price:{discountPrice}
</Typography>
<Typography variant="h5" className={styles.price_full}>
Full price:{priceCommon}
</Typography>
<Typography variant="h6" className={styles.price}>
Tax: {`${tax * 100} %`}
</Typography>
</>
) : (
<Typography variant="h4" className={styles.price}>
Price:{priceCommon}
</Typography>
)}
<Typography variant="h6" align={'justify'}>
{description}
</Typography>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,44 @@ import LoadingProgress from '../../components/LoadingProgress/LoadingProgress';
import { Outlet } from 'react-router-dom';
import { useGetAllCategoriesQuery } from '../../api/categoriesApi';
import { setCategories } from '../../store/slices/categoriesSlice';
import { useGetAllTaxesQuery } from '../../api/taxApi';
import { setTaxes } from '../../store/slices/taxesSlice';

const CategoriesQuery = (): JSX.Element => {
const accessToken = useAppSelector(getAccessToken);
const dispatch = useAppDispatch();

const { isLoading, isError, isSuccess, data } = useGetAllCategoriesQuery(accessToken as string);
const {
isLoading: isLoadingCategories,
isError: isErrorCategories,
isSuccess: isSuccessCategories,
data: dataCategories,
} = useGetAllCategoriesQuery(accessToken as string);
const {
isLoading: isLoadingTaxes,
isError: isErrorTaxes,
isSuccess: isSuccessTaxes,
data: dataTaxes,
} = useGetAllTaxesQuery(accessToken as string);

useEffect(() => {
if (!isSuccess) return;
if (data && 'results' in data) {
dispatch(setCategories(data));
if (!isSuccessCategories) return;
if (dataCategories && 'results' in dataCategories) {
dispatch(setCategories(dataCategories));
}
}, [isSuccess, data]);
}, [isSuccessCategories, dataCategories]);

if (isLoading || isError) {
useEffect(() => {
if (!isSuccessCategories) return;
if (dataTaxes && 'results' in dataTaxes) {
dispatch(setTaxes(dataTaxes));
}
}, [isSuccessTaxes, dataTaxes]);

if (isLoadingCategories || isErrorCategories || isLoadingTaxes || isErrorTaxes) {
return <LoadingProgress />;
}

return (
<>
<Outlet />
</>
);
return <Outlet />;
};
export default CategoriesQuery;
36 changes: 36 additions & 0 deletions e-commerce-app/src/store/slices/taxesSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ITaxFromSlice } from '../../types/slicesTypes/taxSliceTypes';
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { IGetAllTaxesResponse } from '../../types/slicesTypes/taxApiTypes';
import { RootStateType } from '../store';

const initialState: ITaxFromSlice = {
fetching: false,
limit: 0,
count: 0,
offset: 0,
total: 0,
taxes: [],
};

export const taxesSlice = createSlice({
initialState,
name: 'taxesSlice',
reducers: {
startLoadingTaxes: (state) => {
state.fetching = true;
},
setTaxes: (state, action: PayloadAction<IGetAllTaxesResponse>) => {
state.total = action.payload.total;
state.count = action.payload.count;
state.offset = action.payload.offset;
state.limit = action.payload.limit;
state.taxes = action.payload.results;
state.fetching = false;
}
}
});


export const getTaxes = (state: RootStateType) => state.taxes.taxes;
export const TaxesReducer = taxesSlice.reducer;
export const { startLoadingTaxes, setTaxes } = taxesSlice.actions;
5 changes: 5 additions & 0 deletions e-commerce-app/src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,28 @@ import { productsApi } from '../api/productsApi';
import { categoriesApi } from '../api/categoriesApi';
import { CategoriesReducer } from './slices/categoriesSlice';
import { ProductsReducer } from './slices/productsSlice';
import { taxApi } from '../api/taxApi';
import { TaxesReducer } from './slices/taxesSlice';

export const store = configureStore({
reducer: {
[authApi.reducerPath]: authApi.reducer,
[myCustomerApi.reducerPath]: myCustomerApi.reducer,
[productsApi.reducerPath]: productsApi.reducer,
[categoriesApi.reducerPath]: categoriesApi.reducer,
[taxApi.reducerPath]: taxApi.reducer,
user: UserReducer,
categories: CategoriesReducer,
products: ProductsReducer,
taxes: TaxesReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({}).concat([
authApi.middleware,
myCustomerApi.middleware,
productsApi.middleware,
categoriesApi.middleware,
taxApi.middleware,
]),
});

Expand Down
2 changes: 2 additions & 0 deletions e-commerce-app/src/types/addressesTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ export interface IMyCustomerApiAddressRequest {
export interface IMyCustomerAddressResponse extends IMyCustomerApiAddressRequest {
id: string;
}

export type CountriesType = 'US' | 'DE' | 'CA' | 'PL' | string;
Loading