Skip to content

Commit 2913710

Browse files
authored
Video-26-Checkout-Wizard (#26)
1 parent 540754b commit 2913710

10 files changed

Lines changed: 227 additions & 32 deletions

File tree

README.md

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -230,71 +230,70 @@ JS AMAZONA
230230
5. create profile update api in backend
231231
6. create isAuth in utils.js and use in update profile
232232
7. implement sign out
233-
26. Create Checkout Wizard Header Component
234-
1. create component
235-
2. style component
236-
27. Shipping Screen
237-
1. create ShippingScreen.js
238-
2. style elements
239-
3. handle form submit
240-
28. Payment Screen
241-
1. create PaymentScreen.js
242-
2. style elements
243-
3. handle form submit
244-
29. PlaceOrder Screen UI
233+
26. Checkout Wizard
234+
1. create CheckoutSteps.js
235+
2. create div elements for step 1 to 4
236+
3. create redirectUser() in utils.js
237+
4. copy profile screen and as shipping screen
238+
5. use CheckoutStep
239+
6. define getShipping and setShipping
240+
7. copy shipping screen and as payment screen
241+
8. define getPayment and setPayment
242+
9. redirect user to PlaceOrder.js
243+
27. PlaceOrder Screen UI
245244
1. create PlaceOrder.js
246245
2. style elements
247-
30. PlaceOrder Screen Action
246+
28. PlaceOrder Screen Action
248247
1. handle form submit
249248
2. create backend api to create order
250-
31. Order Screen
249+
29. Order Screen
251250
1. create OrderScreen.js
252251
2. style elements
253-
32. PayPal Payment
252+
30. PayPal Payment
254253
1. add paypal checkout script
255254
2. show paypal button
256255
3. update order after payment
257-
33. User Profile UI
256+
31. User Profile UI
258257
1. create ProfileScreen.js
259258
2. style elements
260-
34. User Profile Data
259+
32. User Profile Data
261260
1. Create profile info backend api
262261
2. Create user orders api
263262
3. Call apis in the backend
264-
35. Update Profile
263+
33. Update Profile
265264
1. handle form submit
266265
2. send request to backend
267266
3. create api to update profile
268-
36. Admin Products UI
267+
34. Admin Products UI
269268
1. create Admin Order menu in header
270269
2. create ProductListScreen.js
271270
3. show products with edit and delete button
272271
4. show create product button
273272
5. implement create product backend
274273
6. redirect user to edit product screen
275-
37. Edit Product
274+
35. Edit Product
276275
1. create ProductListScreen.js
277276
2. load product data from backend
278277
3. handle form submit
279278
4. save product in backend
280-
38. Delete Product
279+
36. Delete Product
281280
1. update ProductListScreen.js
282281
2. handle delete button
283282
3. rerender after deletion
284-
39. Admin Orders
283+
37. Admin Orders
285284
1. create Admin Order menu in header
286285
2. create AdminOrder.js
287286
3. load orders from backend
288287
4. list them in the screen
289288
5. show delete and edit button
290289
6. redirect to order details on edit action
291-
40. Edit Order
290+
38. Edit Order
292291
1. if order is payed show deliver button for admin
293292
2. handle click on deliver button
294293
3. set state to delivered
295-
41. Delete Order
294+
39. Delete Order
296295
1. update OrderListScreen.js
297296
2. handle delete button
298297
3. rerender after deletion
299-
42. Publish heroku
298+
40. Publish heroku
300299
1. publish steps
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const CheckoutSteps = {
2+
render: (props) => {
3+
return `
4+
<div class="checkout-steps">
5+
<div class="${props.step1 ? 'active' : ''}">Signin</div>
6+
<div class="${props.step2 ? 'active' : ''}">Shipping</div>
7+
<div class="${props.step3 ? 'active' : ''}">Payment</div>
8+
<div class="${props.step4 ? 'active' : ''}">Place Order</div>
9+
</div>
10+
`;
11+
},
12+
};
13+
export default CheckoutSteps;

frontend/src/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import SigninScreen from './srceens/SigninScreen';
77
import Header from './components/Header';
88
import RegisterScreen from './srceens/RegisterScreen';
99
import ProfileScreen from './srceens/ProfileScreen';
10+
import ShippingScreen from './srceens/ShippingScreen';
11+
import PaymentScreen from './srceens/PaymentScreen';
1012

1113
const routes = {
1214
'/': HomeScreen,
@@ -16,6 +18,8 @@ const routes = {
1618
'/signin': SigninScreen,
1719
'/register': RegisterScreen,
1820
'/profile': ProfileScreen,
21+
'/shipping': ShippingScreen,
22+
'/payment': PaymentScreen,
1923
};
2024
const router = async () => {
2125
showLoading();

frontend/src/localStorage.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,37 @@ export const getUserInfo = () => {
3535
? JSON.parse(localStorage.getItem('userInfo'))
3636
: { name: '', email: '', password: '' };
3737
};
38+
export const getShipping = () => {
39+
const shipping = localStorage.getItem('shipping')
40+
? JSON.parse(localStorage.getItem('shipping'))
41+
: {
42+
address: '',
43+
city: '',
44+
postalCode: '',
45+
country: '',
46+
};
47+
return shipping;
48+
};
49+
export const setShipping = ({
50+
address = '',
51+
city = '',
52+
postalCode = '',
53+
country = '',
54+
}) => {
55+
localStorage.setItem(
56+
'shipping',
57+
JSON.stringify({ address, city, postalCode, country })
58+
);
59+
};
60+
61+
export const getPayment = () => {
62+
const payment = localStorage.getItem('payment')
63+
? JSON.parse(localStorage.getItem('payment'))
64+
: {
65+
paymentMethod: 'paypal',
66+
};
67+
return payment;
68+
};
69+
export const setPayment = ({ paymentMethod = 'paypal' }) => {
70+
localStorage.setItem('payment', JSON.stringify({ paymentMethod }));
71+
};
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { getUserInfo, setPayment } from '../localStorage';
2+
import CheckoutSteps from '../components/CheckoutSteps';
3+
4+
const PaymentScreen = {
5+
after_render: () => {
6+
document
7+
.getElementById('payment-form')
8+
.addEventListener('submit', async (e) => {
9+
e.preventDefault();
10+
const paymentMethod = document.querySelector(
11+
'input[name="payment-method"]:checked'
12+
).value;
13+
setPayment({ paymentMethod });
14+
document.location.hash = '/placeorder';
15+
});
16+
},
17+
render: () => {
18+
const { name } = getUserInfo();
19+
if (!name) {
20+
document.location.hash = '/';
21+
}
22+
return `
23+
${CheckoutSteps.render({ step1: true, step2: true, step3: true })}
24+
<div class="form-container">
25+
<form id="payment-form">
26+
<ul class="form-items">
27+
<li>
28+
<h1>Payment</h1>
29+
</li>
30+
<li>
31+
<div>
32+
<input type="radio"
33+
name="payment-method"
34+
id="paypal"
35+
value="Paypal"
36+
checked />
37+
<label for="paypal" >PayPal</label>
38+
</div>
39+
</li>
40+
<li>
41+
<div>
42+
<input type="radio"
43+
name="payment-method"
44+
id="stripe"
45+
value="Stripe"
46+
/>
47+
<label for="stripe" >Stripe</label>
48+
</div>
49+
</li>
50+
<li>
51+
<button type="submit" class="primary">Continue</button>
52+
</li>
53+
</ul>
54+
</form>
55+
</div>
56+
`;
57+
},
58+
};
59+
export default PaymentScreen;

frontend/src/srceens/RegisterScreen.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { register } from '../api';
22
import { getUserInfo, setUserInfo } from '../localStorage';
3-
import { showLoading, hideLoading, showMessage } from '../utils';
3+
import { showLoading, hideLoading, showMessage, redirectUser } from '../utils';
44

55
const RegisterScreen = {
66
after_render: () => {
@@ -19,13 +19,13 @@ const RegisterScreen = {
1919
showMessage(data.error);
2020
} else {
2121
setUserInfo(data);
22-
document.location.hash = '/';
22+
redirectUser();
2323
}
2424
});
2525
},
2626
render: () => {
2727
if (getUserInfo().name) {
28-
document.location.hash = '/';
28+
redirectUser();
2929
}
3030
return `
3131
<div class="form-container">
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { getUserInfo, getShipping, setShipping } from '../localStorage';
2+
import CheckoutSteps from '../components/CheckoutSteps';
3+
4+
const ShippingScreen = {
5+
after_render: () => {
6+
document
7+
.getElementById('shipping-form')
8+
.addEventListener('submit', async (e) => {
9+
e.preventDefault();
10+
setShipping({
11+
address: document.getElementById('address').value,
12+
city: document.getElementById('city').value,
13+
postalCode: document.getElementById('postalCode').value,
14+
country: document.getElementById('country').value,
15+
});
16+
document.location.hash = '/payment';
17+
});
18+
},
19+
render: () => {
20+
const { name } = getUserInfo();
21+
if (!name) {
22+
document.location.hash = '/';
23+
}
24+
const { address, city, postalCode, country } = getShipping();
25+
return `
26+
${CheckoutSteps.render({ step1: true, step2: true })}
27+
<div class="form-container">
28+
<form id="shipping-form">
29+
<ul class="form-items">
30+
<li>
31+
<h1>Shipping</h1>
32+
</li>
33+
<li>
34+
<label for="address">Adress</label>
35+
<input type="text" name="address" id="address" value="${address}" />
36+
</li>
37+
<li>
38+
<label for="city">City</label>
39+
<input type="text" name="city" id="city" value="${city}" />
40+
</li>
41+
<li>
42+
<label for="postalCode">Postal Code</label>
43+
<input type="text" name="postalCode" id="postalCode" value="${postalCode}" />
44+
</li>
45+
<li>
46+
<label for="country">Country</label>
47+
<input type="text" name="country" id="country" value="${country}" />
48+
</li>
49+
50+
<li>
51+
<button type="submit" class="primary">Continue</button>
52+
</li>
53+
</ul>
54+
</form>
55+
</div>
56+
`;
57+
},
58+
};
59+
export default ShippingScreen;

frontend/src/srceens/SigninScreen.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { signin } from '../api';
22
import { getUserInfo, setUserInfo } from '../localStorage';
3-
import { showLoading, hideLoading, showMessage } from '../utils';
3+
import { showLoading, hideLoading, showMessage, redirectUser } from '../utils';
44

55
const SigninScreen = {
66
after_render: () => {
@@ -18,13 +18,13 @@ const SigninScreen = {
1818
showMessage(data.error);
1919
} else {
2020
setUserInfo(data);
21-
document.location.hash = '/';
21+
redirectUser();
2222
}
2323
});
2424
},
2525
render: () => {
2626
if (getUserInfo().name) {
27-
document.location.hash = '/';
27+
redirectUser();
2828
}
2929
return `
3030
<div class="form-container">

frontend/src/utils.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { getCartItems } from './localStorage';
2+
13
export const parseRequestUrl = () => {
24
const url = document.location.hash.toLowerCase();
35
const request = url.split('/');
@@ -38,3 +40,11 @@ export const showMessage = (message, callback) => {
3840
}
3941
});
4042
};
43+
export const redirectUser = () => {
44+
console.log(getCartItems().length);
45+
if (getCartItems().length !== 0) {
46+
document.location.hash = '/shipping';
47+
} else {
48+
document.location.hash = '/';
49+
}
50+
};

frontend/style.css

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ footer {
256256
.form-container {
257257
display: flex;
258258
justify-content: center;
259-
align-items: center;
259+
align-items: flex-start;
260260
height: 100%;
261261
}
262262
.form-items {
@@ -277,3 +277,20 @@ footer {
277277
.form-container h1 {
278278
font-size: 2.5rem;
279279
}
280+
/* Checkout */
281+
.checkout-steps {
282+
display: flex;
283+
justify-content: space-between;
284+
width: 40rem;
285+
margin: 1rem auto;
286+
}
287+
.checkout-steps > div {
288+
border-top: 0.3rem #c0c0c0 solid;
289+
color: #c0c0c0;
290+
flex: 1 1;
291+
padding-top: 1rem;
292+
}
293+
.checkout-steps > div.active {
294+
color: #f08000;
295+
border-top-color: #f08000;
296+
}

0 commit comments

Comments
 (0)