Skip to content

Commit 19a5beb

Browse files
committed
add shoppingCardList and CartContext
1 parent 8cb1804 commit 19a5beb

File tree

2 files changed

+234
-52
lines changed

2 files changed

+234
-52
lines changed

examples/kendo-react-e-commerce-astro-app/src/components/ShoppingCartList.tsx

Lines changed: 177 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// src/components/ShoppingCartList.tsx
21
import * as React from "react";
32
import {
43
chevronLeftIcon,
@@ -17,7 +16,6 @@ import {
1716
import { Error } from "@progress/kendo-react-labels";
1817
import { Input, NumericTextBox } from "@progress/kendo-react-inputs";
1918
import { Button } from "@progress/kendo-react-buttons";
20-
import { useNavigate } from "react-router-dom";
2119
import { Layout } from "./Layout";
2220
import { Avatar } from "@progress/kendo-react-layout";
2321
import { SvgIcon } from "@progress/kendo-react-common";
@@ -32,70 +30,94 @@ const EmailInput = (fieldRenderProps: FieldRenderProps) => {
3230
);
3331
};
3432

35-
const ShoppingCartList: React.FC = () => {
33+
export const ShoppingCartList: React.FC = () => {
34+
// Example cart data
35+
const cart = [
36+
{
37+
product: {
38+
id: 1,
39+
img: "/shoppingCartImg.png", // Single image for all items
40+
title: "Product 1",
41+
newPrice: 150,
42+
},
43+
quantity: 2,
44+
},
45+
{
46+
product: {
47+
id: 2,
48+
img: "/shoppingCartImg.png", // Single image for all items
49+
title: "Product 2",
50+
newPrice: 100,
51+
},
52+
quantity: 1,
53+
},
54+
];
3655

3756
const onBackClick = () => {
38-
// navigate("/products");
57+
window.location.href = "/products";
3958
};
4059

4160
const onProceedClick = () => {
42-
// navigate("/paymentdetails");
61+
window.location.href = "/paymentdetails";
4362
};
4463

4564
const updateQuantity = (event: any) => {
46-
const target = event.target.element;
47-
const id = target.getAttribute("id");
48-
49-
updateIndividualCartItem(id);
65+
console.log("Quantity updated:", event.target.value);
5066
};
5167

5268
return (
5369
<>
5470
<Layout>
55-
<div className="k-h2 k-font-bold k-text-black k-col-span-12 k-pt-5 ">
71+
<div className="k-h2 k-font-bold k-text-black k-col-span-12 k-pt-5">
5672
Shopping Cart
5773
</div>
5874
<div className="k-pb-5">
59-
<Button svgIcon={chevronLeftIcon} fillMode={"flat"} onClick={onBackClick}>
75+
<Button
76+
svgIcon={chevronLeftIcon}
77+
fillMode={"flat"}
78+
onClick={onBackClick}
79+
>
6080
Back
6181
</Button>
6282
</div>
6383

64-
{cart.map((item) => {
65-
const isCartItem = "quantity" in item;
66-
67-
return (
68-
<div className="k-d-flex k-gap-5 k-justify-content-center k-border-y k-align-items-center k-pb-5" key={isCartItem ? item.product.id : null} style={{ height: "120px" }}>
69-
<img
70-
className="k-rounded-lg"
71-
src={isCartItem ? item.product.img : undefined}
72-
alt={isCartItem ? item.product.title : undefined}
73-
style={{ maxHeight: "120px" }}
74-
/>
75-
<div className="k-d-flex k-justify-content-between k-w-full">
76-
<span>{isCartItem ? item.product.title : null}</span>
77-
<span>{`$${isCartItem ? item.product.newPrice.toLocaleString() : null}`}</span>
78-
<span>
79-
{isCartItem ? (
80-
<NumericTextBox
81-
value={item.quantity}
82-
id={String(item.product.id)}
83-
onChange={updateQuantity}
84-
width={"118px"}
85-
fillMode={"flat"}
86-
/>
87-
) : (
88-
<span>Quantity not available</span>
89-
)}
90-
<Button svgIcon={trashIcon} fillMode={"flat"}></Button>
91-
</span>
92-
<span>{`$${(item.quantity * item.product.newPrice).toLocaleString()}`}</span>
93-
</div>
84+
{cart.map((item) => (
85+
<div
86+
className="k-d-flex k-gap-5 k-justify-content-center k-border-y k-align-items-center k-pb-5"
87+
key={item.product.id}
88+
style={{
89+
height: "120px",
90+
}}
91+
>
92+
<img
93+
className="k-rounded-lg"
94+
src={item.product.img}
95+
alt={item.product.title}
96+
style={{
97+
maxHeight: "120px",
98+
}}
99+
/>
100+
<div className="k-d-flex k-justify-content-between k-w-full">
101+
<span>{item.product.title}</span>
102+
<span>{`$${item.product.newPrice.toLocaleString()}`}</span>
103+
<span>
104+
<NumericTextBox
105+
value={item.quantity}
106+
id={String(item.product.id)}
107+
onChange={updateQuantity}
108+
width={"118px"}
109+
fillMode={"flat"}
110+
/>
111+
<Button svgIcon={trashIcon} fillMode={"flat"}></Button>
112+
</span>
113+
<span>{`$${(
114+
item.quantity * item.product.newPrice
115+
).toLocaleString()}`}</span>
94116
</div>
95-
);
96-
})}
117+
</div>
118+
))}
97119
</Layout>
98-
{cart.length > 0 ? (
120+
{cart.length > 0 && (
99121
<Layout>
100122
<section className="k-d-flex k-justify-content-between k-align-items-center">
101123
<div className="k-col-span-3">
@@ -106,23 +128,56 @@ const ShoppingCartList: React.FC = () => {
106128
<legend className={"k-h2"}>Order Details</legend>
107129
<span>Please, insert your details</span>
108130
<FieldWrapper>
109-
<Field name={"firstName"} component={Input} labelClassName={"k-form-label"} label={"Full Name"} />
131+
<Field
132+
name={"firstName"}
133+
component={Input}
134+
labelClassName={"k-form-label"}
135+
label={"Full Name"}
136+
/>
110137
</FieldWrapper>
138+
111139
<FieldWrapper>
112-
<Field name={"email"} type={"email"} component={EmailInput} label={"Email Address"} />
140+
<Field
141+
name={"email"}
142+
type={"email"}
143+
component={EmailInput}
144+
label={"Email Address"}
145+
/>
113146
</FieldWrapper>
147+
114148
<FieldWrapper>
115-
<Field name={"phone"} component={Input} labelClassName={"k-form-label"} label={"Phone Number"} />
149+
<Field
150+
name={"phone"}
151+
component={Input}
152+
labelClassName={"k-form-label"}
153+
label={"Phone Number"}
154+
/>
116155
</FieldWrapper>
156+
117157
<FieldWrapper>
118-
<Field name={"country"} component={Input} labelClassName={"k-form-label"} label={"Country"} />
158+
<Field
159+
name={"country"}
160+
component={Input}
161+
labelClassName={"k-form-label"}
162+
label={"Country"}
163+
/>
119164
</FieldWrapper>
165+
120166
<FieldWrapper>
121-
<Field name={"street"} component={Input} labelClassName={"k-form-label"} label={"Street Address"} />
167+
<Field
168+
name={"street"}
169+
component={Input}
170+
labelClassName={"k-form-label"}
171+
label={"Street Address"}
172+
/>
122173
</FieldWrapper>
123174
</fieldset>
124175
<div className="k-form-buttons">
125-
<Button themeColor={"primary"} size={"large"} onClick={onProceedClick}>
176+
<Button
177+
themeColor={"primary"}
178+
size={"large"}
179+
onClick={onProceedClick}
180+
>
126181
Proceed to Checkout
127182
</Button>
128183
</div>
@@ -131,11 +186,81 @@ const ShoppingCartList: React.FC = () => {
131186
/>
132187
</div>
133188
<div className="k-col-span-9 k-rounded-lg">
134-
<img src="/shoppingCartImg.png" alt="Shopping Cart Background" style={{ width: "630px", height: "455px" }} />
189+
<img
190+
src="/shoppingCartImg.png"
191+
alt="Shopping Cart Background"
192+
style={{
193+
width: "630px",
194+
height: "455px",
195+
backgroundSize: "cover",
196+
backgroundPosition: "center",
197+
backgroundRepeat: "no-repeat",
198+
}}
199+
/>
135200
</div>
136201
</section>
137202
</Layout>
138-
) : null}
203+
)}
204+
<Layout>
205+
<div className="k-d-flex k-flex-col k-align-items-center k-py-12 k-px-4.5 k-gap-10">
206+
<div className="k-d-flex k-flex-col k-align-items-center k-gap-4 k-text-center">
207+
<h2 className="k-h2 !k-mb-0">Why people choose us?</h2>
208+
</div>
209+
<div className="k-d-grid k-grid-cols-1 k-grid-cols-md-3 k-gap-5">
210+
<div className="k-d-flex k-flex-col k-align-items-center">
211+
<Avatar
212+
rounded="full"
213+
type="icon"
214+
themeColor="primary"
215+
size="large"
216+
>
217+
<SvgIcon icon={walletSolidIcon} size="xxlarge" />
218+
</Avatar>
219+
<p className="k-font-size-xl k-font-bold k-px-4 k-py-3 !k-mb-0">
220+
Return Policy
221+
</p>
222+
<p className="k-p-4 !k-mb-0 k-text-center">
223+
You can return your items within 30 days for a full refund or
224+
exchange.
225+
</p>
226+
</div>
227+
228+
<div className="k-d-flex k-flex-col k-align-items-center">
229+
<Avatar
230+
rounded="full"
231+
type="icon"
232+
themeColor="primary"
233+
size="large"
234+
>
235+
<SvgIcon icon={heartIcon} size="xxlarge" />
236+
</Avatar>
237+
<p className="k-font-size-xl k-font-bold k-px-4 k-py-3 !k-mb-0">
238+
Included Gift Wrapping
239+
</p>
240+
<p className="k-p-4 !k-mb-0 k-text-center">
241+
Add a personalized message and gift wrapping for your order.
242+
</p>
243+
</div>
244+
245+
<div className="k-d-flex k-flex-col k-align-items-center">
246+
<Avatar
247+
rounded="full"
248+
type="icon"
249+
themeColor="primary"
250+
size="large"
251+
>
252+
<SvgIcon icon={percentIcon} size="xxlarge" />
253+
</Avatar>
254+
<p className="k-font-size-xl k-font-bold k-px-4 k-py-3 !k-mb-0 k-text-center">
255+
Discount Code Available
256+
</p>
257+
<p className="k-p-4 !k-mb-0 k-text-center">
258+
Apply your discount code at checkout for exclusive savings.
259+
</p>
260+
</div>
261+
</div>
262+
</div>
263+
</Layout>
139264
</>
140265
);
141266
};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { createContext, useState, useContext, ReactNode } from 'react';
2+
import { CartContextDescriptor, ListDataDescriptor } from '../data/types';
3+
import React from 'react';
4+
5+
interface CartContextType {
6+
cart: CartContextDescriptor[];
7+
addItemToCart: (product: ListDataDescriptor) => void;
8+
updateIndividualCartItem: (cart: CartContextDescriptor) => void;
9+
}
10+
11+
const ShoppingCartContext = createContext<CartContextType | null>(null);
12+
13+
interface CartProviderProps {
14+
children: ReactNode;
15+
}
16+
17+
export const CartProvider = ({ children }: CartProviderProps) => {
18+
const [shoppingCart, setShoppingCart] = useState<CartContextDescriptor[]>([]);
19+
20+
const addItemToCart = (product: ListDataDescriptor) => {
21+
const itemExists = shoppingCart.some(cartItem => cartItem.product.id === product.id);
22+
23+
if (itemExists) {
24+
setShoppingCart(shoppingCart.map(cartItem =>
25+
cartItem.product.id === product.id
26+
? { ...cartItem, quantity: cartItem.quantity + 1 }
27+
: cartItem
28+
));
29+
} else {
30+
setShoppingCart([...shoppingCart, { product, quantity: 1 }]);
31+
}
32+
};
33+
34+
const updateCartItem = React.useCallback((id: any) => {
35+
setShoppingCart(shoppingCart.map(item => {
36+
if (item.product.id === Number(id)) {
37+
return {...item, quantity: item.quantity + 1};
38+
}
39+
40+
return item
41+
}))
42+
}, [shoppingCart]);
43+
44+
return (
45+
<ShoppingCartContext.Provider value={{ cart: shoppingCart, addItemToCart, updateIndividualCartItem: updateCartItem }}>
46+
{children}
47+
</ShoppingCartContext.Provider>
48+
);
49+
};
50+
51+
export const useCart = () => {
52+
const context = useContext(ShoppingCartContext);
53+
if (context === null) {
54+
throw new Error('useCart must be used within a CartProvider');
55+
}
56+
return context;
57+
};

0 commit comments

Comments
 (0)