React Full Stack Web Development for Beginners: Build & Launch Digital Ecommerce Website with MERN | Czero | Skillshare
Search

Playback Speed


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

React Full Stack Web Development for Beginners: Build & Launch Digital Ecommerce Website with MERN

teacher avatar Czero, Backend Developer

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Intro

      2:20

    • 2.

      Setup

      2:26

    • 3.

      MongoDB

      2:39

    • 4.

      Product Model

      3:55

    • 5.

      Postman & Product Routes

      7:10

    • 6.

      Frontend Setup

      2:17

    • 7.

      Protected Routes

      2:40

    • 8.

      Admin Dashboard

      6:36

    • 9.

      Delete And Edit Product

      11:06

    • 10.

      Spinner

      1:57

    • 11.

      Cloudinary

      2:39

    • 12.

      Create Product

      7:05

    • 13.

      Home

      1:48

    • 14.

      Product Card

      3:17

    • 15.

      Navbar And ThemeToggle

      2:09

    • 16.

      Cart

      7:16

    • 17.

      Stripe

      4:25

    • 18.

      Cart Checkout

      1:04

    • 19.

      Cancel Success Page

      1:24

    • 20.

      Shop

      1:57

    • 21.

      Footer Subscriptions

      4:59

    • 22.

      Stats

      3:07

    • 23.

      Jwt

      3:54

    • 24.

      Login Register

      7:29

    • 25.

      Admin Navbar

      2:20

    • 26.

      Jwt Auth For Requests

      1:41

    • 27.

      Ui Fixes

      5:51

    • 28.

      Deploy Vercel Updated

      6:12

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

37

Students

--

Project

About This Class

This course is designed for developers who want to build a fully functional digital ecommerce website from scratch. You'll gain hands-on experience with MongoDB, Express.js, React, and Node.js, learning how to integrate these technologies to create a modern and dynamic user experience.Throughout the course, you'll explore key concepts such as database management, server-side logic, front-end development, and API integration. By the end of the course, you'll have a solid understanding of the MERN stack and the skills to develop your own fullstack applications using this stack.

Meet Your Teacher

Teacher Profile Image

Czero

Backend Developer

