├── README.md
├── package-lock.json
├── package.json
├── public
├── cake1.jpg
├── cake2.jpg
├── cake3.jpg
├── cake4.jpg
├── cake5.jpg
├── cake6.jpg
├── cookies1.jpg
├── cookies2.jpg
├── cookies3.jpg
├── crepes1.jpg
├── crepes2.jpg
├── crepes3.jpg
├── icon.png
├── index.html
├── manifest.json
├── muffin1.jpg
├── muffin2.jpg
└── robots.txt
└── src
├── About.js
├── App.css
├── App.js
├── Components
├── Cart
│ ├── Cart.js
│ ├── CartItem.js
│ └── ChangeQuantity.js
├── DessertsComponents
│ ├── Dessert.js
│ └── Desserts.js
└── Filter
│ ├── AllCategories.js
│ └── Filter.js
├── Contact.css
├── Contacts.js
├── History.js
├── IconCart.js
├── Login.js
├── Logout.js
├── ParticlesContact.js
├── ParticlesHistory.js
├── Particless.js
├── Secret.js
├── Shop.js
├── VideoOne.mp4
├── data
└── dataDesserts.js
├── historyImages.js
├── index.css
├── index.js
├── redux
├── cartSlice.js
├── dessertsSlice.js
└── store.js
└── reportWebVitals.js
/README.md:
--------------------------------------------------------------------------------
1 | # Website built with Node.js, Auth0, React, React Router, React hooks, Redux, Redux toolkit, AOS.js animations, tsParticles, CSS3, JSX.
2 |
3 | ## In the project directory, you can run:
4 |
5 | ## npm i
6 | ## npm start
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "finalproject",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@auth0/auth0-react": "^1.12.0",
7 | "@reduxjs/toolkit": "^1.9.0",
8 | "@testing-library/jest-dom": "^5.16.5",
9 | "@testing-library/react": "^13.4.0",
10 | "@testing-library/user-event": "^13.5.0",
11 | "aos": "^2.3.4",
12 | "gsap": "^3.11.3",
13 | "localforage": "^1.10.0",
14 | "match-sorter": "^6.3.1",
15 | "react": "^18.2.0",
16 | "react-dom": "^18.2.0",
17 | "react-particles": "^2.5.3",
18 | "react-redux": "^8.0.5",
19 | "react-router": "^6.4.3",
20 | "react-router-dom": "^6.4.3",
21 | "react-scripts": "5.0.1",
22 | "react-tsparticles": "^2.5.3",
23 | "redux": "^4.2.0",
24 | "sort-by": "^1.2.0",
25 | "tsparticles": "^2.5.3",
26 | "web-vitals": "^2.1.4"
27 | },
28 | "scripts": {
29 | "start": "react-scripts start",
30 | "build": "react-scripts build",
31 | "test": "react-scripts test",
32 | "eject": "react-scripts eject"
33 | },
34 | "eslintConfig": {
35 | "extends": [
36 | "react-app",
37 | "react-app/jest"
38 | ]
39 | },
40 | "browserslist": {
41 | "production": [
42 | ">0.2%",
43 | "not dead",
44 | "not op_mini all"
45 | ],
46 | "development": [
47 | "last 1 chrome version",
48 | "last 1 firefox version",
49 | "last 1 safari version"
50 | ]
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/public/cake1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/cake1.jpg
--------------------------------------------------------------------------------
/public/cake2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/cake2.jpg
--------------------------------------------------------------------------------
/public/cake3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/cake3.jpg
--------------------------------------------------------------------------------
/public/cake4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/cake4.jpg
--------------------------------------------------------------------------------
/public/cake5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/cake5.jpg
--------------------------------------------------------------------------------
/public/cake6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/cake6.jpg
--------------------------------------------------------------------------------
/public/cookies1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/cookies1.jpg
--------------------------------------------------------------------------------
/public/cookies2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/cookies2.jpg
--------------------------------------------------------------------------------
/public/cookies3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/cookies3.jpg
--------------------------------------------------------------------------------
/public/crepes1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/crepes1.jpg
--------------------------------------------------------------------------------
/public/crepes2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/crepes2.jpg
--------------------------------------------------------------------------------
/public/crepes3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/crepes3.jpg
--------------------------------------------------------------------------------
/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/icon.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
19 |
20 |
29 | Candyshop
30 |
31 |
32 | You need to enable JavaScript to run this app.
33 |
34 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/muffin1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/muffin1.jpg
--------------------------------------------------------------------------------
/public/muffin2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/public/muffin2.jpg
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/About.js:
--------------------------------------------------------------------------------
1 | import video from './VideoOne.mp4';
2 | import React from 'react';
3 | import AOS from 'aos';
4 | import 'aos/dist/aos.css';
5 | AOS.init({
6 | duration: 1200,
7 | })
8 |
9 | const About = ()=>{
10 | return(
11 |
12 |
13 |
14 |
15 |
16 |
CANDYSHOP
17 |
Your favourite genuine sweets
18 |
Since 1948
19 |
20 |
21 | )
22 | }
23 |
24 | export default About;
25 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #c28585;
3 | font-family: 'Montserrat', sans-serif;
4 | }
5 | .particles {
6 | z-index: -1;
7 | }
8 | .background-video {
9 | position: absolute;
10 | object-fit: cover;
11 | width:100vw;
12 | height:100vh;
13 | top: 50%;
14 | left: 50%;
15 | transform: translate(-50%, -50%);
16 | z-index: -1;
17 | }
18 | nav{
19 | display: flex;
20 | justify-content: space-around;
21 | }
22 | .transparentBtn{
23 | background-color: transparent;
24 | margin: 0;
25 | padding: 0;
26 | font-size: 25px;
27 | text-align: center;
28 | }
29 | .marginAuth{
30 | margin-top: 25px;
31 | text-align: center;
32 | }
33 | .par{
34 | z-index: 10;
35 | margin-bottom: 10px;
36 | margin-top: 40px;
37 | color: rgb(44, 30, 30);
38 | text-transform: capitalize;
39 | font-weight: bolder;
40 | font-size: 28px;
41 | }
42 | .italic{
43 | font-style: italic;
44 | }
45 | .aboutLines{
46 | display: flex;
47 | flex-direction: column;
48 | justify-content: flex-end;
49 | margin-left: 20px;
50 | margin-bottom: 10px;
51 | margin-top: 140px;
52 | }
53 | .linkStyle{
54 | margin-top: 40px;
55 | margin-bottom: 40px;
56 | margin-left: 40px;
57 | font-size: 45px;
58 | text-decoration: none;
59 | color: #fff;
60 | font-weight: bolder;
61 | }
62 | .linkStyle:hover{
63 | color: #94526e;
64 | }
65 | .block {
66 | background-color: rgba(253, 218, 226, 0.5);
67 | padding: 20px;
68 | width: 40vw;
69 | }
70 | .App {
71 | text-align: center;
72 | display: flex;
73 | justify-content: space-around;
74 | }
75 | .categoryButton{
76 | cursor: pointer;
77 | margin: 5px;
78 | padding:15px ;
79 | }
80 | .categoryButtonSelected{
81 | background-color: pink;
82 | }
83 | .categoryButton:hover {
84 | background-color: #94526e;
85 | }
86 | .icon{
87 | width: 20px;
88 | }
89 | button{
90 | font-family: 'Montserrat', sans-serif;
91 | padding: 10px;
92 | border: none;
93 | cursor: pointer;
94 | background-color: #f3f2e7;
95 | border-radius: 12px;
96 | font-weight: bolder;
97 | }
98 | .App-logo {
99 | height: 40vmin;
100 | pointer-events: none;
101 | }
102 | .box{
103 | display: flex;
104 | justify-content: center;
105 | align-items: center;
106 | }
107 | .btnNexAndPrevious{
108 | background-color: #c28585;
109 | color: yellow;
110 | font-size: 20px;
111 | }
112 | .btnNexAndPrevious:hover{
113 | color: aliceblue;
114 | }
115 | .text{
116 | text-align: center;
117 | opacity: 0.8;
118 | }
119 | .slide{
120 | width:600px;
121 | height:400px;
122 | }
123 | .bgCart{
124 | background-color: #ad4c74;
125 | padding: 20px;
126 | color: #fff;
127 | width: auto;
128 | }
129 | .italic{
130 | font-style: italic;
131 | color: rgb(247, 218, 218);
132 | font-weight: lighter;
133 | }
134 | .visibilityBtn{
135 | z-index:1;
136 | }
137 | @media (prefers-reduced-motion: no-preference) {
138 | .App-logo {
139 | animation: App-logo-spin infinite 20s linear;
140 | }
141 | }
142 | .App-header {
143 | background-color: #282c34;
144 | min-height: 100vh;
145 | display: flex;
146 | flex-direction: column;
147 | align-items: center;
148 | justify-content: center;
149 | font-size: calc(10px + 2vmin);
150 | color: white;
151 | }
152 | .App-link {
153 | color: #61dafb;
154 | }
155 | @keyframes App-logo-spin {
156 | from {
157 | transform: rotate(0deg);
158 | }
159 | to {
160 | transform: rotate(360deg);
161 | }
162 | }
163 | @media all and (max-width:800px){
164 | .par{
165 | color: #fff;
166 | text-transform: uppercase;
167 | font-weight: bolder;
168 | font-size: 20px;
169 | }
170 | .linkStyle{
171 | font-size: 34px;
172 | }
173 | .imgSmallDevice{
174 | width: 50%;
175 | }
176 | }
177 | @media all and (max-width:500px){
178 | nav{
179 | display: flex;
180 | justify-content: space-between;
181 | }
182 | button{
183 | color: #000000;
184 | }
185 | h1{
186 | font-size: 20px;
187 | }
188 | .par{
189 | color: #fff;
190 | text-transform: uppercase;
191 | font-weight: bolder;
192 | font-size: 15px;
193 | }
194 | .linkStyle{
195 | font-size: 12px;
196 | }
197 | .transparentBtn{
198 | font-size: 12px;
199 | margin-left:5px;
200 | }
201 | .imgSmallDevice{
202 | width: 100px;
203 | }
204 | .slide{
205 | width:400px;
206 | height:300px;
207 | }
208 |
209 | }
210 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | BrowserRouter as Router,
4 | Routes,
5 | Route,
6 | Link
7 | } from 'react-router-dom';
8 | import About from './About';
9 | import './App.css';
10 | import Contacts from './Contacts';
11 | import History from './History';
12 | import Shop from './Shop';
13 | import Login from './Login';
14 | import Logout from './Logout';
15 |
16 | function App() {
17 |
18 | return (
19 |
20 |
21 | About
22 | History
23 | Shop
24 | Contacts
25 |
26 |
27 |
28 |
29 |
30 | } />
31 | } />
32 | } />
33 | } />
34 |
35 |
36 |
37 |
38 |
39 | );
40 | }
41 |
42 | export default App;
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/Components/Cart/Cart.js:
--------------------------------------------------------------------------------
1 | import { useSelector, useDispatch } from "react-redux";
2 | import { getCartItems, getTotalPrice } from "../../redux/cartSlice";
3 | import CartItem from "./CartItem";
4 | import { clearCart } from "../../redux/cartSlice";
5 |
6 | const Cart = ()=> {
7 | let cartItems = useSelector(getCartItems);
8 | const totalPrice = useSelector(getTotalPrice);
9 |
10 | const dispatch= useDispatch();
11 |
12 | return(
13 |
TOTAL: $ {totalPrice}
14 |
15 |
16 | dispatch(clearCart({cartItems: []}))}>Clear cart
17 |
18 |
19 | {cartItems.map((cartItem,index)=>
)}
20 |
21 | )
22 | }
23 |
24 | export default Cart;
25 |
--------------------------------------------------------------------------------
/src/Components/Cart/CartItem.js:
--------------------------------------------------------------------------------
1 | import { useDispatch } from 'react-redux';
2 | //import dataDesserts from '../../data/dataDesserts';
3 | import { removeItemFromCart } from "../../redux/cartSlice";
4 |
5 | const CartItem = ({ cartItem })=> {
6 |
7 | // const desserts= dataDesserts.find(item=> item.id === cartItem.dessertId)
8 | const dispatch= useDispatch();
9 |
10 | return(
11 |
12 | {/*
*/}
13 |
{cartItem.name}
14 |
Portion(s): {cartItem.quantity}
15 |
${cartItem.totalPrice}dispatch(removeItemFromCart({cartItemId: cartItem.id}))}>
16 |
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default CartItem;
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Components/Cart/ChangeQuantity.js:
--------------------------------------------------------------------------------
1 | const ChangeQuantity=({quantity, setQuantity})=>{
2 |
3 | const addQuantity= ()=>{
4 | const newQuantity = quantity + 1;
5 | setQuantity(newQuantity);
6 | }
7 |
8 | const removeQuantity= ()=>{
9 | if (quantity <= 0) return;
10 | const newQuantity = quantity - 1;
11 | setQuantity(newQuantity);
12 | }
13 |
14 | return(
15 |
16 | -
17 | {quantity}
18 | +
19 |
20 | )
21 | }
22 |
23 | export default ChangeQuantity;
24 |
--------------------------------------------------------------------------------
/src/Components/DessertsComponents/Dessert.js:
--------------------------------------------------------------------------------
1 | import ChangeQuantity from "../Cart/ChangeQuantity";
2 | import { useState } from "react";
3 | import { useDispatch, useSelector } from "react-redux";
4 | import { addItemToCart, getCartItems, updateQuantity } from "../../redux/cartSlice";
5 |
6 | import 'aos/dist/aos.css';
7 |
8 |
9 | const Dessert = ({ dessert })=> {
10 | const [quantity, setQuantity]= useState(1);
11 | const dispatch= useDispatch();
12 |
13 | const cartItems = useSelector(getCartItems);
14 | console.log(cartItems);
15 |
16 | const dessertInCart = cartItems.some(item => item.id === dessert.id);
17 |
18 | const handleBuy = () =>{
19 | if (!dessertInCart){
20 | dispatch(addItemToCart({ dessert, quantity }))
21 | }
22 | else{
23 | dispatch(updateQuantity({dessert, quantity}))
24 | }
25 |
26 | }
27 |
28 | return(
29 |
30 |
31 |
{dessert.name}
32 |
${dessert.price}
33 |
34 |
35 | Add to cart
36 |
37 | )
38 | }
39 |
40 | export default Dessert;
41 |
42 |
43 |
44 |
45 | // import ChangeQuantity from "../Cart/ChangeQuantity";
46 | // import { useState } from "react";
47 | // import { useDispatch } from "react-redux";
48 | // import { addItemToCart } from "../../redux/cartSlice";
49 |
50 | // const Dessert = ({ dessert })=> {
51 | // const [quantity, setQuantity]= useState(1);
52 | // const dispatch= useDispatch();
53 | // return(
54 | //
55 | //
56 | //
{dessert.name}
57 | //
${dessert.price}
58 | //
59 | //
60 | // {dispatch(addItemToCart({dessert,quantity}))}}>Add to cart
61 | //
62 | // )
63 | // }
64 |
65 | // export default Dessert;
66 |
--------------------------------------------------------------------------------
/src/Components/DessertsComponents/Desserts.js:
--------------------------------------------------------------------------------
1 | import dataDesserts from '../../data/dataDesserts';
2 | import Dessert from './Dessert';
3 | import { useSelector } from 'react-redux';
4 | import { getSelectedCategory } from '../../redux/dessertsSlice';
5 |
6 |
7 | const Desserts = ()=> {
8 | const selectedCategory= useSelector(getSelectedCategory);
9 | return(
10 | {dataDesserts
11 | .filter(dessert => {
12 | if (selectedCategory === 'ALL') return true;
13 | return selectedCategory === dessert.category;
14 | })
15 | .map((dessert, index)=> )}
16 |
)
17 | }
18 |
19 | export default Desserts;
--------------------------------------------------------------------------------
/src/Components/Filter/AllCategories.js:
--------------------------------------------------------------------------------
1 | import Filter from "./Filter";
2 |
3 | const AllCategories = ()=> {
4 | return(
5 |
YOUR CART
6 | Smth sweety this way comes...
7 | { ['ALL','COOKIES', 'CAKES', 'MUFFINS', 'CREPES'].map((category, index)=> )}
8 |
9 | )
10 | }
11 |
12 | export default AllCategories;
13 |
--------------------------------------------------------------------------------
/src/Components/Filter/Filter.js:
--------------------------------------------------------------------------------
1 | import { useDispatch, useSelector } from "react-redux";
2 | import { filterCategory, getSelectedCategory } from '../../redux/dessertsSlice';
3 |
4 | const Filter =({ category })=>{
5 | const dispatch = useDispatch();
6 | const selectedCategory= useSelector(getSelectedCategory);
7 | return(
8 |
{dispatch(filterCategory(category))}}
9 | className={selectedCategory === category ? 'categoryButtonSelected categoryButton' : 'categoryButton'}> { category }
10 |
)
11 | }
12 | export default Filter;
--------------------------------------------------------------------------------
/src/Contact.css:
--------------------------------------------------------------------------------
1 | @import url(https://fonts.googleapis.com/css?family=Roboto:400,300,600,400italic);
2 | * {
3 |
4 | padding: 0;
5 | box-sizing: border-box;
6 | -webkit-box-sizing: border-box;
7 | -moz-box-sizing: border-box;
8 | -webkit-font-smoothing: antialiased;
9 | -moz-font-smoothing: antialiased;
10 | -o-font-smoothing: antialiased;
11 | text-rendering: optimizeLegibility;
12 | }
13 |
14 |
15 |
16 | .container {
17 | max-width: 400px;
18 | width: 100%;
19 | margin: 0 auto;
20 | position: relative;
21 | }
22 |
23 | #contact input[type="text"],
24 | #contact input[type="email"],
25 | #contact input[type="tel"],
26 | #contact input[type="url"],
27 | #contact textarea,
28 | #contact button[type="submit"] {
29 | font: 400 12px/16px 'Montserrat', sans-serif;;
30 | }
31 |
32 | #contact {
33 | border-radius: 15px;
34 | background: #fad0d0;
35 | padding: 25px;
36 | margin: 50px 0;
37 | box-shadow: 0 0 20px 20px rgba(238, 230, 230, 0.2), 0 5px 5px 0 rgba(122, 68, 68, 0.24);
38 | }
39 |
40 | #contact h3 {
41 | display: block;
42 | font-size: 30px;
43 | font-weight: 300;
44 | margin-bottom: 10px;
45 | }
46 |
47 | #contact h4 {
48 | margin: 5px 0 15px;
49 | display: block;
50 | font-size: 13px;
51 | font-weight: 400;
52 | }
53 |
54 | fieldset {
55 | border: medium none !important;
56 | margin: 0 0 10px;
57 | min-width: 100%;
58 | padding: 0;
59 | width: 100%;
60 |
61 | }
62 |
63 | #contact input[type="text"],
64 | #contact input[type="email"],
65 | #contact input[type="tel"],
66 | #contact input[type="url"],
67 | #contact textarea {
68 | width: 100%;
69 | border: 1px solid rgb(245, 182, 182);
70 | border-radius: 15px;
71 | background: #FFF;
72 | margin: 0 0 5px;
73 | padding: 10px;
74 | }
75 |
76 | #contact input[type="text"]:hover,
77 | #contact input[type="email"]:hover,
78 | #contact input[type="tel"]:hover,
79 | #contact input[type="url"]:hover,
80 | #contact textarea:hover {
81 | -webkit-transition: border-color 0.3s ease-in-out;
82 | -moz-transition: border-color 0.3s ease-in-out;
83 | transition: border-color 0.3s ease-in-out;
84 | border: 1px solid #aaa;
85 | }
86 |
87 | #contact textarea {
88 | height: 100px;
89 | max-width: 100%;
90 | resize: none;
91 | }
92 |
93 | #contact button[type="submit"] {
94 | cursor: pointer;
95 | width: 100%;
96 | border: none;
97 | background: rgb(233, 172, 177);
98 | color: #000000;
99 | margin: 0 0 5px;
100 | padding: 10px;
101 | font-size: 15px;
102 | }
103 |
104 | #contact button[type="submit"]:hover {
105 | background:rgb(185, 124, 124) ;
106 | color:#212121;
107 | font-weight: bolder;
108 | transition: background-color 0.3s ease-in-out;
109 | }
110 |
111 | #contact button[type="submit"]:active {
112 | box-shadow: inset 0 1px 3px rgba(221, 168, 168, 0.5);
113 | }
114 |
115 | .copyright {
116 | text-align: center;
117 | }
118 |
119 | #contact input:focus,
120 | #contact textarea:focus {
121 | outline: 0;
122 | border: 1px solid #aaa;
123 | }
124 |
125 | ::-webkit-input-placeholder {
126 | color: #888;
127 | }
128 |
129 | :-moz-placeholder {
130 | color: #888;
131 | }
132 |
133 | ::-moz-placeholder {
134 | color: #888;
135 | }
136 |
137 | :-ms-input-placeholder {
138 | color: #888;
139 | }
140 |
141 | h3, h4{
142 | text-align: center;
143 | }
--------------------------------------------------------------------------------
/src/Contacts.js:
--------------------------------------------------------------------------------
1 | import ParticlesContact from './ParticlesContact';
2 | import './Contact.css';
3 | import 'aos/dist/aos.css';
4 |
5 | const Contacts = ()=>{
6 | return
7 |
8 |
We would ❤️ to hear from you
9 |
Always.
10 |
27 |
28 | }
29 |
30 | export default Contacts;
31 | ;
--------------------------------------------------------------------------------
/src/History.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { historyImages } from "./historyImages";
3 | import ParticlesHistory from "./ParticlesHistory";
4 | import 'aos/dist/aos.css';
5 |
6 | const History = ()=>{
7 |
8 | const [foto, setFoto]= useState(0);
9 | const {image}= historyImages[foto];
10 |
11 | const previousFoto=()=>{
12 | setFoto((foto=>{
13 | foto --;
14 | if (foto<0){
15 | return historyImages.length-1
16 | }
17 | return foto
18 | }))
19 | }
20 |
21 | const nextFoto =()=>{
22 | setFoto((foto=>{
23 | foto++;
24 | if(foto>historyImages.length-1){
25 | foto=0;
26 | }
27 | return foto;
28 | }))
29 | }
30 |
31 | return(
32 |
33 |
34 |
35 |
36 |
37 |
From donuts to carrot cakes, from brownie to meringue...
38 |
From our ❤️ to yours
39 |
40 |
See our best desserts in the gallery:
41 |
42 |
43 |
44 |
Back
45 |
46 |
Next
47 |
48 |
49 |
"This was why she enjoyed baking. A good dessert could make her feel like she'd created joy at the tips of her fingers. Suddenly, the people around the table were no longer strangers. They were friends and confidantes, and she was sharing with them her magic." - Marissa Meyer
50 |
51 |
52 |
)
53 | }
54 |
55 | export default History;
--------------------------------------------------------------------------------
/src/IconCart.js:
--------------------------------------------------------------------------------
1 | import { useSelector } from "react-redux";
2 | import { getTotalPrice } from "../../redux/cartSlice";
3 |
4 | const IconCart = ()=> {
5 | const totalPrice = useSelector(getTotalPrice);
6 | return()
9 | }
10 |
11 | export default IconCart;
--------------------------------------------------------------------------------
/src/Login.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useAuth0 } from "@auth0/auth0-react";
3 |
4 |
5 | const Login = ()=>{
6 | const { loginWithRedirect, isAuthenticated } = useAuth0();
7 | return(
8 | !isAuthenticated && loginWithRedirect()}> Log in
9 |
10 | )
11 | }
12 |
13 | export default Login;
--------------------------------------------------------------------------------
/src/Logout.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useAuth0 } from "@auth0/auth0-react";
3 | import Secret from './Secret';
4 |
5 | const Logout = ()=>{
6 | const { logout, isAuthenticated } = useAuth0();
7 | return(
8 | isAuthenticated &&
9 | logout({ returnTo: window.location.origin })}> Log out
10 |
11 |
12 |
13 | )
14 | }
15 |
16 |
17 |
18 |
19 |
20 | export default Logout;
--------------------------------------------------------------------------------
/src/ParticlesContact.js:
--------------------------------------------------------------------------------
1 | import { useCallback } from "react";
2 | import Particles from "react-tsparticles";
3 | import { loadFull } from "tsparticles";
4 |
5 | const ParticlesContact = () => {
6 | const particlesInit = useCallback(async engine => {
7 | console.log(engine);
8 | await loadFull(engine);
9 | }, []);
10 |
11 | const particlesLoaded = useCallback(async container => {
12 | await console.log(container);
13 | }, []);
14 |
15 | return (
16 |
162 | );
163 | };
164 |
165 | export default ParticlesContact;
166 |
167 |
--------------------------------------------------------------------------------
/src/ParticlesHistory.js:
--------------------------------------------------------------------------------
1 | import { useCallback } from "react";
2 | import Particles from "react-tsparticles";
3 | import { loadFull } from "tsparticles";
4 |
5 | const ParticlesHistory = () => {
6 | const particlesInit = useCallback(async engine => {
7 | console.log(engine);
8 | await loadFull(engine);
9 | }, []);
10 |
11 | const particlesLoaded = useCallback(async container => {
12 | await console.log(container);
13 | }, []);
14 |
15 | return (
16 |
129 | );
130 | };
131 |
132 | export default ParticlesHistory;
--------------------------------------------------------------------------------
/src/Particless.js:
--------------------------------------------------------------------------------
1 | import { useCallback } from "react";
2 | import Particles from "react-tsparticles";
3 | import { loadFull } from "tsparticles";
4 |
5 | const Particless = () => {
6 | const particlesInit = useCallback(async engine => {
7 | console.log(engine);
8 | await loadFull(engine);
9 | }, []);
10 |
11 | const particlesLoaded = useCallback(async container => {
12 | await console.log(container);
13 | }, []);
14 |
15 | return (
16 |
129 | );
130 | };
131 |
132 | export default Particless;
--------------------------------------------------------------------------------
/src/Secret.js:
--------------------------------------------------------------------------------
1 | import { useAuth0 } from "@auth0/auth0-react";
2 |
3 |
4 | const Secret = ()=>{
5 | const { isAuthenticated } = useAuth0();
6 |
7 | return(
8 | isAuthenticated &&
9 |
10 |
11 | Only today FREE shipping with any order!
12 |
13 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default Secret;
--------------------------------------------------------------------------------
/src/Shop.js:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import Cart from './Components/Cart/Cart';
3 | import Desserts from './Components/DessertsComponents/Desserts';
4 | import AllCategories from './Components/Filter/AllCategories';
5 | import Particless from './Particless';
6 |
7 | function Shop() {
8 |
9 | return (
10 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
25 | export default Shop;
26 |
--------------------------------------------------------------------------------
/src/VideoOne.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NatachaKey/react-redux-online-shop-with-autentification-Auth0/8cdea5109d3a9f7c18e5560753187d25555205be/src/VideoOne.mp4
--------------------------------------------------------------------------------
/src/data/dataDesserts.js:
--------------------------------------------------------------------------------
1 | const dataDesserts = [
2 |
3 | {
4 |
5 | id: 1,
6 |
7 | price: 25,
8 |
9 | img: 'cake1',
10 |
11 | name: 'Strawberry dream',
12 |
13 | category: 'CAKES'
14 |
15 | },
16 |
17 | {
18 |
19 | id: 2,
20 |
21 | price: 50,
22 |
23 | img: 'cake2',
24 |
25 | name: ' FUNtastic cake ',
26 |
27 | category: 'CAKES'
28 |
29 | },
30 |
31 | {
32 |
33 | id: 3,
34 |
35 | price: 30,
36 |
37 | img: 'cake3',
38 |
39 | name: 'Classic brownie',
40 |
41 | category: 'CAKES'
42 |
43 | },
44 |
45 | {
46 |
47 | id: 4,
48 |
49 | price: 40,
50 |
51 | img: 'cake4',
52 |
53 | name: 'Banana splash',
54 |
55 | category: 'CAKES'
56 |
57 | },
58 |
59 | {
60 |
61 | id: 5,
62 |
63 | price: 25,
64 |
65 | img: 'cake5',
66 |
67 | name: 'Cinnamon cake',
68 |
69 | category: 'CAKES'
70 |
71 | },
72 |
73 | {
74 |
75 | id: 6,
76 |
77 | price: 20,
78 |
79 | img: 'cake6',
80 |
81 | name: 'Be my cherry',
82 |
83 | category: 'CAKES'
84 |
85 | },
86 |
87 | {
88 |
89 | id: 7,
90 |
91 | price: 25,
92 |
93 | img: 'muffin1',
94 |
95 | name: 'Chocolate muffins',
96 |
97 | category: 'MUFFINS'
98 |
99 | },
100 |
101 | {
102 |
103 | id: 8,
104 |
105 | price: 20,
106 |
107 | img: 'muffin2',
108 |
109 | name: 'Cream muffins',
110 |
111 | category: 'MUFFINS'
112 |
113 | },
114 |
115 | {
116 |
117 | id: 9,
118 |
119 | price: 20,
120 |
121 | img: 'cookies1',
122 |
123 | name: 'Hearts with chocolate',
124 |
125 | category: 'COOKIES'
126 |
127 | },
128 |
129 | {
130 |
131 | id: 10,
132 |
133 | price: 15,
134 |
135 | img: 'cookies2',
136 |
137 | name: 'Crunchy fanstasy',
138 |
139 | category: 'COOKIES'
140 |
141 | },
142 |
143 | {
144 |
145 | id: 11,
146 |
147 | price: 15,
148 |
149 | img: 'cookies3',
150 |
151 | name: 'Berlin cookies',
152 |
153 | category: 'COOKIES'
154 |
155 | },
156 |
157 | {
158 |
159 | id: 12,
160 |
161 | price: 10,
162 |
163 | img: 'crepes1',
164 |
165 | name: 'Crepes fantasy',
166 |
167 | category: 'CREPES'
168 | },
169 |
170 | {
171 |
172 | id: 13,
173 |
174 | price: 13,
175 |
176 | img: 'crepes2',
177 |
178 | name: 'Crepes with fruits',
179 |
180 | category: 'CREPES'
181 |
182 | },
183 |
184 | {
185 |
186 | id: 14,
187 |
188 | price: 25,
189 |
190 | img: 'crepes3',
191 |
192 | name: 'Loving crepes',
193 |
194 | category: 'CREPES'
195 |
196 | }
197 |
198 | ]
199 |
200 | export default dataDesserts;
--------------------------------------------------------------------------------
/src/historyImages.js:
--------------------------------------------------------------------------------
1 | export const historyImages = [
2 | {
3 | id:1,
4 | image:" https://images.unsplash.com/photo-1530648672449-81f6c723e2f1?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1089&q=80"
5 | },
6 | {
7 | id:2,
8 | image:" https://images.unsplash.com/photo-1587314168485-3236d6710814?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1078&q=80"
9 |
10 | },
11 | {
12 | id:3,
13 | image:" https://images.unsplash.com/photo-1567691334394-c0d00a7716db?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1460&q=80"
14 | },
15 | {
16 | id:4,
17 | image:" https://images.unsplash.com/photo-1573050329989-9c2509328687?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80"
18 | },
19 | {
20 | id:5,
21 | image:"https://images.unsplash.com/photo-1514481538271-cf9f99627ab4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80"
22 | },
23 | {
24 | id:6,
25 | image:"https://images.unsplash.com/photo-1469533778471-92a68acc3633?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1171&q=80"
26 | },
27 | {
28 | id:7,
29 | image:"https://images.unsplash.com/photo-1587306433556-18d035a3ed97?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80"
30 | }
31 | ]
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 | import { Provider } from 'react-redux';
7 | import { store } from './redux/store'
8 | import { Auth0Provider } from "@auth0/auth0-react";
9 |
10 | const root = ReactDOM.createRoot(document.getElementById('root'));
11 | root.render(
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | );
28 |
29 | // If you want to start measuring performance in your app, pass a function
30 | // to log results (for example: reportWebVitals(console.log))
31 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
32 | reportWebVitals();
33 |
--------------------------------------------------------------------------------
/src/redux/cartSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | export const cartSlice = createSlice({
4 | name: 'cart',
5 | initialState: {
6 | cartItems: []
7 | },
8 | reducers: {
9 | addItemToCart: (state, action) =>{
10 | // const timeId = new Date().getTime();
11 | state.cartItems.push({
12 | id:action.payload.dessert.id,
13 | dessertId: action.payload.dessert.id,
14 | name:action.payload.dessert.name,
15 | quantity: action.payload.quantity,
16 | price: action.payload.dessert.price,
17 | totalPrice: action.payload.quantity * action.payload.dessert.price,
18 | })
19 | },
20 |
21 | removeItemFromCart: (state, action) =>{
22 | state.cartItems = state.cartItems.filter( cartItem => cartItem.id !== action.payload.cartItemId
23 | )
24 | },
25 |
26 | updateQuantity: (state, action) => {
27 | const newCart = [];
28 |
29 | state.cartItems.forEach(item => {
30 |
31 | if (item.dessertId === action.payload.dessert.id) {
32 | let newQuantity = item.quantity + action.payload.quantity;
33 | let totalSum = item.price * newQuantity;
34 | const changeCart = {...item, quantity: newQuantity, totalPrice: totalSum };
35 | newCart.push(changeCart);
36 | } else {
37 | newCart.push(item);
38 | }
39 | })
40 | state.cartItems = newCart;
41 | },
42 |
43 | clearCart: state => {
44 | state.cartItems = []
45 | }
46 | }
47 | })
48 |
49 | export const getTotalPrice= state=> {
50 | return state.cart.cartItems.reduce((total, cartItems)=>{
51 | return cartItems.totalPrice + total
52 | }, 0)
53 | };
54 |
55 | export const getCartItems= state=> state.cart.cartItems;
56 | export const { addItemToCart, removeItemFromCart, clearCart, updateQuantity }= cartSlice.actions;
57 | export default cartSlice.reducer;
58 |
--------------------------------------------------------------------------------
/src/redux/dessertsSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | export const dessertsSlice = createSlice({
4 | name: 'desserts',
5 | initialState: {
6 | selectedCategory: 'ALL'
7 | },
8 | reducers: {
9 | filterCategory: (state, action)=>{
10 | state.selectedCategory = action.payload;
11 | }
12 | },
13 | })
14 |
15 | export const getSelectedCategory =state =>state.desserts.selectedCategory;
16 | export const { filterCategory }= dessertsSlice.actions;
17 | export default dessertsSlice.reducer;
--------------------------------------------------------------------------------
/src/redux/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from '@reduxjs/toolkit';
2 | import desserts from './dessertsSlice';
3 | import cart from './cartSlice';
4 |
5 | export const store = configureStore({
6 | reducer: {
7 | desserts,
8 | cart
9 | },
10 | })
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------