diff --git a/public/coffee-mug.png b/public/coffee-mug.png
new file mode 100644
index 000000000..c19c2088d
Binary files /dev/null and b/public/coffee-mug.png differ
diff --git a/src/02-component-patterns/assets/no-image.jpg b/src/02-component-patterns/assets/no-image.jpg
new file mode 100644
index 000000000..ae122b097
Binary files /dev/null and b/src/02-component-patterns/assets/no-image.jpg differ
diff --git a/src/02-component-patterns/components/ProductButtons.tsx b/src/02-component-patterns/components/ProductButtons.tsx
new file mode 100644
index 000000000..5332c59bd
--- /dev/null
+++ b/src/02-component-patterns/components/ProductButtons.tsx
@@ -0,0 +1,18 @@
+import { useContext } from 'react'
+import { ProductContext } from './ProductCard'
+import styles from '../styles/styles.module.css'
+
+export const ProductButtons = () => {
+ const {increaseBy, counter } = useContext(ProductContext)
+ return (
+
+
+
{counter}
+
+
+ )
+}
diff --git a/src/02-component-patterns/components/ProductCard.tsx b/src/02-component-patterns/components/ProductCard.tsx
new file mode 100644
index 000000000..e5fcb33fc
--- /dev/null
+++ b/src/02-component-patterns/components/ProductCard.tsx
@@ -0,0 +1,23 @@
+import { createContext } from 'react'
+import styles from '../styles/styles.module.css'
+import { useProduct } from '../hooks/useProduct'
+import { ProductContextProps, ProductCardProps } from '../interfaces/interfaces'
+
+export const ProductContext = createContext({} as ProductContextProps)
+const { Provider } = ProductContext
+
+export const ProductCard = ({ product, children }: ProductCardProps) => {
+ const { counter, increaseBy } = useProduct()
+
+ return(
+
+
+ {children}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/02-component-patterns/components/ProductImage.tsx b/src/02-component-patterns/components/ProductImage.tsx
new file mode 100644
index 000000000..7d747eb5a
--- /dev/null
+++ b/src/02-component-patterns/components/ProductImage.tsx
@@ -0,0 +1,13 @@
+import { useContext } from 'react'
+import { ProductContext } from './ProductCard'
+import noImage from '../assets/no-image.jpg'
+import styles from '../styles/styles.module.css'
+
+export const ProductImage = ({ img = '' }) => {
+ const {product} = useContext(ProductContext)
+ const imgShow = img || product.img || noImage
+
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/src/02-component-patterns/components/ProductTitle.tsx b/src/02-component-patterns/components/ProductTitle.tsx
new file mode 100644
index 000000000..88bbf0ec1
--- /dev/null
+++ b/src/02-component-patterns/components/ProductTitle.tsx
@@ -0,0 +1,12 @@
+import { useContext } from 'react'
+import { ProductContext } from './ProductCard'
+import styles from '../styles/styles.module.css'
+
+export const ProductTitle = ({ title }: { title?: string }) => {
+ const {product} = useContext(ProductContext)
+ const titleShow = title || product.title
+
+ return (
+ {titleShow}
+ )
+}
diff --git a/src/02-component-patterns/components/index.ts b/src/02-component-patterns/components/index.ts
new file mode 100644
index 000000000..d5c704d4d
--- /dev/null
+++ b/src/02-component-patterns/components/index.ts
@@ -0,0 +1,15 @@
+import { ProductCardHOCProps } from '../interfaces/interfaces'
+import { ProductCard as ProductCardHOC } from './ProductCard'
+import { ProductImage } from './ProductImage'
+import { ProductTitle } from './ProductTitle'
+import { ProductButtons } from './ProductButtons'
+
+export { ProductImage } from './ProductImage'
+export { ProductTitle } from './ProductTitle'
+export { ProductButtons } from './ProductButtons'
+
+export const ProductCard: ProductCardHOCProps = Object.assign( ProductCardHOC, {
+ Title: ProductTitle,
+ Image: ProductImage,
+ Buttons: ProductButtons
+} )
\ No newline at end of file
diff --git a/src/02-component-patterns/hooks/useProduct.ts b/src/02-component-patterns/hooks/useProduct.ts
new file mode 100644
index 000000000..8f854f251
--- /dev/null
+++ b/src/02-component-patterns/hooks/useProduct.ts
@@ -0,0 +1,11 @@
+import { useState } from 'react'
+
+export const useProduct = () => {
+ const [counter, setCounter] = useState(0)
+
+ const increaseBy = (value: number) => {
+ setCounter(prev => Math.max(prev + value, 0))
+ }
+
+ return { counter, increaseBy }
+}
\ No newline at end of file
diff --git a/src/02-component-patterns/interfaces/interfaces.ts b/src/02-component-patterns/interfaces/interfaces.ts
new file mode 100644
index 000000000..15547c59d
--- /dev/null
+++ b/src/02-component-patterns/interfaces/interfaces.ts
@@ -0,0 +1,25 @@
+import { ReactElement } from 'react'
+
+export interface ProductCardProps {
+ product: Product,
+ children?: ReactElement | ReactElement[]
+}
+
+export interface Product {
+ id: string
+ title: string
+ img?: string
+}
+
+export interface ProductContextProps {
+ counter: number
+ increaseBy: (value: number) => void,
+ product: Product
+}
+
+export interface ProductCardHOCProps {
+ ({ product, children }: ProductCardProps): JSX.Element,
+ Title: ({ title }: {title?: string}) => JSX.Element,
+ Image: ({ img }: {img?: string}) => JSX.Element,
+ Buttons: () => JSX.Element
+}
\ No newline at end of file
diff --git a/src/02-component-patterns/pages/ShoppingPage.tsx b/src/02-component-patterns/pages/ShoppingPage.tsx
new file mode 100644
index 000000000..56902684d
--- /dev/null
+++ b/src/02-component-patterns/pages/ShoppingPage.tsx
@@ -0,0 +1,35 @@
+import { ProductButtons, ProductCard, ProductImage, ProductTitle } from "../components"
+
+const product = {
+ id: '1',
+ title: 'Coffee Mug - Card',
+ img: './coffee-mug.png'
+}
+
+export const ShoppindPage = () =>{
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/src/02-component-patterns/styles/styles.module.css b/src/02-component-patterns/styles/styles.module.css
new file mode 100644
index 000000000..48ab3f819
--- /dev/null
+++ b/src/02-component-patterns/styles/styles.module.css
@@ -0,0 +1,62 @@
+
+
+.productCard {
+ background-color: white;
+ border-radius: 15px;
+ color: black;
+ padding-bottom: 5px;
+ width: 250px;
+ margin-right: 5px;
+ margin-top: 5px;
+}
+
+.productImg {
+ border-radius: 15px 15px 0px 0px;
+ width: 100%;
+}
+
+.productDescription {
+ margin: 10px;
+}
+
+.buttonsContainer {
+ margin: 10px;
+ display: flex;
+ flex-direction: row;
+}
+
+.buttonMinus {
+ cursor: pointer;
+ background-color: transparent;
+ border: 1px solid black;
+ border-radius: 5px 0px 0px 5px;
+ font-size: 20px;
+ width: 30px;
+}
+
+.buttonMinus:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.countLabel {
+ border-bottom: 1px solid black;
+ border-top: 1px solid black;
+ font-size: 16px;
+ height: 25px;
+ padding-top: 5px;
+ text-align: center;
+ width: 30px;
+}
+
+.buttonAdd {
+ cursor: pointer;
+ background-color: transparent;
+ border: 1px solid black;
+ border-radius: 0px 5px 5px 0px;
+ font-size: 20px;
+ width: 30px;
+}
+
+.buttonAdd:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+}
\ No newline at end of file
diff --git a/src/routes/Navigation.tsx b/src/routes/Navigation.tsx
index a9bfe83b0..bb82c5153 100644
--- a/src/routes/Navigation.tsx
+++ b/src/routes/Navigation.tsx
@@ -4,6 +4,7 @@ import {
Route,
NavLink
} from 'react-router-dom';
+import { ShoppindPage } from '../02-component-patterns/pages/ShoppingPage';
import logo from '../logo.svg';
@@ -15,7 +16,7 @@ export const Navigation = () => {
-
- Home
+ Shopping
-
About
@@ -36,7 +37,7 @@ export const Navigation = () => {
Users
- Home
+