Teacher
Level: Beginner

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Intro: This course, you will learn about MRNSTeC in practice on creating a FlSteC digital ecommerce website. Welcome. This is a class for everyone who wants to learn about creating a FluSTeC applications using MarnSTeC. It will give you a great basics and understanding how Fluste applications work. MRNSTeC is a group of technologies that work together to build websites and web apps. By the end of this course, you will be able to add new data from your website, straight to the database, update them straight from your website, and also delete. Don't worry if you are new to this, I will explain everything in simple terms, so it's easy to follow. Let's say that first 70% of the course is learning MntC, learning how to work with the data and HDDP methods to work with your database. In our case, MongoDB and the rest will be about utilizing this for a digital ecommerce website. So in the end, you have a nice working project. Let's see what these letters stands for M for MongoDB MongoDB is a type of database where you can store information for your app, like user data or posts. It keeps things organized and easy to find, just like a digital filling cabinet, for example. E in Express, which is a tool that helps connect front of the website or user C to the back where the data and logic are. It makes it easier to handle things like routes, which are like paths that lead to different parts of our application, R in react, which is a framework that is used to build a part of the website that users interact with. It makes the pages more interactive and dynamic. So when user clicks on things or type in forms, the page can update smoothly without needing to reload. Then in note, which lets us use JavaScript on the server, which is the part of the app that works behind the scenes and it will help our app to do things like connect to the database, handle user logins and process data. We will be also integrating different solutions for our needs. For example, stripe for payments and loadinary for image storage. We also deploy our site to go through it together and learn how to deploy flustg application like this to the Internet. Because we care about security, we will also use something called JWT or JSON web tokens to secure our paths that we will be using to call this database and do these HDP methods as post, get or delete. Looking forward to see on the course. 2. Setup: First, we will CD the Band folder. We will do Command NPM Y. We will go into package dot JSON and there below the description. We'll do a type with module, which will allow us to import and export. And we will execute the command you can find in the description for installing all the libraries we will need. In Backend folder, again, we will create a new file dot N for storing the environment variables. We will start with the port there and we will use 3,000. Now we'll create another new file, and that will be called index dogs. In indexed Dogs, we'll start with importing the modules we will need for the application. Express library for building the server, config function from the tenth to load the environment variables, Mangus for Mongo DB Object modeling, course middleware to enable cross origin sharing, Cloudinary for handling, Cloud Image storage, Mlter for handling, file uploads, and Cloudinary storage adapter for Mlter. This function, we will load environment variables from dot N file, we created there. Then we will create an instance of Express, and then on this instance of Express, we will use course. That means we'll allow the cross origin requests. You can imagine this as giving a passport to our request. If we don't use it, our request will fail. Now you'll start the server and tell our app that it should listen to our report that we set there. It will be port 3,000. And also to know if it's successful or not, we'll add a console log, and that will back then that server running on and out board. Before we write, we need to review things. Let's go to package JSON. There under script. There we can delete the test. And instead, we'll write the start, node index to Js and also def nodemon indexed to JS. We will CD acond now we'll do NPM run. Deaf and you can see our server was started, and also server running on 3,000 port was shown in a Console. And now I'll show you what is nodemon doing for us. So let's change this server to server AA, save it. And now you can see the server got automatically restarted. And this is how it will work even with other changes. Of course, we won't be changing the console logs, but if you change something on our page, then the server will get automatically restarted and we will see the change in real time. 3. MongoDB: Now we will set up MongoiB. So we'll click there on their page, mongoib.com on try free. Then we will fill the data and we will create the account. Then you will verify your email, then we will go there in the panel, and we will click on Database. Then we'll click on Create we will select free. There you can change the name for your cluster, and what is a cluster? Cluster is a collection of multiple servers. Imagine we have a books stored in a library, and instead of one library, we have it in multiple smaller libraries. In case one is closed, we can access other libraries. In our term, that means that we will avoid the server failure. Now, you click on Create Deployment. We will select their username and password, and then before we continue, need to click on this Create database user. Then we will click on Choose a connection method. Then we'll click on drivers. Then we have their selected no JS, which is correct. We need to use NPM install Mon goody B in our project. Then we will take this string and we'll put it into environment variables. Let's copy this, click on D. We'll go to VS code where we have folder with our project. There we will do CD back end. We will do NPM I Mongo DB. Then in our Bond project, we will go into environment variables. There we'll create MongoDB, equals, and we will place there the string that we just copied. Now what you need to do is that you need to change this for your password that you set in the Mongo DB. Now we will connect to MongoDB using Mongoose and lock success or error. After Mongos keyboard, we will do. Connect. Then and catch. We'll try to connect. Then if we are successful, we'll want to log also next to server is running. Database is connected, so we know it. And in case there are issues, we can see that the database wasn't connected, and that we will achieve with catching this error there. But if you will see the first line, where we should connect. Now it's empty. So we cannot do just MongoDB. We need to also do the process dot environment or NO you can already see we go there an error. But in this case, you just need to restart the server. If you don't know how, you will do Sutter plus C, Y, Enter. And then again NPM run Gap. Now if you run again, your database will be connected. 4. Product Model: Now we'll tell our application to use a tool that will automatically read and understand JSON data that will be sent in request to our server. So for example, when we will receive JSON like this, we will be able to work with it. Now, let's create modules and routes and see this in practice. So first, in our Ben folder, we'll create a new one and that will be called Models. Then another one, and that will be called Routes. In modules, we will start with the file called product dot JS or product model dot JS, and in routes, we will start with product Route dot JS. First, let's start in product model dot JS. We will import the Mongoose to work with the Mongo D B. We'll create the product schema, and there we'll start putting the fields we want for our product. First one will be name, it will be of type string, and it will be required. Next one will be price, and a stripe is accepting the price in a sense, we will do it directly in sense also, so it will be done easier. It will be type number and also it will be required. For every product, we will need an image, and this image is type of string because it's actually an URL of the image. And how we will get this URL, we will get it via the Cloudinary that we will configurate later. This field will be also required. Next, we will create a picklist named category. It will be of type string, and also it will be required, but we will have their anym for course and template, and that will create a pick list. And then we will export this product schema. Now we'll start writing the routes. First, we will import the express to create a router and then we'll also import our product, which we just created. Now we will create a new router instance, and this will help us to organize and handle different URLs or routes in our application. For you to have Context immediately, we will do Export Default Router, and we will go into index dot JS and there we will first import the product route we are now working on, and then we will app use product from the product drought. Now, when we will create a new route for creating a new product item with dot Post method, we will have there this slash online. But we define there this product route. So when we want on front end to actually create a new item or in postman, we will need to call then the URL would look like something like this. It will be Local host 3,000. Then as we did there, the product in product route, it will be slash product. And there as there is nothing, it will be empty. But we will specify before for Excios or in Postman, that this is a post method. So it will automatically know it's this route, and to have even another example, if we do the create product, then the route will look like this. We will put there recommend that this route is for creating a new product and we can continue. First, we'll do Tricatchblock inside. So if our block from Tri fails, we will lock the error in catch. We will lock the error in console log, and then we will set the response status to 500 and we will send the error message. Now, to check if the required fields are present, we will do if condition there. In case any of these fields are missing, it will return a true that means it will fail because then in the true block, we will set response status to 400 and message required fields are missing. In case it's false and we have all the fields, it will continue to this creation of a new constant, new product where we will set all the fields. Now with product dot create, we will save this new product into a database, and then we will return a response with two oh one and send the created status with new product. 5. Postman & Product Routes: We'll go on postman.com and we'll sign up for free. We will fill all the informations there, fill more informations there. Then down on the page, you will click on Download Desktop application, actually download it and install it. Now on the page, we'll click on new request, and there we'll work with our requests. There we'll be putting the endpoint URL, and there we'll be changing the methods. So get post put and delete. We'll be using these four. Then we'll hit Send and we'll be seeing the outputs there. If we will be sending some data, we'll be putting them into this body, where we will select raw and make sure that is selected also JSON. Format, how you are inserting data looks like curly brackets, then field and then value. And this field name will depend on what we will define at our model. So for example, for the name, it will be name and value John. And the URL depends on the route you set. So we are using their product route, which starts with slash Product and then, for example, for creating a new product, we are seeing their post method, and that's it. We'll be calling local host 3,000 product. And now, if we send, we have a route for get. That means it will get all our data from Mongo Di B. Now to immediately test our new route, we will go into Postman, there we will change it to post. We'll start with the local host, then we will do the product, and then we can leave this empty. We will select their body, raw, Make sure this is Jason. First, we will insert your name, then price in Send image with testing URL, of course, won't work and the category where you have a template. Now as these are strings, we need to have the quotes, but for number, we don't need it. Now let's try to hit Send and we were successful. Now we can check our MongoDB, and in our MongoDB, this record was inserted. Later on front end, we will create an input form where we will fill all these fields. Then we will click on a button it will call this route, and it will get inserted into MongoDB. Now to display our products that we will insert into Mongol B on our page, we will need to also get a route for getting them. And now we will go one by one and create them. For routes of getting all items, we will first again do try catch block inside. We did find, we will retrieve all the items from our database, and we'll set status to 200, which is like O status. In case there will be an error, we will lock the error message, answer the response status to 500, then we will go to Postman to test it, change post to get, it sent, and we will receive data from our database. Like before, we'll do Router dot get, but now in a path, we will specify also the ID. That means we will in the path need to specify also this ID we have there. And that we will extract simply from the request parameters like this. Now we will find it by ID with this function find by ID, and then we'll send a response to 100 with the product retrieved. In case this fails, like before, we will log the error message, set the status to 500 and send it. Now let's test it. So let's take the ID, put it there, now send it. And we got the item. Now let's continue with route for deletion, and it will be quite similar to get product route. Instead of G, we will do that delete method. Then we will also insert the ID parameter, and we'll do Trakageblock. In wreckage block, the catch will be the same, and in Tribloc we'll also first extract the product item by the ID parameter. Then we'll find it with the ID and we will delete it. We will do an if condition there in case result is false and the item was not found, and we'll return a response with Satus four oh four, and then we will also send a message with product not found. In case result is true, so the product is found, we will skip this if condition and continue and set the response status to 200 and send a message product successfully deleted. Now we can test it, so we simply change this two, delete. Now we will send it and product successfully deleted. Now, we select back the G method, and we delete the ID and leave there product and hit Send, you will see that we have nothing there. But now if we change this to post, we can do the test AA, send it. Now we can do again get, send it. You will see that we again fill there some data. We will continue with the Update product route. In update, we'll be using Dt Put method, and there we'll also specify the D of which product we want to update. Again, we'll do a tryCatch block. And this condition to check if we have there all the fields, and we won't be updating the product image. In case some field is missing, we'll send a message with response status 400 required fields are missing. Then we'll extract the ID from the request parameters. We'll find the product item and update it with new data from the request body. Then we'll do an I condition, and in case this result is false, we will return status four oh four with product not found. In other scenario, we will just continue set the status to 200 and send the message, product updated in case the trade block fails and we gauge block, will again log the message, set the status to 500 and send the error message. Now in Postman, we have the URL with local host product and ID. We'll change it to put there we will change the test AA to test BB. Now we'll delete the image because that is something we won't be updating. We will send it and we got product not found. So that is because I put there a wrong ID. We will change this to get. We will delete this. We will hit Send. Now we will copy this ID from the product we have there. We will put it there. Now we will change this to put. Now we will have it like this. We will hit Send, and we will receive product updated. Now, if you go back to get and delete this ID and hit Send, you can see that in our database, we have now test A B instead of test AA, and we can also do post, then leave it like this. There we can do test AA. We will put their image URL test image. Now we will hit Send. Now we will do also test CC. We will hit Send and Test DD, hit Send. Now we will change it to get method, hit Send, and you will see that we have there all our test records we put there. Now you can try all these routes that we did get post, put, and delete. 6. Frontend Setup: And now we'll move to front end folder and start coding the front end there. We can close these, open a terminal, then terminal, we will do CD. Front end, we will execute the tailwind commands you can find on telencss.com. First one is this one. I'm putting there the dot, so the project will be created inside this front end folder. Now I will run this for the installation. Then this for the initialization. Now, this one for installing the libraries, you can find this command in description. Now we will go into tailwind conflict JS file. We need to paste there this setting, we will need for Daisy UI and also, of course, for tailwind. Also, to have the correct typography, we will put the first require tan CSS typography, and the next after this needs to be require Daisy UI. Otherwise, it won't work. You can do your own team. So there I put the placeholder for my team where I choose primary, secondary accent and neutrlor. And we are also using there some DaisyUI teams, which are light, dark, cupcake, forest or business. Then we'll be choosing which two to use for light and dark mode. Now let's start creating folders and files. First, in the front end folder, we'll create a DdentFle where we also put environment variables. There we will put Wheat, React app, backend, base URL, and we'll put there the base URL of our backend, which is HTDP local host 3,000. Then in source file, we will create a new folder and it will be with pages. Then another one, that will be with context, then another one, and that will be with components. In Index CSS, we will paste the three till end directives. Then we can delete Abdo CSS. And in ABD Jasix, we can delete everything and leave there just to return with the empty fragment. 7. Protected Routes: First, before creating anything, we'll go into components and we'll create a new file. The file will be called Protected Routes. Do JS six. First, for navigation, we'll import reactor router dome. Then we'll have a function to check if the user is authenticated. Inside, we will return true if the token exists in local storage. Otherwise, we will return false. Then we'll define the protected route component itself. And we'll start with the If condition. This if condition we'll check the mentioned is authenticated, and in case the user is not authenticated, it will navigate him to the login page when he can try it again and login or register. In case he is and this will pass, we will render the children components, and on the end, we will export the protected route component, so we can reduce it in Abdo J six. In Abdo J six, let's do these two imports. Then we will also import the created protected route, and we will create our first route inside the empty fragments. Everything in this protected route will begin with Admin and inside, we'll have the other routes. To have this a little bit more clear we'll create a new constant with admin routes. Now we can take it and put it inside the protected route. So now all the routes for our admin interface, we'll put there into these admin routes. Let's start with the first one. So let's go to pages, create a new file there. It will be called admin.j6. There we will use the Snippet. We have there to export, so we can go to Abdo J six, and there we can start Admin import, then save it, check that is imported. Also, what we will do there is that we will route it. So route path slash element like this, and now there we will just close the tech. Now if we do slash Admin, and that's it because there is nothing else. If there will be XIX, then if we do slash admin slash XIX, we will get to this admin component. We still don't have the login and register functionality, but we will do it a little bit later. Now we will just go to protected routes, we can just mock it by commenting this and putting there return through. Like this, it will always return through, so we will be always authenticated. But of course we should put there to do implement login and register functionality. Let's open the terminal, CD front end, do NPM Run Dev, and there you can see our page. 8. Admin Dashboard: Now we can add some class name there so we can double check everything is working. Now to fix few issues. We need to go into Abdo GS six and wrap our route into outtak we will add this then we will move into main do GS six. There we will import Browser Router from React router Dome and we will change direct strict mode and wrap our app inside Browser Router text. Now, if we will go on the URL with local host that we use and do slash Admin, you will see our tailwind is working and we have dio text we have in our DIF. So we can go back to admin page. First, we'll import, use effect and us state, also from react, then Exios and then Link from React router Dome. We will use us State hook to manage the product state. We will initialize it as an empty array. Then we will use us State Hook to manage the loading state, and this one will help us because then we'll be using the loading, and in case it will be true, we will be showing the spinner. Now we will use useffectHok to perform side effects in the function component on our admin page. First, we will set loading to true. We will make a Gad request to fetch the product data from the API. You can think of Exiles as a tool that allows our application to talk to servers and get or send data. So our Exiles will get something and then it will do something or in case it will fail, it will catch the error. So this is the URL that we were using in the postman now we will use it there in Exis. So we will try to get this URL API endpoint. In case we have a successful response, we will update the product state with the data we received. And then we'll also set the loading state to falls. In case we fail, we will catch it, log any error, and then we will set also loading state to falls. Then on this last line is actually an empty dependency array, and that means this effect will run once when the component is loaded. Now let's move to the UI, there we can start. We will delete the class name from there. We will add some padding and maximum width. I can also add there some background gray 500 so we can see where we are on the page. So we can see we will also need to center this with Mx auto. Now we'll add another div because above this div, we will have still a stats component that we didn't code yet. And for this div, we'll put the overflow X auto class because we want to enable the side scrolling. Then we'll add table itself, and we will put the class name table because we are using Daisy UI and DUI have a class for the table. Inside the table, we will put a Thad, which stays for table head, TR, which stays for table rows, and now we are the table headers of nine price, description, and category, we have the first table header empty because we will end a button that will lead us to a page where we'll be able to create a new product item. So inside, we can put a link with name at item. Currently, we still don't have the page for creation of a new item, so we will leave this to empty, and then we will fix it. I will set the green background, and onHver I will change the background to darker green. Then I will send text size and text color, some padding and border radius with some shadow. Now we'll need to do the table body, so we will make there some space. Inside, we'll do a table body, and in table body, we will need to iterate over the product. And now we'll code one table row and it will get repeated every time it will iterate over the product array. So in our table row, we'll do the key as product ID, and then we'll start with the class name for background white, and when we will hover over our item, we will change it to a bit darker color, which is in our case, background gray to 300. And now we'll be just putting into this table row the table data, which will be accordingly to the table headers. So on the first position, we will have product image. So you can see there we are using DIV with class name Avatar, which is from DSUI and then another div with mask, also from DSUI some width and height set. Then in image itself, we are setting source from product dot image, and in case it won't be loadable, for product dot title. We are getting all this data from Mongo DV where we loaded them. In the next olume, we will show product dot name. You can see it also there. Then we'd like to show price description and category, let's do it. As price, we will show price in a sense, then the description, then a category, and that will be it for now. You can see I left the description without mapping over the product and that's because we didn't add description yet to our model. I wanted to leave there one field in case you would also want to add one field for yourself. I will show you now how you can add additional fields, we'll open the explorer. In back end, we'll go to the models. In Barack model we will there add a description. It will be of type string, and the required will be false because we don't need to have this field filled, then we will go into route product route. Now we'll scroll up to create new product route. There in new product, we will add a description, so description request, but the description. And now we can test it, so we will open Postman, and by the way, if anything is not working for you, make sure you are always in a CD backend folder, like now I am. And you have the server running with NPM run depth because the database needs to be connected all the time. So just check through these open terminals you have there. You can see I have running the front end and also I have running the backend. Sometimes what can happen, there is some issue error, and the backend server is down. So you need to check it sometime because otherwise, it won't work for you. Now I will put there some testing data even with the description field. Now I will hit Send and you can see it was inserted like this. Now if I will change this to get hit Send I can see that I have their items without the descriptions, but my newest one is including the description field that we just edit. So now what I will also do is that I will just copy paste this, take the description, save it, and now it will work. And now you can see on our page, all the items are loaded. 9. Delete And Edit Product: Pages, we will create a new file. Edit product G six, also delete Product G six. Then we will use AFC, so it will use the snippet from the extension ES seven plus. There you can download it also. Now we'll go to AG six, where we will import these two new pages. And also we'll route them. We will add its route to the route of admin routes. Put the path where it will be product, edit, or delete with the ID. In edit product, we will start with the imports. First, we'll import notice tech for the snack bar we'll use for notifications. Then we'll import with react also the use state and use effect hooks, react roterdm and also Eos. We will set the state hook for form fields and loading state. We will be updating the name, pricing sense, and category. And if you edit some field, as, for example, the description, you will edit there also like this. Description, set description, and the default value will be empty. Then we'll do hook for navigation. That means after we successfully edit our product, it will navigate us to the admin dashboard. I mean not yet, but we will use this navigate function for that. Next one, what we will do is hook to get the ID parameter from the URL, and then snack bar. That means we'll be showing the notification in case we will be successful or not successful at editing our product. We'll create an effect hook to fetch the product details when our page loads. We'll start with setting loading to room. Then like as before, we'll use Exiles first, we'll try to get the response from this endpoint where we'll also paste this ID. If you don't remember, in our product route, we set the ID as needed parameter for the update product route because we need to know which product we want to update. Then in case we are successful, we will set the fields value to the response. So set name, price incense, description, and category. Then also, we will set loading to falls. So after we will have a spin on there, it will stop spinning. In case this will fail, we will catch the error there, also set loading to falls, log it, and then send an alert that something happened. And what about the dependence array? Now it won't be empty, but we'll have their NID. And this means that this effect will run when the ID will be changed. Now we'll do a handle for editing the product item. First, we will have their data to be sent in the Put request, then we will set loading to true, and we'll have their Excise. First, we'll make put request to update the product item. And then in case we are successful, we'll set loading to falls. We will then show the success message or the success notification, as you can see QsNCbr there, and then we will navigate to the admin dashboard. So whenever this was successful from this page, we'll be automatically sent to admin dashboard. In case it fails, we will have the error there. We will set loading to falls. We will send the error notification, and also we'll log the arrow into the console. And to get on our page where we'll actually code DUI, let's go to admin J six. There had on the bottom, below last table data, create a new table data, put the class name for the table data before. Now create your TV with classes of flexbox layout so we can use Justify Center for centering it horizontally and then gap X one to have there some gap between these two links. We'll have the link for the edit. Now, if we go through it, you can see it using the path Admin product slash Edit and then it's also sending the ID of the product. Remember, we are still iterating over the products there. So we have the ID of the product and we can use it there, and we'll send it to Path then we have some class names, so we will use background to orange and on Her we will make it a bit darker. Then we will set text white and font weight. Also, we'll make the border radius there, text size there, and use some padding. You can basically just copy paste it because it will be almost the same. Just in the path, there will be delete instead of edit. Also, you'll change the background color, and we'll change the text. It won't be edit, but it will be delete. Now on our page, we will select some product and click on Edit, and it will take us to the page where we are currently coding now. So let's go to the page, and there we can start. First, in the main div, we'll use some bedding, background gray to 50, flexbook layout, so we can center the items. Then we'll do another div with container class that is from the AZUI. We'll set some mix with the shadow then border radius, bedding and background to white. And in case we would like to go back, we'll use this link to B. So we'll put there to Admin, then in class name, we'll center the B text. Set the background for this button, some padding, so it actually looks like button, margin with text size, and I border radius. Then we'll continue with the title for this section. We'll name it Edit product. We'll set some text size to it, from semi bold, vertical margin, and we'll set the text color to text gray 800. Now we will prepare the di for the input boxes. So we'll do a DIF with vertical margin to four. First input will be for the name, so we will have the label with the name and its input. In label, we'll do HTML four where we'll put a name. For class names, we'll put block, text medium, and text grade to 600 with some margin bottom, and for the input, we'll set ID to name type of text. Value will be set also to name, and on change, we will set the name for the target value we have there. For a class name of this input, we'll set some border, and we will set also the border color, some padding, with the full, and also we will use some border radius there. And now it will be basically the same for all the other inputs. But some values will be changing This HTML four, this ID, the type, of course, the value, and the use state hook. So for pricing sense, we will change it to these values, but otherwise, it will use the same values as before. So same for description with set description description, ID, and HTML four like this, of course, also with the label text, for category, we will also add their options because this is a pick list. So you have option for select category and then curse and template. We will have the values that will be set then there and read by this use state. Otherwise, again, everything is the same. HTML four is changed to category, ID is category, value category, classes are the same, and we are having required there because we need to select the picklist value. Also, this is not an input but a select, so take care of this text. Then to confirm this edit, we'll have a button there with safe changes. On a click, it will call this handle Edit product, which we code there. Then we'll set our class name of it to full. We'll set the background to green, and we will use a darker green, then text to white, some padding and border radius with some margin to top. Now we can test the functionality, and then I promise we will make the UI look better. So let's change TBB to Test BB edit it. Then we can also change price in sense, description, and category. Save changes. Now you can see it got edited. And if we go into MongoDB, you can see it's edited also there. Let's now go to files there into Min J six, and there we will wrap our application into the Snack Bar provider text. So there let's put our application inside. Also, let's import it. So if we do just this, it will get imported automatically. Now if we go back to our page, we can try again to edit something. Now, we will leave this field empty. We will save changes, and you can see we received an error in case we will fill it and we will save it. You can see the successful notification. So again, main do the JA six, wrap our application to Snack Bar provider, import it, and it will work. Now to finally set a team, we'll go to index dot HTML, and there in HTML tech, we will do data team to Light. Now it finally it looks better, and if we go to edit, you can also see it looks good there. And now to the deletion part. Let's click there on some item. Let's click on Delete. So we'll get to the page VS code, we'll go to delete product.j6. First, let's do the imports like before, then the hooks. First one for the loading state, Navigation, getting ID from the URL. And the snack bar for the notifications. Now we'll create a handler for deleting the product item. We will set loading to true, and then we'll excise with delete function, we will call the endpoint to delete the product. In case we are successful, well set loading to fals notification with product was deleted and navigate ourselves to admin dashboard. In case it fails, we will set loading to false also send error notification and log the error. In the main div, we'll use some pedding background gray, and then flexbox layout so we can center the items with justify and items center. Inside, we'll do another div with class container, which is from the DaisUI. We will set some maximum width shadow and pedding. Like before, in Edit product GS six, we'll do a back button there. And as it is absolutely the same, I want to explain it, and I will move to the heading two, where we will ask, Are you sure you want to delete this product? For this, we will set some classes with text size, text weight, margin to bottom and text color. And then the answer can be clicking on a button with yes, delete. Otherwise, user will just click on a B button. In case, user will click on delete button, it will call the handle delete product, which we did there. And class for this delete button will be a red button with darker red on Her, text white, some pading, border radios, and with to full. Now we can check it on the page, and we can try to delete the product. Let's click on as Delete. You can see product deleted and also it disappeared from our list of items. 10. Spinner: Now before moving to create product GS six, we will create a spinner. Let's open the files there, and this one will be in components. So in components, create a new file, spinner dot JS six. Use the snippet there. Now for this div, we'll use a flexbox layout, justify items to center, and side determine height to screen. For the next div, we will set with anhight to 16, border with to four pixels, border style to dashed, round it full, so it's a circle. Animate spin, which we will create index dot CSS in a minute. Then border primary, so we will use the primary color, top of the border color transparent. Now for the animated spin, we go to index dot CSS there first with keyframes, spin, where we will use the rotate 360 degrees, and then for the animated spin, where we will set the animation for spinning, 1 second, linear and infinite loop. Now, if we go to Edit Product GS six and there in the return below the start of the main dith, we will add the spinner. It enters, so it will get imported and save it. Now, if we go onto our page, you will see the spinner is there. You end it there only when the page will be loading. So what we'll do is that we will put there curly brackets with loading. And in case that's true, it will generate the spinner. Now I will save it. If you will save the changes, you can see the spinner was there for a really short time because the save was actually quite fast. Now we can add the spinner also to delete page. So let's just copy this. Go to delete product. Let's edit there. Now delete the last leather, hit Enter. That way, it gets imported. And from now on, we can use the spinner wherever we will have the loading state. 11. Cloudinary: Before creating a UI for adding an item, we'll need to set up a Cloudinary. It won't be efficient to upload such big images into MongoDB, so we'll upload it into separate storage like Cloudinary. We will get the URL, and in a string, we will store the URL in MongoDB. Then we will access the URL and we will be displaying the images on our page. Let's go to cloudinary.com. Click on Get Started. There you will sign up. Then you can select something there. Let's start. There you will scroll down, click on View credentials, and then you will use these credentials. We'll take this cloud name, API key and API secret. Go into environment variables, and there we'll create Cloud name, API secret and key. You will paste there all the values you have on your Cloud binary, and then we'll move to media library, and there we'll find all our pictures we will upload. Now, if we go to Index Dogs, we can start coding the route that will actually aloud our images. Now we'll configure Cloudinary creentials from environment variables. So first will be the cloud Name, API key, and then API secret. Now we'll add a Cloudinary instance with this upload use. So we are making sure Cloudinary will be accessible before the next route that follows. So the storage and upload image. Now we'll configure Multi storage to use a Cloudinary for storing our images. We will do the Cloudinary instance, and then we'll set the parameters. First parameter is the folder for our images, which is images. And then we'll set allowed image formats, which will be JPEG and PNG. What we are doing there is that we are creating a parser that uses moltorlibrary, which we are using for handling the file uploads and the storage configuration is the object we set there to specify how and where to save the uploaded files. Now, we'll create a route for uploading the image to Cloud binary. We'll do this with post method, and the route will be called Upload Image. We'll use a parser to upload a single file, and then with If condition, we'll check if the file is there. In case it's not, it will return status 400 with no file uploaded. Then we'll create a tryCatch block. And we'll create an If condition if we have the path for the file. In case not, we'll return error with file uploaded, but no path available. In case there is the path, we'll return the file URL. And then in case this world wide Block fails, we'll catch an error there, return a Status 500 with the internal server error. 12. Create Product: Now we'll go to pages and create the new file. Create product.j6. There we snip it. Then we'll go to ab dot JSix. There we can copy this, paste it there, type there, create product. Hit Enter, that way it gets imported, and there we'll change the path to just slash product slash Create. Now we'll go to admin.j6 and there in at item, we'll add a link, and that will head us to Admin slash Create. First, we'll do the imports, then we'll create state variables for all the field we have for our product. And now we also at image preview. This means when we load the image, we will see the preview of it on our page before we confirm and upload it, and then also state variable for the loading to show the spinner. Then hook for the navigation. After we will be successful, it will navigate us to the Advantage board and also snack bar for displaying the notifications. We'll create their handler for file input change. We'll create a constant for the selected file that we'll get from the input event. We will update the image state with the selected file, and then on the selected file, we'll do an Ils condition. In case we have selected files, and this is true, we will create a new file reader instance, and we will set image preview to the file, we upload it. And what this read as data URL method does is that it reads the content of the selected file and converts it to an encoded string. And also, we'll add set image preview to null in case the selected file is false, so it's not there. We'll create a function to upload a file to Cloud binary. First, we need to check if we have an image for uploading. In case not, we'll send the warning notification with no image selected. In case we have the image, we'll continue, and we'll create a new form data instant and we'll append to it our image. Then we'll create a Tricatblock. In Trblock we'll create Upload URL, and you can see there the upload image route that we did in Index dogs for uploading the files into Cloudinary. And we will send the post request to upload the URL with the image data. Now from the Cloud binary response, we will get the SURL which is the URL for our uploaded image. Then we will cons overload the uploaded image URL so we can double check it, and we will send a notification that the image was uploaded successfully. In the end, we'll return the SecuRL. So when we will call this upload file function and it will be successful, we will get the URL from it. In case it fails, we will set error there with notification, fail to upload an image. And now we'll do the big function itself for uploading all the fields, even with the image for our product, and we'll call it handlesafe product. We'll check if required fields are filled, and in case we will send a notification, please fill all the required fields. We have the or operator, so in case that any of these values are false, so they are empty, it will send this notification. Then we will have the check that the price is a positive number, and in case not, we'll again send the notification. We are also setting the new price constant because we are converting price incense into an integer. Then we will set loading to true and we will use it for the spinner eter, and we will create a tryCatch block there. First, we will attempt to upload the image. You can see we'll await the upload file, which we have there. In case we won't be successful and this function fails, we will get an error image upload failed. In other case, if we are successful, we'll continue there and we will prepare the form data for saving the product. Then we will send a post request to save the product with the form data, and in case we are successful, we will send a notification about it and then navigate the user to an admin dashboard. In case this dryblock fails, we will send a notification about error and log it, and we'll add also finally block where we will set the loading state to false to indicate that the process is done. Now we will code JS six so we can test our new functions. We will now be cheating a bit. Or maybe it's better to call it that we'll be smart developers. What we'll do is that we will go to Edit Product J six, and there we will actually take the wall J six. Copy it and put it into our create product. So there now we have it. And instead of edit, we'll put there create. Next thing, what we do is that we will scroll down and this button won't be saving changes, but it will be just saving. Also on click, it won't call handle Edit product because we don't even have this function there. It's only there, but it will be calling the HandlesafPduct function. So we can just delete this one word, HandlesafPduct, and if you will double click there and do Satterf, you can see it's this function that we did for saving the product. Now we need to include three more things, and that is the image. So pretty similar to this label and Input, we will create one for the image like this, and it will be actually the same. Label will say upload image, HTML four will be image, ID image, type will be file, except will be image slash and on change, it will call this handle file change that we did. Then it will also be required, and one more thing we need to add there is the image preview. For that, we'll use a similar render like we are using for spinner. We will check if image preview is true, and in case it is, we will generate this HTML. Where we will have a diff that will be wrapping the image, and source of the image will be the image preview. You can again see image preview is our state variable there, and we are setting it in this handle file change. We will set there and mix with to full and height to auto, that's pretty much it. Now if you want to double check, I will slowly go through it. But it is basically the same as we have Edit product, but we add different on click function for the button, image preview, and input for the image. We need to fix a small thing there. It's actually not admin Create. It's Admins product slash Create. Now if you save it, we are now on our loco host with slash Admin, so we are on the dashboard. We will click Adm, and we'll get to an actual create product form. And now let's test it. So let's fill the data and upload some image. We'll hit Save. End product with image we saved and uploaded successfully. Now you can see our new item. You can see the image preview there. Now we can delete the old data. I created two courses and one template, and by now, we covered all create, read, update, and delete or also known as rat actions. And it's time to show them on UI for our customers. 13. Home: We will now delete this Admin, we will be coding the customer facing. Now we'll go into pages, create a new file, home dot JS six. We will use the snippet there. Now we'll go into Abdo GSSix and there in routes, but not in the admin route, we will create a new route for the homepage. We will put there this path and also element home. Then we will import the home we created. There we import effect and State Hook and Eos State to all the product items, and we will continue to use effect Dook to fetch the data when this page will load. We will make an HTP gut request to fetch the product data. That means we'll call this endpoint with product. And if you remember, this is exactly what we did in Postman to get all the products we have in our MongoiB. Then we will set the fetch data to the product state. And in case we are not successful, we will lock any errors to the console. And also, this empty dependency array means that this effect runs once when the page will be loaded. For the main dif, we'll set maximum weight to 1,300 pixels, and we will center it with mix Oto. Also, we'll add some merge into top and padding. Inside, we'll do another div with class hero content, and this zero content is a class from DZI. Inside this zero content, we'll do another div with some maximum weight, and there we'll put a text descriptions and shop button. First, we'll welcome the users on our page. We will add the name of the page into the Spentex where we'll add some different text color to highlight it. Then we'll include there some text and the button that will redirect our user to a shop itself later. 14. Product Card: Now we will load all the products, but for that, we'll create two separate components that will keep our code clean. Let's go into the components and create a new file. Product single card J six and product card.g6. In product card, we'll first import product single card because this JS six will be as a layout for all these single cards. Then this component will receive a product object as a prop, and it will display its information. For the main div, we'll use a grid layout, and we will on large screen, we'll use a grid columns tree. Then on a smaller screen, we will use two grid columns, and on mobile screen, we will use one grid column that we don't need to define because it's a default value. Then we'll set maximum width of 1,200 pixels, some gap between the items. We will center this with mix Auto, and we also center the items with place items center. Then we will iterate over the products. And for every product, we will call the component product single card that we will code right after this one, and we will input key with item ID and product with the item. Now in dc.com, we will go into the card on the panel, and we'll select there a card that we like. Then you will select the J six and you can copy the code. Then insert the code into this return on our product single card a six. Also, don't forget to put there a prop of product. To have all our images for products nicely aligned, we'll set there to full, set there some fixed height. Also set their Object to cover and object top. Also, we will set the source as product dot image. Old as product dot name, and then to the card body and the description, we'll set card title as product name, then the description, and in case the description won't be available, we will display no description available, and then for the price, we'll divide it and show it in dollars. So currently it's in cents, and we can convert it like this. It's also possible to have some number format and use it. Now we'll do it in more simple way. Then we'll head to home Jasix and there we'll start writing product card. Hit Enter that way, it gets imported. Then we need to set there a prop which is Product as product, save it. And then if we go to our page, we will see all the items we edit into our Mongo DB there. Now, everything we will edit, delete or add there on the admin dashboard, you will see it also on the customer facing there. So let's go to this item. Let's edit it. Save changes, go back to customer facing, refresh the page, and we can see the change there. Now let's try to add an item. And now we can see it on our customer facing. And now we'll create a card. So we are able to add there the products we want to buy and then check out. For a card, we need to add navigation bar. So let's go one by one. 15. Navbar And ThemeToggle: A two components. The create a new file, nowbar.j6, and there you'll use the snippet, go on ap J six and edit above brutes. So there now bar, import it. You can head to daisy.com and select there on Navbar you like. I go to J six, copy it and paste it on my page. You will paste it there, but I have a modified version, and I will go through it. But before going through it, I will show you what I actually imported, and we'll just need to import the link for the smooth scroll. We will use this link tech. Instead of the anchor tag we head there. Now, to have this navigation bar complete, we need to create two more components. First one, which will be Team tagle button, Chasix and second one, which will be card icon six. What we'll do in Dem toggle is to import use state and usefect. Then we'll define a state variable for the dark mode and continue with usefecdok to set the initial team based on current team attribute. We will get the current team from the documentary element. You can see there data team, and if you now remember and go to Index HML is the one we set there. We'll set is dark mode state based on the current them. Then we'll need to do the function for tagling the team. So we'll name it TagleTm and we'll make the new team based on the current is dark mode state. So in case the dark mode state is true, it will be cupcake team, and in case it's false, it will be business team. Then we will get the data team and we will set there the new team. And also, we'll toggle the Is dark mode state. Now, we'll use this toggle button that you can find on the DCUIPage we will use Is dark mode for checked and Toggle Team on change. Now, if we will go back to our Navbar, we can add the Toggle team button and then put it there into the Div Navbar end, which is also from DZUI and there we need to import it. So let's do it like this. Save it. And now we will check our page. It's there, so let's try to click it. And you can see it's changing the team. And now, if you go back to VS code in Team toggle button, you can set the teams you would like for light and dark mode. 16. Cart: Now let's call the Card icon J six. On DCU icon, if you go into the NaF Bar section, you can see that there is enough bar with a card and if you go into Ja six, we can actually take the card from there. We can paste it to our card icon, but we will need to add a few things. First, before coding the wall card context and this functionality, we can just mark it by setting there a constant of total quantity to zero. Then we'll wrap this dip into a total quantity check with a condition that this will be rendered only if total quantity is above zero. Also, we'll add a total quantity there and there. If we'll go back to our navigation bar, we can add the link to a card icon. Don't forget to import it. For the path, we'll do slash Card. Let's create in pages new file, Card J six. There we can do the snip it. Now we can go to Abdo J six. There we will edit to routes. Don't forget, we need to also import it and the path will be card. Now, if we go on our page, you can see there the card and when we will click on it, we will get redirected to the card page. Let's jump to the total quantity for a while now. If we go back to card icon and change this total quantity to five and save it now, you will check our page, you can see that the number was shown there. Let's go to Context and there we'll create a new file. That file will be called Card Context. Logistics. In card context, first, we'll import also the hooks, and then we'll start with creating context for the card. We will use a custom hook to use the card context. We will set children as a prop, and we will wrap it with the card context. Inside, we'll create a state to hold the card items that will be initialized from the local storage. Local storage is a feature that is provided by web browsers. And it allows us to store the data locally on the user's device. That means we will send to local storage the product that the user will want to buy, and then in the card provider, we will load it from the local storage. We'll create the use effect to update the local storage whenever card items state changes. There we will set it and in dependence array, we will put the card items. Whenever it will be changed, we will update the local storage. We'll create a function to add an item to the card. For the set card items, we'll create a constant item in card and we'll try to find. Then we'll create if condition to check if the item is already in the card. In case it's there, we will increase its quantity by one, and in case not, we'll just it with quantity of one. Then we also need to have a function for removing from card. Then we'll again set card items, which is the state defined there, and we'll just decrease the quantity or remove the item if the quantity is zero. Also, we will have function to decrease the quantity of an item in a card. There again, we use the state. And on iterating over the prep items, we'll be decreasing the quantity, and in case we get to zero, we'll again remove it. We will also function for clearing the card, and there we'll just set card items to empty array, and also we'll remove them from the local storage. In return, we'll provide card state and functions to children components. In the values, we'll paste all the functions we did, and it will wrap the children prop. Now, we need to go to Mindjsix. There we will add a card provider, and we will put our application inside. Now the card provider is wrapping our application. It's imported there, and we will have the functionality. We can go to cardicon J six. In card icon, we'll ds card context and import use card. We will use the used card hook to access the card items from the card context. And instead of this hardcoded five we have there, we will calculate the total quantity of items in the card. Now to add the ad to card item functionality, we'll go on to product single card. We will import our used card hook from the card context to interact with the card state. We will extract adds to card, remove from card and card items from our use card hook we created in the card context. We will find the item in a card that matches the current product item and save it into item in card. Then we'll get a quantity of the item in a card, and before it will be zero, if the item won't be there, we'll create a function for handling the adding item to the card and also function to handle removing the item from the card. These are the functions that we did there. Into product single card, we'll add the disk with card actions from the ZI. We'll do a check on the quantity. If it's above zero, in case it is and we have the product in a card, we'll have removed from card button. On a click, it will remove the product from the card. In case the quantity will be zero, it will render this add to card, and on a click, it will add the item to the card. And now we can test it. You can see it gets edited or removed from the card. So now I will click that add to card. We have there one I will add also this item and this item, and our number with quantity in a card is increasing or decreasing depending on how many items we have in a card. And now, when we'll click on the card, we will code the JS six. Let's go on to created card J six page. First, we'll do the imports. Then we'll extract the card items decreased card item quantity, and edit the card from our use card hook. In case our card will be empty, we want to show a message. Your card is empty. So we'll adare if condition. And in case our card items length will be zero, we will show this message. Also, before clicking on Checkout for the stripe, we would like to know which is the total price of the product we have in our card. For this, we will create a new constant, total price, and we will count there the total price of the items in our card. And then before creating a handle checkout function, we will create J six. For the main day we will put maximum width of 1,400 pixels, center it with MMX Auto, put there some pedding and margin from top. We'll put there heading with shopping cart, set there some font weight, font size, margin and text to center. Then we'll create the DIF with a grid layout and set there similar grid columns like we have on a homepage, and we will iterate over the card items. Each card item we'll show in this div where we will have this key index and we will show the item image. Then also, we'll show the item name and its price which we will convert to US dollars. Then we'll create a DIF that will show the quantity of the item we currently have in our card and below in another DIF we will have the button for remove in case we want to remove the item from the card. Last thing that we will add there is a div that will hold paragraph tech with total price again, and a button proceed to checkout. After we'll create a function for handling the checkout, we will add there to this button, but for that, first, we need to implement the stripe. This is how the page will look like when it's empty. And if we now go back to home, add there some products and go to card, you can see there, we have the total price and also both products. Now if you click on remove them, we can remove them from the card, and when there's nothing, it will again show your card is empty. 17. Stripe: Go to stripe.com page, and we'll click there on Start now. You will fill there your data and click on Create Account. After you will verify your email, you can click there on Explore features. If you don't want to see this now, you can click there and leave there on this page, we will click on developers. There we will click on API keys. There you will click on Reveal Test key, then you will click on it, and it will automatically copy it to clipboard. We will head into Bend folder, and we'll save it there in our environment variables. Create a new one under stripe secret key and paste it there. Now we'll open a terminal. We will see the front end. And there we'll do NPM stripe slash Stripe Js. We'll install this library. We will import it in our card dot JS six, and there we'll create the constant stripe promise with this load stripe function, and inside, we'll put the publisher black key we have there in our stripe account. First, we need to create the route for checkout session. We will go into our Ben folder. In routes, we'll create a new file, StripRut dot JS, and then Index DJs, we will use this Stripe route b dot slash Stripe and StripeROut. And we didn't import this strip out yet. So let's go on the top of this file. Make there a new import from the route that we just created and call it a stripe route. Then we have everything in number indexOJS and we can go into Stripe Route and code it there. We will import stripe, Express to create the router and a config from the tenth to load the environment variables. Now we'll load them with this method. Then we'll create a new router instances and we'll initialize stripe with a secret key that we saved into environment variables. And on the bottom of this route, we'll export the router to be used in other parts of the application. And now we'll create the route for a checkout session. We'll extract the products from the request body. Then we'll create line items for each product. We will set the price data and product quantity. For price data, we'll set the currency, and then we will set the name of the product and we can also preview the image. Then for the unit amount, we will set the product price in cents, and on quantity, we will set the product quantity. Then we'll serialize the product details for metadata. We will set there again, product name, product dot quantity, and product dot price in sens multiplied by 100 to create the dollar value. Then we'll create a TIG block and in the Tide block, we'll try to create checkout session with stripe. For the session, we'll need to set multiple parameters. First, we'll set card to payment method types, then we'll set the line items that we set there. Then we will add product details to metadata that we seralized there. We will set a payment mode Make billing address required, and then we will set a success and cancel URL. Currently, I will set there local host 5173. Where is my front end running. But for this, we'll need to create a component for success and cancel options. And then set there its path. After this checkout session, we'll send the session ID as a JSON responds. And if there's an error in a catchblog we will log it and send the Bd request status with an error message. Now to show you how to create the success and cancel page, we'll go into pages. There, we'll create a new file, success.j6. Second one will be cancel.j6. We'll put there just a template. Now we'll go into eb.j6, and we will add these two new pages into routes and put there a path of success and cancel. Also, don't forget to import these two new pages. And now we'll go into B end folder, go there in the environment variables, and there we'll create the new one. And we'll call it front end URL and Titer our local host 5173, where is our front end running. And then for the success URL, we'll put this environment variable with success, which is the path for this page. Also, it can be done like this, but it's better practice to put the environment variable with the front end URL as it can change, and then we don't need to change all the paths in our code. Let's again double check the stripe out. 18. Cart Checkout: And now in our card J six, we will code the handle checkout function. First, we'll initialize the stripe and we'll await stripe promise that we did there. Then we'll transform card items into the checkout session, and we will load there the necessary fields. Then we'll do a Trikagblock. Inside the Tri Block. We'll make a post request to create the checkout session with the transformed items. Then we'll redirect the stripe checkout page using the session ID from the response. Now we'll check if the error is true and in case it is, we will log it and send a message into Console. Then if this wall try Block fails, we will log any errors that occur during the checkout process. And now don't forget to add handle checkout function to OnClick on our button that will take user to checkout. I will click there on Pros Checkout in my card. And it will redirect me to page. Now we can test the Cencl so when I will go back, I will get redirected to CenclPage where it's currently nothing. Now we will fill the data and we'll click on pay, and we will be redirected to a success page. 19. Cancel Success Page: Now on our success page, next to displaying a message that payment was successful, we'll be also siting our card item because after user bought the products, there's no reason to store them in a card. So we'll also seffactHok and use card custom hook that we created in card context. First, we'll destructure the clear card function from useCardHok and then we'll create the use effect hook. We can console log that the payment was successful, and then we'll call the clear card function to empty our card. Now we can test it. Let's add something to card, proceed to checkout, pay it with Link, and there you can see the card go reset. And we can also leave this dependency array empty. So this use effect will run only when this page will be loaded. Now, let's add some styling to it. So first, let's enter the items. Let's add some in height to screen. We can use background base 100 text based to content, then add some shadows, text sizes and backgrounds, and the page will look like this. Now we can code also the Cancel page. So let's get on the page now. Let's proceed to checkout and let's cancel it by just going back that will redirect us on the Cancel page. Now what we'll do, we'll just take the JS six from Success page. We will paste it there. We'll put there payment canceled, and we'll put there your payment has been canceled. Please try again or Contact support if you have any questions. Now, if you'll save it and go on the page, you can see the output there. 20. Shop: Let's make this shop button work. So we'll create a new page. We'll call it shop Dot JSix. We'll snipe there and we'll go into Eb do JSix. There we'll include it in the routes and also we'll import it. If we go to our homepage, we'll make our life easier by changing this button to an anchor tag where we will set HRF to shop. Now on our page, we click the button, it will redirect us to slash SHOP. And there we will code the page. We will Improduce state and use effect hook, Exiles and product card that we used on homepage. We will create state variables for filtered products category, and product itself. Then use effect to fetch the product items from the server. We will make an HTP request, get put there the route for getting the products, and with response, we will set the product state and filtered product state. In case it fails, we will log errors. Then we'll create a function for filtering the products. Will do F condition to error in case product is not an array, then we will create a copy of the product array, and in case category is not empty, we will filter our products based on a salted category. Then we'll update the filtered product state with a filtered product, and we'll create use effect that will call this filter product function whenever product or category will be changed. For J six, we'll create the main div that will have a maximum width of 1,300 pixels and center it with a mix auto. Inside, we'll do a div that will hold the picklist value selection for category. We'll put there a label with category and the pick list itself, where we will be setting the category, and in our case, the options will be all curses templates. Then on change there, we will set the state variable value. And last but not least, we'll put there the product card, and we will send there the filtered products. And now let's go on our page, and we'll select courses. There we have only two courses that we created, and there we will have a template, and that's it for our shop page. 21. Footer Subscriptions: To add a footer for our page, we can go on DZUI and select the one that we like. Then in components, we'll create a new one and we'll call it FuterGsix. There we is the snippet. In Abdo J six, we'll import it on the bottom, so below the routes because it's not routed. Import it Click on JS six and copy it. I will paste it into my footer on J six. Then I will go back, take also this footer. I will mix it with the one I just edit. So for the one I edit, I don't need the legal and I don't also need the services. Instead, I will put there the footer I took. Save it. Now we'll check how it looks like. As this is not our logo, we will put the H two with name of our page. So text size. Then we can delete this put there different ear. We can leave this as this.This also and now for newsletter. We'll go into Bend folder in models, we'll create in file, subscriber model dot s. We can copy paste product model, put it there. Now we will update the fields, and there will be just email of type string required True, and we'll put there also unique True. Then we need to rename this. So this will be subscriber schema. Take this, put it there, subscriber now we'll go into Index dogs. Then we can copyat Stripe, put there. Subscriber, change it to subscriber route. In Route, we'll click on new file subscriber route dot js. We can check how it's done in the product route dogs if we don't remember, and we'll redo it for subscriber out also. So there we'll have imports with the subscriber model that we created Express to create there the Router, and we'll export it. Then now we'll finish importing it in Index dogs. We can copypate the Stripe route and we will put there instead of it the name for subscriber route and also we'll import it from the subscriber route we have there. We will create a new route for posting subscriber to our MongoiB, we will extract the email from the request body. Then we'll create RChbok and we will create a new subscriber and call the create on subscriber model with the input of the extracted email from the request body. In case we will be successful, we will set the status to two oh one and send there a new subscriber. In case not, we will set the status to 400 and send an error message. In Index DJs, let's double check and make sure we are using the subscriber route. Now in Postman, we can test it. So there we will select a post route. We will input there our local host. We are using SAS subscriber, we select their body, raw JSON, we'll input their testing email, and we will hit Send. And now we have it there. We can also double check it and see it in our database. And now we can go back to Footer and create their function that will call this endpoint. We will import also use state, and we will create a state variable for email. I handle subscribe, we'ate we'll use this prevent default form submission behavior. In a normal scenario, when you will submit a form, the page will start reloading and with this prevent default, it won't happen. Then we'll do a trackage block. We will send a post request with the email as a payload. Now also before we end this, we need to send some notification. Let's import there U Snack Bar from notice tag, use there the snack Bar hook, and we can show success notification with clearing the email from the input box that user put there. In case this ride block fails, we'll log the error and send a notification about it. Now let's move to the bottom of the page. And there we have the subscribe button. For the input tech, we'll change type to email and we'll also set value to email, and we'll make it required so user cannot just subscribe without filling an email at their unchanged that will set our email state. We will set type of our button to submit, and the main thing to our form, we'll add on submit and call this handle subscribe function that we did. And I did the type of there. I'm not on local host 5,000, but it's 3,000 and we didn't import Exiles. So let's fix it by importing it now. And indexOJS, we are not using subscribe but subscriber. So now we can fix it by just dding there R at the the value of email, we can subscribe. And now we subscribe successfully. Email got reset, and in our Mongo Dib we can see the new email. I would do that one more thing. Instead of company, I would put their support. I would put their email. In some cases, we can put there also a phone number and we can delete these two Anchor texts. 22. Stats: Now we'll create stats component. In components, we'll create a new one, stats.j6, go on to admin.j6, and we'll addi their intermeddv. We will import it. We will import use effect and use state hooks. Then we'll define the state variables for stats and loading status and we'll create use effect hook to fetch the stats when this component will load. We'll create an asynchronous fetch stats function, do a tricagblock, make a good request to fetch the stats, and there we'll put an endpoint, but we'll do it after this component. We will check if the response is okay, and in case not, we'll send there an error. Then parse the JSN response and update the stat state with the fetch data. In the end, we'll set loading to falls. If there will be an error, we will log it and set loading to falls also. Then to start the data fetching, we need to call this function. Dependency array will be empty, so this use effect will run after the component loads. Then to see our data in a correct format, we'll use a format currency where we will send the value and we will get rid of the digits number. For JA six, we'll create a grid layout that will use three columns. On mobile screen, it will use just one. And inside, we'll put these text boxes from DZI. Well input there the available balance, pending balance, and total purchases. We'll check the loading state, and in case it will be loading, we'll put there loading message, and in case not, we'll put there the available balance or the total charges. Now, you can see it already on our page if you do slash Admin, but we don't have anything because we didn't code the route yet. And now we will code it. We don't need to create any new one. We'll just go into Backend folder and then stripe JS. It will use the Get method. We'll put the path of slash API slash Stat. We will do Trcchblog we will retrieve the account balance from Stripe. For the bugging, we can log in inter Console. Then we'll extract the available and pending balance amounts in USD, and we'll need to convert it from cents to dollars. Then we will list the charges from Stripe and we'll get the total number of charges. We will send the balance and charges stats as JSON responds. And in case it fails, we will lock in errors in catch block. Now as we have the route and we can use the endpoint, we will go into stats and there we'll insert it. It will look like this, and now if we go on page in admin dashboard, you can see the values got changed. But for the balance, it's a bit different because the values are actually correct. But as we are using test data in stripe, many are not loaded into our bands. And now we can hide the footer on our Admin dashboard, so it will look better. We have already imported their use location. Now we'll use it there and also use constant is admin route and check the path name starting with slash Admin. Then we will put it on the bottom of the page, and we'll return the operator there. In case this admin route is true, we will show nothing, and in case it's false, we will show the footer. Now, if you will go on the page, you can see that footer disappeared from our admin dashboard, and if you go onto the customer facing, the footer is still there. 23. Jwt: And now it's time to secure the endpoints. JWT is commonly used for authentification. How this will work. When user will log in, which we will also create, the server will create a JWT or JCN webTken and it will send it back to user. Then the user or Admin, will include this JWT in headers of the requests. This will allow the server to verify the user without some need to query the database each time. In our Bend folder, we'll create a new folder. And we'll call it middleware. In this folder, we will create a new file. Of middleware Js. Now, we'll go to environment variables and we'll create a new one, which will be called JWT secret, and there we'll generate our own secret key. How we can do it is that we will open a terminal. We will pace there this command. And it will generate the string for us. Now we'll take it, put it into JWT secret, and we can continue in our middleware. First, we'll import Config from the ten and also JWT from JSN WebTken. Then we'll call Confit method to load the environment variables, and we'll create a middleware function to authenticate users using JWT. We'll retrieve the authorization header from the request, then we'll do a check if the header is missing or doesn't start with better. And in case not, we'll send a message with no token authorization denied. Then we'll extract the token from the authorization header, and we'll do the verification in a tricachblock. There we will verify it and save it into a decoded constant. We will attach the decoded user information to the request object and we will pass control to the next function. In case this fails, we will log the errors in catch and on the bottom of the file, we'll export it as off. Now we'll go to product route dot gs. There we'll import this authentication, and we'll specify the off as the second argument there in the post route. We will now put it also into delete route. And update route. Now, if you will try to update the product, it will fail. Let's now create login and register so we can actually pass this off middleware. First, in models, we will create usermodel dot js. We will import Mongoose and create user schema. For user, we'll need email, which will be type string, required and unique, and also password, which will be typestring and also be required. Then we'll export this model as user. Now, we'll create a new folder and call it Controllers. Inside, we'll do a new file of controller dot JS. We'll import the necessary modules and libraries. We will create a new router instance, and we can start with route for user registration. In Tricagblock, we'll extract email and password from the request body. Then we will check if the user with given email exists. And in case, yes, we'll set status to 400 and send a message that user already exists. Then we will has the password using B crit, and we will create a new user instance with the email and the DHHedPassword. We will save user to database, and we will generate a JWT token and set expiration to 1 hour. Then we'll send a created status with a token and success message. In case this fails, we will catch and log the error. We will continue with route for login in Tricagblock. We will extract email and password from the request body. We'll try to find the user with a given email, and in case it doesn't exist, we'll set the response status and sent a message with invalid credentials. Then we'll compare the provided password with the hashed password we are storing in our database and use if condition and in case it's true and the password is matching, we will create the payload with the user ID and email. Then we'll sign a JWT token with payload, secret key, and expiration time for 1 hour. Otherwise, we'll return status 400 with message invalid credentials, and in case this drive block fails, we will lock the error. Also, we'll export it as Afouter. 24. Login Register: Now we need to create login and register form. Let's go into front end folder pages, and there create a new file, Log in D J six and another one Register DJ six. We will use this nippeS there. Then Abdo J six, we'll route it like this. We'll put there a path of login and register, and we'll also import it. In register JA six, we'll import necessary modules and hooks. We will be using Use State Hook and also link and use Navigate for navigation. For making HTD P requests, we will import Exis. We'll declare hook for navigation, and then we'll initialize variables for user data, and we will set their name, email, and two passwords for confirmation, and default value will be empty. We will continue with state variable for status message, where we will load error or success message in case we will fail or succeed. He state variable for success is a flag that we will use for displaying the message on Y for the error or success. Default ly, we'll set their falls. Then we'll do handler for input change. And there we'll update the user data state with a new input value. Also, we will clear the status message and set our flag to falls. Then it's time for the handler for form submission, and to prevent the default form behavior, we will use prevent default method. That means submitting the form won't reload the page. Then we will check if the confirmation password is same as the first password. This will be handling the error scenario because we have the not equals to, and we will set the state variable that we are using as a flag to falls. Then we'll set status message passwords don't match, that we will also display on UI in JA six, and we will return. Rest of the functionality, we will wrap in Tricag we'll make configuration for the Es request, and for the headers, we will set content type to JSON. After we will send the post request to the register endpoint with the user data, and this is the endpoint that we coded in our controller. We will also attach config with the headers to it to inform the server that the reckless body contains JSON data. And after we manage to post everything, let's set a success to true, then display the status message with the registration successful and then navigate the user to login page. In case describe block fails, we will set a success to false and we will send an error message. Now to JS six itself, we will use the Flexbox layout with flex direction column, so the inputs will be below each other. Then we will center the items with items center and Justify center. We'll set Mnheiight to screen, and we'll set some light background. We will create heading for our form, which will be register and now to the displaying of the status message. We will check if the status message is true or false. In case it's true and some status message is actually set, we will render it, and we will render it in paragraph tech, and then we'll just play with the color. We need to check is success variable, then use ternary operator, and in case it's true, we will set it to green color, and in case it's false, we will set it to red color. Way we will get the registration successful in green, errors in red. Now, to the form itself, we will add there on submit that we did there. And now we will insert all the inputs for user name, email, and both passwords. For the first input, it will be of type text with placeholder user name and name set as name. Value, we will set userdata dot name. This is from the state variable that we defined there, so user data dot name. And on change, we'll call the change input handler, which is the first handler that we did. Then for classes, we will set some shadow, border radios, with to fool, bedding, and gray text color. Now, we can copy paste it, and we'll set that type of email, placeholder to email, name to email, and user data to email also. Otherwise, it will be the same for password as well. But again, the type, placeholder and name will be different. For value, we will again want to set stay variable for a password. Then for confirmation password input, we'll just add confirm password two for the name, and in user data, it's also called password two. Then a button for submitting the form, so we will ada type submit. Then we can select the color for the button on however, let's use some darker one, text white. We can increase the font weight, bedding, border radius, and with two full. That's it for the form. Below the form, let's add some additional text for asking the user if he has an account already. And in case, yes, he can click on sign in link that will send him to a login page. We will go into Index DGS. There we'll do Abdo use for off router. Now you move to the top of the file. And we'll import the router as of router. This is how the page will look like. Let's input some test data and click on register. So we were directed onto a login page. And in database, we can see under the users our newly created user with email and hast password. For login page, it will be almost the same as in register page. We will do the Imports there, declare a hook for navigation, set state variable for login data, with default empty values, set state variable for status message, then start with handler for input change where we'll start with updating the login data state with a new input value because this will be called on our inputs that we will do in case status message is there, we'll reset it with setting there an empty value, then we'll do the submit handler. We don't want to form to reload our page, so we will prevent defaults. Then we'll do a wreckage block. And we will send the post request to the login endpoint with the login data, and we'll also log it. Then we'll save the received token in local storage. This is the JWT token, and after we log in, we'll have it in our local storage, and we'll be able to use the page as the locked users. After the token will be deleted from our local storage, the page will see us as the unlocked users, so the functionality for locked users won't be there for us, or any functionality that is behind authorization for JWT won't be accessible for us. Then after everything is successful, we'll be navigated onto admin page. In a catch block, we'll create if condition to see which error type we got. In case it's response, we'll set status message with the response error. In case it's request, we'll console log the error request. And otherwise, we'll just log the error. For J six, we can reuse the UI that we used in register, but we will have there less inputs. Heading instead of register will be login. Again, we'll render status message in case the status message variable will be true, and then we'll render it in paragraph tag only with a red color because for login, we'll be redirected to an advent page, so there won't be any time for seeing the successful message. Then we'll create tech for reform, and on submit, we'll call the submit handler. Now we can copy PACD inputs from Register JO six. We'll start with the email one, but we'll change the value to login data dot email that we defined there. On change, we'll call the change input handler. Then for the other input, it will be on value login data password. Then as a submit button, we'll create their login button, blue background, and on how we'll use darker blue. And also on the bottom below the form itself, we will add a text. No account yet, and in case user won't have account, can go on the register page with this link. Now it's time to test it, so we will click there on sign in. Now I will use the password I set. I will click on Login and it log me in. 25. Admin Navbar: I will show you how you can create an admin nav bar that will be shown only after login to admin dashboard. On this navigation bar, well then put a Logout button in case our admin will want to logo. First in components, we'll create a new file and we'll call it adminnavbr dot Jsix. We can take our current NAV bar and paste it there. Then we will delete the car icon, Mobile navigation, add a function for logout, that will remove the token from the local storage and also will redirect us to the customer facing. Then instead of this unordered list, we can put this button there and we will add our Logout function to OnClick. Now, we'll go back to apt JSix and we already defined the dislocation and is admin route. And you can see we are using this for a footer. When we are in admin route, the footer is hidden. And we can just copy this, put it on top there, take this navigation bar and put it instead of the footer. But in case we are in admin route, we will use the admin navigation bar. So internal operator on true position, we will put this snap bar, but we will at the Adminovbr and we will import it. And as we copy paste it, don't forget to change this name to Admin nav bar. And then we will use the export there. And import it into our Ab Doja six. Now, if you'll go onto the page, we have di Logout, and if we click on it, we will get to customer facing. And now for the admin dashboard, we are using these admin routes. And these admin routes are wrapped in protected route, which is one of our components. In this protected route, we are having this authenticated method, which was set to true because we were not using it when we were developing the project. But now we have the JSON webtokenFunctionality, so we can delete this return to True. And uncomment this return, that will check if the token is in local storage. In case it is, it will return children and it will render the Advantage board, and in case not, it will navigate us to login page. We did slash Admin. We will login, and we go to JSN WebTken into our local storage. Now, we'll go back to customer facing and then back to Advantage Board, we don't need to login again because we still have it stored. 26. Jwt Auth For Requests: We are locked in and we'll try to call the endpoint that needs this authorization. So let's click on Edit So file, and then we'll try it and we will get an error. So if you will think of it as some passport, we have the password, but we are not showing it. We will now need to go to pages where we are editing, creating or deleting the product or simply to pages where we want to authorize the request. Now, as we'll be using this part of code, I will explain it there on a new page, and there we'll be adding it to our request. First, we need to get a token from the local storage and save it into a token. And now we will set a conflict that we'll pass as an argument to our request. First header will be the authorization, and we will reuse our saved value from the local storage. Then we will set the content type to Jason. The setting is Exos doing for us, so we don't even need to set this. It can only help us to prevent some issues in the future. Now, we will copy this and we'll go on to page where we want to edit authorization. I will paste it there, and now I can reuse the config. So I will go into my function, and there next to data, I will include as third argument, the config. Now I will save it and I will try on my page to edit the product. And it's working. Now you'll edit also for delete and creation as a second argument there and as a third argument there, we will delete the product, and we are also able to create it. And that will be it for the authorization with JCN lab tokens. 27. Ui Fixes: Now let's fix the teams and play with the UI bit. We will go into Tem Tagle button and there we will use light and dark team instead of the Business one. For set this dark mode, we will set dark also don't forget we need to set the data team there in index HTML, and I will use their default light. We will update the UI of the Admin Dashboard. Let's go to Admin J six. First, we will remove their background gray to 500. Then we will delete text colors, and we can change this background to background base 100 and on Her background base 300. And then we look like this. Now we will go to stats and there we will set text base 100, and we will put it also for the other stats. And for a third one, we will also add a different background. We can add the accent and also text secondary content. That way, it will look like this. Now we need to update it also there on our at item button back on Admin page. We will put there also text base 100. Now only the logout is missing, we'll go into Admin Navbar, and there we'll remove this text bag, and we'll set there a DCI class button. Also, we'll remove there a background base to 100, and if we'll go to a second Navbar, we'll remove the background base 100 also from there. Now, our admin dashboard will look like this, so we can logo, and there we can just update some colors of buttons we have there. And also, I would add some vertical margin to sections on homepage for this div that is holding the Welcome text, and I will add the margin to bottom 24. Then we'll change the color for this Show button and we can change it for button accent and we will go into dark mode. We'll want to highlight the cards a little bit. Let's go into product card single and we'll set there background base 200. Now we'll fix it also for login and register. So let's go to the login page, change this for background base. Go on to register and do the same thing, and then for edit, delete or create product. Let's do it also. So there based to 100, we can copy this. Put it also there on delete product. And there on edit product, from there, we will delete the text colors. Change this two button. For the back button there we'll put instead of background white background base 200 and we'll set the border to base 300. So we can again copy this base and change it to all borders Now it will look like this. For create product, we will do the same thing, so we'll change the borders. Delete the text color. Set their background base 100. For the Back button, I will set the button class and I miss there one more border, so there. Also with the text color, And now for the deletion, we will change it to background base 300. From there, we will delete the text color. From there, we will also delete the text color, and for the link, we will add the bottom class. So we can actually delete the background base we edit. It will look like this. And that's it for the admin dashboard. Now let's check how it looks like on a mobile phone. And we need to fix the logout button. So let's go quickly to Admin navigation bar. And we will remove this hidden, and we'll just leave their display flags. And now that should be it. So let's check the dashboard. Let's try to add item. Let's try to edit item. And everything works. And by the way, if you would like to show only, for example, first three items on your homepage, you will go to Home J six, and there below the use effect, you will create the constant latest product, and you will slice the product you got into the state variable by the number of products you want to show. So I will use their tree there I will get this latest product. I will input it there, save it. And now on my homepage, I will have only three items displayed. And maybe I will get there some more margin. So let's change this margin to top to actually vertical margin. Now, we'll go to card and also change the background to background base 200. One more thing we need to do in tailwind configuration JS. We'll go there, for example, above plugins and we'll set there also dark mode. Then we'll make sure our dark mode will stay even if we will be going through different pages. 28. Deploy Vercel Updated: Will use versal and deploy our website. What we need to do, even before pushing this project to Git, we should add Git Ignore because we need to ignore the environment variables mainly from backend. We are storing our secrets and passwords, and the Git Ignore is located next to front end and Backend folder there. You will create it dot Gidgno, put there these values. But the main one is Bendslash dot NF. Now we need to check if we have this environment variable for Bend base URL. This one, we now need to use instead of all local hosts 3,000. For example, on homepage, we have this dot get HDP Local Host 3,000 product, and now we need to change it for the value that we have there in our environment variable, which is the Vet react app. Let's take this name on Home J six, let's change. And also be careful. These are backslashes, not the normal single quotes. Now, like we did there, we'll do for all local hosts we can find in our project. That means you will click on this search or do Sutter o Shift F, and there you will input local host 3,000 to find for you all the files where you didn't change this yet. You will change all local host 3,000 to this environment variable. You can see the changes I did there. After we did this, we'll go into Bend folder and create a new file. You will name this file versal dot JSON. After you will create this file, you will insert this because it will specify for Versal where is our index dogs in the folder. Then we will go into front end folder, and there we will also create the VersL dot JSON. But there we'll insert just rewrite source and destination to index dot HTMOEactly like I have there. In case you will be then failing on deployment, go to package the JSON and like I did, change Cloudinary to this version. Before it was to some version 2.2, and when I was deploying it, it was failing. Then I lowered the version and it passed. But now I will show you how I do the deployment in versal. Versal will be taking our project from Git repository. So what we need to do is that we will just hit a command sotorShift B. There we will hit Publish to Git there you will put the name for your repository. I can put there, for example, products two, hit Enter, that way it will get published to Git. In case you will do any changes. Let's try it. Let's put there index CML, something like this and save it. You will then have your changes there in source control. And in case you want to update the repository and push it, you will click there on Plus. You will put there some Commit message, update, and you will click on Commit, and it will put these changes also to Git where you will then have your updated project. Now you will go to Versal and there we will log in. I will click on a new project there I will have the project I just pushed. So Marin digital products, I will click on Import. There I will leave Or for directory, you will click on Edit and backend continue and then in environment variables, you need to set all the environment variables you have. So we will go back to VS code. If we go to our Bend folder and to environment variables, we will need to copy paste there all these environment variables we have there. So let's start. First, I will input there JWT secret and click on AD, and then I will add the next environment variable, front end URL, and this one be Local host. This will be the actual URL of the front end we will deploy after this backend. So for now, we can leave this empty, d it like this, and we will update. But now keep adding rest of all the variables we have there. Now you can see I edited them there and we will click on Deploy. Now our page is deployed. You will click there on your project, and we'll do deployment of the front end. We'll click on Add New project. There we'll use the same repository as we used before. We will set the preset for wet. There we will click and edit to front end. So the root directory will be front end. And then to environment variables, we will check which one we have in front end folder and it's this one, so we will copy it. Put it there and into value, we'll put the URL of our backend. So let's go to our Bend deployment and then we'll copy this URL. We will paste it there. But before the URL, we'll put HDDPS slaSAS. Otherwise, we won't be able to do the request. Now we click on AD, we'll hit the Deploy. Now our page got deployed, and now we need to take this URL of the front end. Go into our Bend application deployment, click on the project, click on settings, go into environment variables. And there we need to input the front end URL that we got by this deployment of front end. So we will click on Edit. We will put there the URL, and we will put there HTPS. Now we will click on Save. And after we will change this, we will go into Deployment and there redeploy and redeploy. Because as we change the environment variable, we need to redeploy the back end. And now on our page, we can try to buy a product, so edit there, proceed to checkout, and the stripe is working. We can just pay this that's it.