-
+ {label &&
}
+
+
+
+ {cartItems.map(item => {
+ return (
+
+ )
+ })}
+
+
+
Subtotal
+
{`${subTotal}€`}
+
+
+
Shipping
+
{`${shippingCost}€`}
+
+
+
Total
+
{`${subTotal + shippingCost}€`}
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/Overview/index.js b/src/components/Overview/index.js
new file mode 100644
index 00000000..0025d5d4
--- /dev/null
+++ b/src/components/Overview/index.js
@@ -0,0 +1 @@
+export { default } from "./Overview"
\ No newline at end of file
diff --git a/src/components/Overview/overview.scss b/src/components/Overview/overview.scss
new file mode 100644
index 00000000..ea43b291
--- /dev/null
+++ b/src/components/Overview/overview.scss
@@ -0,0 +1,8 @@
+.wrapperOverview {
+ width: 40%;
+}
+.imgProduct {
+ width: 35%;
+ height: auto;
+ border-radius: 20px;
+}
diff --git a/src/components/OverviewContextProvider/OverviewContextProvider.js b/src/components/OverviewContextProvider/OverviewContextProvider.js
new file mode 100644
index 00000000..62ca250b
--- /dev/null
+++ b/src/components/OverviewContextProvider/OverviewContextProvider.js
@@ -0,0 +1,25 @@
+import React from "react"
+import { OverviewContext } from "../../context/OverviewContext"
+import { getCartTotal } from "../Cart/Cart";
+
+export default function OverviewContextProvider({
+ children,
+ setCartItems,
+ cartItems,
+ handleChange,
+ handleRemove }) {
+ const subTotal = getCartTotal(cartItems)
+ return (
+
+ {children}
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/OverviewContextProvider/index.js b/src/components/OverviewContextProvider/index.js
new file mode 100644
index 00000000..14df3312
--- /dev/null
+++ b/src/components/OverviewContextProvider/index.js
@@ -0,0 +1 @@
+export { default } from "./OverviewContextProvider"
\ No newline at end of file
diff --git a/src/components/ProgressBar/ProgressBar.js b/src/components/ProgressBar/ProgressBar.js
new file mode 100644
index 00000000..20434ef1
--- /dev/null
+++ b/src/components/ProgressBar/ProgressBar.js
@@ -0,0 +1,43 @@
+import React, { useContext } from "react";
+
+// context
+import { CheckoutContext } from "../../context/CheckoutContext";
+// styles
+import "./progressBar.scss";
+
+const STEPS = ["Account", "Shipping", "Payment", "Review"]
+
+
+export default function ProgressBar() {
+ const { setStep, actualStep } = useContext(CheckoutContext)
+
+ const stepClassBtn = (stepNum) => {
+ if (actualStep > stepNum) return "mnoButton stepButton stepCompleted"
+ if (actualStep === stepNum) return "mnoButton stepButton stepActive"
+ return "mnoButton stepButton"
+ }
+ return (
+
+
+ {STEPS.map((step, index) => {
+ const stepNum = index + 1
+ return (
+
+
+
{step}
+
+ )
+ })}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/ProgressBar/index.js b/src/components/ProgressBar/index.js
new file mode 100644
index 00000000..f13ef6fe
--- /dev/null
+++ b/src/components/ProgressBar/index.js
@@ -0,0 +1 @@
+export { default } from "./ProgressBar"
\ No newline at end of file
diff --git a/src/components/ProgressBar/progressBar.scss b/src/components/ProgressBar/progressBar.scss
new file mode 100644
index 00000000..700eec0d
--- /dev/null
+++ b/src/components/ProgressBar/progressBar.scss
@@ -0,0 +1,56 @@
+@use "../../assets/styles/variables.scss" as v;
+@use "../../assets/styles/layout.scss" as l;
+.progressBar {
+ width: 50%;
+ height: 10em;
+ position: relative;
+ .stepSecondaryLine,
+ .stepLine {
+ border-radius: 10px;
+ transition: 1.5s;
+ content: "";
+ position: absolute;
+ height: 10px;
+ z-index: -1;
+ }
+ .stepSecondaryLine {
+ background-color: gray;
+ right: 10px;
+ width: 95%;
+ top: 15px;
+ }
+
+ .stepLine {
+ background-color: v.$skyBlue;
+ }
+ //progress width
+ .progress-1 {
+ width: 0%;
+ }
+ .progress-2 {
+ width: 33%;
+ }
+ .progress-3 {
+ width: 66%;
+ }
+ .progress-4 {
+ width: 100%;
+ }
+}
+.stepButton {
+ border-radius: 9999px;
+ border: 2px solid v.$lightPurpledBlue;
+ font-size: 1.5rem;
+ width: 1.8em;
+ background-color: #ffff;
+}
+.stepCompleted {
+ transition: 1s;
+ color: white;
+ background-color: v.$blue;
+}
+.stepActive {
+ font-size: 30px;
+ transition: 1s;
+ font-weight: 550;
+}
diff --git a/src/components/ReviewCheckout/ReviewCheckout.js b/src/components/ReviewCheckout/ReviewCheckout.js
new file mode 100644
index 00000000..5203f1ae
--- /dev/null
+++ b/src/components/ReviewCheckout/ReviewCheckout.js
@@ -0,0 +1,122 @@
+import React, { useContext, useState } from "react"
+import Button from "../Button"
+import ReviewProductItem from "../ReviewProductItem/ReviewProductItem"
+import { CheckoutContext } from "../../context/CheckoutContext"
+import { OverviewContext } from "../../context/OverviewContext"
+
+
+export function ReviewCheckout() {
+ const [saveUserInfo, setSaveUserInfo] = useState(false)
+ const {
+ personalInfo,
+ billingAddress,
+ payment = null,
+ orderID,
+ setCheckoutDone
+ } = useContext(CheckoutContext)
+ const {
+ cartItems,
+ subTotal,
+ shippingCost,
+ taxes
+ } = useContext(OverviewContext)
+ const totalCalculated = subTotal + shippingCost + taxes
+ const typePayment = payment.method === "credit" ? payment.creditCard : payment.method
+ const lastNumbersCard = payment.cardNumber
+ .substring(payment.cardNumber.length - 4, payment.cardNumber.length)
+
+ return (
+
+
+
+
Order confirmed!
+
Hi {personalInfo.name},
+
Your order was confirmed and will be shipping soon.
+
+ setSaveUserInfo(!saveUserInfo)}
+ />
+ Do you want to save your data for future purchases?
+
+
+
+
+
+
+
+ Order Date
+
+
+ {new Date().toLocaleDateString()}
+
+
+
+
+ Order number:
+
+
+ {orderID}
+
+
+
+
+ Payment
+
+
+ {`${typePayment} - ${lastNumbersCard}`
+ }
+
+
+
+
+ Address
+
+
+ {billingAddress.address}
+
+
+
+
+ {cartItems.map(item => (
+
+
+ ))}
+
+
+
+
Sub total
+
{subTotal}€
+
+
+
Express shipping
+
{shippingCost}€
+
+
+
+
+
Total
+
{totalCalculated}€
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/ReviewCheckout/index.js b/src/components/ReviewCheckout/index.js
new file mode 100644
index 00000000..35438249
--- /dev/null
+++ b/src/components/ReviewCheckout/index.js
@@ -0,0 +1 @@
+export { ReviewCheckout } from "./ReviewCheckout"
\ No newline at end of file
diff --git a/src/components/ReviewProductItem/ReviewProductItem.js b/src/components/ReviewProductItem/ReviewProductItem.js
new file mode 100644
index 00000000..6572c3a7
--- /dev/null
+++ b/src/components/ReviewProductItem/ReviewProductItem.js
@@ -0,0 +1,27 @@
+import React from "react";
+
+import "./reviewProductItem.scss"
+
+export default function ReviewProductItem({
+ img,
+ title,
+ price,
+ quantity,
+}) {
+ return (
+
+
+
+

+
+
+
{title}
+
+
+
+
{`Units:${quantity}`}
+
{`${price}€`}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/ReviewProductItem/index.js b/src/components/ReviewProductItem/index.js
new file mode 100644
index 00000000..4f5dfbf1
--- /dev/null
+++ b/src/components/ReviewProductItem/index.js
@@ -0,0 +1 @@
+export { default } from "./ReviewProductItem";
\ No newline at end of file
diff --git a/src/components/ReviewProductItem/reviewProductItem.scss b/src/components/ReviewProductItem/reviewProductItem.scss
new file mode 100644
index 00000000..de9ac27a
--- /dev/null
+++ b/src/components/ReviewProductItem/reviewProductItem.scss
@@ -0,0 +1,48 @@
+.productReview {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ .productMainInfo {
+ width: 50%;
+ display: flex;
+ gap: 2.5em;
+ }
+ .priceInfo {
+ font-size: 1.1rem;
+ width: 30%;
+ display: flex;
+ justify-content: space-between;
+ }
+}
+
+.productTitle {
+ align-self: center;
+}
+.productImage {
+ width: 35%;
+ img {
+ width: 100%;
+ height: auto;
+ border: 1px solid black;
+ border-radius: 5px;
+ overflow: hidden;
+ }
+}
+
+@media screen and (max-width: 800px) {
+ .productImage {
+ width: 100%;
+ }
+ .productMainInfo {
+ width: 50%;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ }
+ .priceInfo {
+ width: 30%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ }
+}
diff --git a/src/context/CheckoutContext.js b/src/context/CheckoutContext.js
new file mode 100644
index 00000000..54c597b8
--- /dev/null
+++ b/src/context/CheckoutContext.js
@@ -0,0 +1,14 @@
+import { createContext } from "react";
+
+export const CheckoutContext = createContext({
+ setFormInfo: () => { },
+ setStep: () => { },
+ setCheckoutDone: () => { },
+ orderID: "",
+ personalInfo: {},
+ billingAddress: {},
+ payment: {
+ cardNumber: 0
+ },
+ actualStep: 1
+})
\ No newline at end of file
diff --git a/src/context/CreditCardContext.js b/src/context/CreditCardContext.js
new file mode 100644
index 00000000..a122e308
--- /dev/null
+++ b/src/context/CreditCardContext.js
@@ -0,0 +1,11 @@
+import { createContext } from "react"
+
+// implement type of card :D
+export const CreditCardContext = createContext({
+ cardType: "",
+ cardHolderName: "",
+ cardNumber: "**** **** **** ****",
+ expireDate: "MM/YY",
+ cvv: "***",
+ flippedCard: false
+})
\ No newline at end of file
diff --git a/src/context/OverviewContext.js b/src/context/OverviewContext.js
new file mode 100644
index 00000000..04301fd9
--- /dev/null
+++ b/src/context/OverviewContext.js
@@ -0,0 +1,11 @@
+import { createContext } from "react";
+
+export const OverviewContext = createContext({
+ handleRemove: () => { },
+ handleChange: () => { },
+ setCartItems: () => { },
+ subTotal: "",
+ cartItems: [],
+ shippingCost: 0,
+ taxes: 0
+})
\ No newline at end of file
diff --git a/src/helper/regex.js b/src/helper/regex.js
new file mode 100644
index 00000000..1395d114
--- /dev/null
+++ b/src/helper/regex.js
@@ -0,0 +1,22 @@
+export const expirationDateFormat = (value) => {
+ const formatedValue = value.replace(
+ /^([1-9]\/|[2-9])$/g, '0$1/' // 3 > 03/
+ ).replace(
+ /^(0[1-9]|1[0-2])$/g, '$1/' // 11 > 11/
+ ).replace(
+ /^([0-1])([3-9])$/g, '0$1/$2' // 13 > 01/3
+ ).replace(
+ /^(0?[1-9]|1[0-2])([0-9]{2})$/g, '$1/$2' // 141 > 01/41
+ ).replace(
+ /^([0]+)\/|[0]+$/g, '0' // 0/ > 0 and 00 > 0
+ ).replace(
+ /[^\d/]|^[/]*$/g, '' // To allow only digits and `/`
+ ).replace(
+ /\/\//g, '/' // Prevent entering more than 1 `/`
+ );
+
+ return formatedValue
+
+}
+
+export const phoneRegex = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/;
\ No newline at end of file
diff --git a/src/pages/Checkout/Checkout.js b/src/pages/Checkout/Checkout.js
new file mode 100644
index 00000000..6c9362f6
--- /dev/null
+++ b/src/pages/Checkout/Checkout.js
@@ -0,0 +1,46 @@
+import React, { useContext } from "react";
+import { Route, Redirect } from "react-router-dom";
+
+// styles
+import "./checkout.scss"
+
+// steps import
+import { PersonalForm, BillingForm, PaymentForm } from "../../components/CheckoutForms"
+
+import { ReviewCheckout } from "../../components/ReviewCheckout";
+
+import withLayout from "../../hoc/withLayout";
+import ProgressBar from "../../components/ProgressBar";
+import Overview from "../../components/Overview/Overview";
+import { CheckoutContext } from "../../context/CheckoutContext";
+import { OverviewContext } from "../../context/OverviewContext";
+
+
+
+
+function Checkout() {
+ const { cartItems } = useContext(OverviewContext)
+ const { actualStep } = useContext(CheckoutContext)
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {cartItems.length === 0 && }
+ {actualStep <= 2 && }
+
+
+ >
+
+ )
+}
+
+export default withLayout(Checkout)
\ No newline at end of file
diff --git a/src/pages/Checkout/checkout.scss b/src/pages/Checkout/checkout.scss
new file mode 100644
index 00000000..ec8319da
--- /dev/null
+++ b/src/pages/Checkout/checkout.scss
@@ -0,0 +1,172 @@
+@use "../../assets/styles/variables.scss" as v;
+@use "../../assets/styles/layout.scss" as l;
+
+.checkoutHeader {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+}
+
+.checkoutFormWrapper {
+ width: 100%;
+ .formBtnWrapper {
+ display: flex;
+ justify-content: space-between;
+ }
+
+ section {
+ width: 100%;
+ }
+}
+
+form {
+ width: 30%;
+ .selectContainer {
+ margin: 30px 0;
+ }
+ .formButton {
+ color: white;
+ background-color: v.$purple;
+ border-radius: 5px;
+ border: none;
+ padding: 5px;
+ justify-self: center;
+ &:disabled {
+ opacity: 0.5;
+ }
+ &:hover {
+ transition: 0.5;
+ opacity: 0.8;
+ }
+ }
+ .backLink {
+ color: inherit;
+ font-size: 14px;
+ text-align: center;
+ }
+ .cancelFormBtn:hover {
+ text-decoration: underline;
+ }
+}
+// personal form phone input
+.phoneInput {
+ margin: 20px 0;
+
+ input {
+ width: 100% !important;
+ }
+}
+
+// pay methods
+.paymentSection {
+ width: 45%;
+ .formTitle {
+ text-align: center;
+ }
+}
+.paymentFormWrapper {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ gap: 5.5em;
+ .paymentForm {
+ width: 30%;
+ display: flex;
+ flex-direction: column;
+ }
+}
+.methodsContainer {
+ display: flex;
+ flex-direction: row;
+ gap: 20px;
+}
+.secretFields {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ input {
+ width: 50%;
+ }
+}
+.conditionsContainer {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ font-size: 12px;
+ #conditions {
+ align-self: flex-start;
+ width: 20px;
+ }
+}
+@media screen and (max-width: 1000px) {
+ .methodsContainer {
+ flex-direction: column;
+ }
+ .paymentFormWrapper {
+ flex-direction: column;
+ align-items: center;
+ .paymentForm {
+ width: 40%;
+ }
+ }
+}
+
+// review component
+.reviewContainer {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+}
+.reviewSection {
+ border-radius: 5px;
+ width: 50% !important;
+ display: flex;
+ flex-direction: column;
+ padding: 30px;
+ border: 1px solid black;
+ background-color: rgb(255, 255, 255);
+ .orderConfirmed {
+ h1 {
+ color: v.$lightBlue;
+ margin-bottom: 10px;
+ }
+ .saveInfoCheckbox {
+ width: 20px;
+ height: 20px;
+ margin-right: 5px;
+ }
+ }
+ .orderInfo {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ }
+ .orderItem {
+ display: block;
+ .orderItemTitle {
+ color: #adb3bc;
+ }
+ }
+ .reviewProducts {
+ margin: 10px 0;
+ border-top: 1px solid rgb(221, 221, 221);
+ padding-top: 10px;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ }
+ .subtotalSummary {
+ border-top: 1px solid rgb(221, 221, 221);
+ border-bottom: 1px solid rgb(221, 221, 221);
+ }
+ .subtotal,
+ .shippingCost,
+ .taxes,
+ .totalContainer {
+ display: flex;
+ justify-content: space-between;
+ }
+ .totalContainer {
+ margin-top: 15px;
+ }
+}
diff --git a/src/pages/Checkout/index.js b/src/pages/Checkout/index.js
new file mode 100644
index 00000000..f8be96f7
--- /dev/null
+++ b/src/pages/Checkout/index.js
@@ -0,0 +1 @@
+export {default} from "./Checkout"
\ No newline at end of file