Vue. js 3 & Firebase For Beginners | Chris Dixon | Skillshare
Search

Playback Speed


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

Vue. js 3 & Firebase For Beginners

teacher avatar Chris Dixon, Web Developer & Online Teacher

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.

      Welcome To The Class!

      1:31

    • 2.

      What You Will Need

      1:38

    • 3.

      Build Tool Setup With Vite

      5:42

    • 4.

      Project Images

      2:16

    • 5.

      Views Or Components?

      1:39

    • 6.

      Views & Routes

      6:29

    • 7.

      RouterLink & RouterView

      6:16

    • 8.

      Named Routes

      2:15

    • 9.

      Nested Routes

      4:23

    • 10.

      The About View & Child Components

      9:10

    • 11.

      Named Views

      6:20

    • 12.

      Project UI- Section Intro

      0:25

    • 13.

      The Menu UI

      4:14

    • 14.

      The Basket UI

      3:03

    • 15.

      Fonts & Base Styles

      7:31

    • 16.

      Menu & Basket Styling

      5:37

    • 17.

      Header Styling

      4:34

    • 18.

      Admin: Add New Pizza Form

      5:54

    • 19.

      Admin: Pizzas Component

      3:36

    • 20.

      Admin: Listing Orders Component

      4:25

    • 21.

      Home View

      7:33

    • 22.

      Form & Table Styling

      10:26

    • 23.

      Options Or Composition?

      3:37

    • 24.

      The NewPizza Object & Data Binding

      5:46

    • 25.

      Ref’s & Adding To Basket

      7:33

    • 26.

      Looping Over Pizzas

      5:30

    • 27.

      Checking For Duplicate Basket Items

      4:30

    • 28.

      Removing & Changing Basket Quantity

      4:18

    • 29.

      Computed Properties

      3:34

    • 30.

      Composable Files

      5:12

    • 31.

      What Is Firebase?

      1:48

    • 32.

      Firebase Setup

      7:30

    • 33.

      Adding Pizzas To The Database

      7:30

    • 34.

      Getting Pizzas

      9:17

    • 35.

      Listing & Deleting Pizzas In Admin

      11:01

    • 36.

      Creating Orders

      8:15

    • 37.

      Fetching Orders

      7:57

    • 38.

      Looping Over Orders

      4:24

    • 39.

      Deleting Orders

      4:43

    • 40.

      Show & Hide Admin Blocks

      5:07

    • 41.

      The Sign Up Function

      7:27

    • 42.

      Creating The Sign In Form

      8:57

    • 43.

      Modal Styling

      8:41

    • 44.

      Modal Toggle

      4:16

    • 45.

      Logging In & Out

      9:34

    • 46.

      Detecting Auth Changes

      5:07

    • 47.

      Styling The About View

      5:18

    • 48.

      Completing The Admin View

      4:55

    • 49.

      Restricting New Pizzas

      2:14

    • 50.

      Adding The User To Orders

      6:45

    • 51.

      Filters & Global Properties

      8:52

    • 52.

      Realtime Pizza Updates

      4:53

    • 53.

      Realtime Order Updates

      2:30

    • 54.

      Unsubscribing From Updates

      2:49

    • 55.

      Adding New Users

      7:37

    • 56.

      Retrieving Admin Users

      5:37

    • 57.

      Updating Regular Users To Admin Part 1

      10:48

    • 58.

      Updating Regular Users To Admin Part 2

      6:42

    • 59.

      Firebase Rules

      17:10

  • --
  • 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.

197

Students

4

Projects

About This Class

Vue 3 is an amazing framework for building web applications! Learn all about it in this class focused on Vue.js version 3. This class is completely project-based, and together we build a pizza restaurant application! 

Discover everything you need to know from structuring an application using pages and components to the latest features such as the Composition API and script setup. You will learn about Vue 3 reactivity, and how to make use of new and existing features including lifecycle hooks, watchers, and computed properties.

Project images can be found here.

If you get stuck, you can also compare your code to the final project at various stages using the following link:

https://github.com/chrisdixon161/vue3-firebase-for-beginners-project-stages

The class is designed to be friendly to developers new to Vue.js or Firebase, however, general web development knowledge is required to get the most out of this class. You should have some experience with JavaScript, either regular JavaScript or experience with another framework such as React or Vue 2.

We will cover routing to switch between pages, how to enable child routes, and using multiple named router view components.

We also make use of Firebase as a backend service for our project. We set up a database to store our orders, menu items, and user data using the latest Firestore web version 9 (modular). This database will also be in real-time, which means our project is kept up to date with any changes to the database as they happen. You will learn about many methods to create, read, update, and delete data.

Firebase authentication will also be set up to manage our users. We will enable user accounts and also multiple access levels. This will enable regular users to place orders and admin users to manage the menu items, orders, and other users.

Meet Your Teacher

Teacher Profile Image

Chris Dixon

Web Developer & Online Teacher

Top Teacher

Hello, My name is Chris and I am a Web Developer from the UK. I am an experienced trainer leading web development bootcamps and also teaching online courses.

My main areas of interest are Vue.js, WordPress, Shopify, Javascript, eCommerce, and business. I am passionate about what I do and about teaching others. 

Whatever your reason for learning to build websites you have made an excellent career choice.

My personal motivation was to become my own boss and have more freedom and flexibility in my life. I also enjoy the technical challenge it provides and the way it constantly evolves. I built my first website back in 1999 and I have watched the web evolve into what it is today.

I try to make my courses enjoyable and try to remember what it was like wh... See full profile

Level: Intermediate

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. Welcome To The Class!: [MUSIC] Vue.js is an amazing and easy-to-use front-end framework for building web applications. In this course, we're going to use it to build a pizza restaurant application packed with features. We will discover all the new features of Vue Version 3 while building this project. In addition, we'll link up our project to Firebase, to apply a back-end to our app. This gives us a real-time database to store our users, our orders, and pizzas from the menu. It will also be used to provide user accounts and authentication, including admin and regular users. The project has many features such as a menu and a basket, add items too and the ability to place orders. We have an admin area where authorized users can add and remove pizzas from the menu. Your orders can be displayed and also deleted, along with the ability to set other stuff as admins do. All of this data is driven from our database. Hi. I'm Chris and I'm an experienced web developer. I've also taught over 100,000 students, both online and also in person too. If you want to build amazing front-end applications with Vue Version 3 and also make use of the Firebase Database and Authentication, then this is the class for you. I look forward to seeing you in the first lesson. 2. What You Will Need: For this cluster there's no additional purchases which you would need to make. All of these software which we are going to be using will be free and open source. Then there's only in fact a few things which we're going to need. But before we do though, let's now take a look at the requirements so we don't get too overwhelmed. Since this is a web development class, you should at least know the basics and it will be assumed you know HTML and CSS pretty well and also comfortable using JavaScript. You don't need to be an expert, but you at least need to know the basics. I do have classes available on each of these if you need to brush up first. Also, if you have experience of other JavaScript frameworks or libraries such as Angular or React, or even Vue Version one or 2, this will be a great benefit, but not essential. Also, we'll be making minor use of the terminal, but if you've not used this in the past, don't worry about it. We're only going to make use of a few simple commands. Visual Studio Code, it'll be the text editor which I'm going to use throughout this course. It's currently very popular in the web development world, but you can use any other editor which you prefer. If you need it, the download link is here to follow, and it's also completely free. Another benefit is it comes with a built-in terminal, which we'll use to set up our project and install the packages which you may need. But also if you are comfortable with the terminal and you maybe have your own external ones such as Item, this is completely fine to use too. With this in mind, let's move on to the next video where we'll take a look at how we can set up our project using Vite. 3. Build Tool Setup With Vite: To begin our project, we'll start off with a local setup. We can run Vue.js on our own computer. For this, we'll make use of a build tool called Vite. Vite was in fact created by Evan You who also created Vue.js. This build tool will allow us to make use of view single file components, which is when we write a part of our website called the components into a single file. This will keep things nice and organized for us when we're creating our project. But it will also means we need a build tool to take all of these separate pieces of components and bundle them together to make our app. Vites also gives us many other benefits, such as a development server, hot module replacement, which will instantly update the project in the browser without needing to refresh the page, we will make a change. There's also many other advantages too. You will need to install Node.js If you've not already done so. You can do this from nodejs.org. Go ahead and download the latest version for your operating system. I'm using a Mac book if you are using Windows, it should automatically detect this to. If not, just click on the other download. This download will also include npm, which we'll use to set up our project and also add packages as we need them. Next, over to the terminal, which you can use a standalone terminal if you prefer. Or you can use the one built into Visual Studio Code. The simplicity, this is the one which I will use. Go into Visual Studio code, into the options, terminal, and then new terminal. We'll now have this terminal at the bottom, which we can use to write commands. If you're new to the terminal, it's nothing to worry about we just need a few simple commands. You can use a terminal to navigate your computer's files and folders, just like you can do with the mouse by clicking inside of a window. Here though, we use commands but we'll need to navigate into where we want to add our project. I want to keep it simple and add it to the desktop for easy access. Currently I'm in the home directory for my user. We can use the LS command to list all the files and folders inside this directory. Then we can use the cd command to change into any of these directories which you want to. For me it's going to the desktop, which we can see listed here. cd to change in it to the desktop. This is also case-sensitive. Hit's "Enter". We can now see we're inside of the Desktop directory. But if you want to, you can change into any other directory which you want to. But just before we create the project, we need to double-check that we have nodes and npm correctly installed. To do this, we'll type in node dash v, which stands for version. We'll see returned back the version number which is installed. My current version is Number 18. You will need at least node Version 15 to continue. Obviously, any version number above 15, you'll be good to go. But if you see an error message or a warning, you will need to go back and makes sure node is correctly installed. Next, we can double-check npm, so npm dash v. We don't see any issues here. To create a view JS project using Vite, we need to run it this command. This is npm in it. View. At latest. Its enter. It's asking us to install the following packages, which is Create View. This is fine. Press "Y" or hit "Enter." Then we asked a series of questions. First of all, the project name, mine will be pizza planet. You can change this if we want to. We're not going to make use of TypeScript. We'll hit No, we don't need JSX support, so we'll go for, No. Do you want to add the view router? Yes, we do. We'll take a look at this soon. Do want state management, we're going to select No since we use a state management inside of composable files. We'll also take a look at how to do this throughout the class. We won't be covering intestine, so we'll hit No and No. Yes. Length. That can be yes. It's up to you. Prettier? Yes. Good, and we are now all done. Remember, that earlier we use the cd command to change into the desktop directory well now we've created a new directory called pizza planet, and this is now inside of our desktop. What we need to do, as it says here, we now need to change into the pizza planet directory, so cd, pizza dash planet or the project name of your choice. We then need to run npm install, which will install all the packages we need for our Vue projects. Good, the packages are now all installed. To start up our development server, we need to run npm, run dev. It's Enter. This will then give us a web address to open up inside the browser. We can copy this or Command or Control click, which will open this up inside of the browser. This is our project starter, meaning everything is working correctly. Since we set up the view router, which we're going to talk about a little bit more later. We'll also see a Home and an About link to switch between. 4. Project Images: Throughout this project, we will be using some project images. You can download your own if you prefer, or you can use the exact same ones which I'm using. If you want to follow along and use the same ones, inside of GitHub, I have this repository which is available at github.com/, my username, which is chrisdixon161/pizza-planet-v3-images with a dash in between each word. This is a simple repository. All we have inside is this images folder with the six images which we're going to be adding to our project. If you want to use these, just click on the Code button. Download the zip file. Once this is finished, you can click on this to open this up. Then we need to add this to our project. First of all, go over to Visual Studio Code or your text editor. What we need to do is to open up our project inside of here. Drag over the project folder which we created in the previous video. Mine is stored on the desktop. Drag this into Visual Studio Code. We now see all of the files and folders for our V project inside the sidebar. We may also need to reopen the terminal. The Terminal and then go to New. We are currently inside of a pizza-planet project. We can run npm, run dev, which will again open this up with this link. Command or control click. Good. Everything is fine. Then if we go into our images, and then into our project folder, which we just opened up in Visual Studio, go into the source folder, into the assets, and then we can drag the images folder inside of here. Hold this down. We can also see these in a sidebar if we open up the source, into the assets, and there's our images which we're going to be using for this project. Again, if you want to use your own images, you can add these to an images folder inside of here and use these as we progress through the project. But now with these available to use, we're going to move on to the routing section, where we'll take a look at how we can switch between pages. 5. Views Or Components?: In this section we'll take a look at some basic routing to allow us to switch between different pages inside of our application. But just before we do this, so I want to take a quick look at two different files which we'll be using. These are views and components. Let's see what the difference is between these two. First of all, we have the idea of views. These are files which hold the contents of each page of view. A typical app or website would use links such as Home, About Us, and Contact Us and each one of these will link to a new page. This is how we will make use of views. Next onto components, and components are generally smaller pieces of our application, which can be either a stand-alone item or it can be reused across multiple pages of views. A component could be any block of content such as a location map, a product on an e-commerce store, or even just a simple block of text. Components can also be reused either by repeating the contents over multiple pages or passing a component dynamic data. Component files and view files are the same. They also have the same contents and structure since they are both view components. But having them in different folders keeps things organized. To recap, a view is a page which we can use to switch between on our application. Generally, a component is a smaller, often more reusable piece of our page. We generally store these in their own folders called views and components to make these more organized. This is what we are going to be taking a look at next. 6. Views & Routes: Inside the browser, we have our project open. We've briefly looked at this Home and the About links which are provided. We switch between these two pages or these two views which we've just learned about. We can see these two view files inside of the Editor. Jumping into Visual Studio code with the project open inside the source folder where we'll write all of our code. We have this folder called views. This has the About and the HomeView which we just switch between inside the project. Inside of these files will look pretty familiar if you've used Vue.js in the past. They're just component files where we have the script section, where we write all of our JavaScript code and import any files which you may need. Then also a template area just here. This is the area where we write our HTML. We can also have a style section which we can see if we go to the AboutView. Above this inside of the source folder, we have this router folder with an index.js page. We have this since we chose to install the Vue router package when we first set up this project. The key here is at the top of the file where we import any one of our views. Here we can see we are importing the HomeView from the views folder. Then below inside of createRouter will have this routes array. Each one of these routes is an object as we see here. We see the file path, which is forward slash. This is the home directory. We can give this router name, which we will refer to during our project. Then link this to a particular component of view which we just imported. After this, we also have the About section. You may be wondering why we've not imported the AboutView up at the top alongside the HomeView. Well, often we would do this and also important the AboutView, just like we did with the Home components. But instead, this is demonstrating a slightly different approach where we use something called lazy loading. We lazy load the page to only download the page contents when the user actually needs them. This router page overview, let's now create the views which we need for our project then we can come back and add this to our array. Into the source, into the views folder, we can still make use of the HomeView and the AboutView for our projects. Let's just clear out the contents inside so we don't need the welcome components. Since we've removed this, we can also remove the reference to it just here. Instead of placing a Level 3 heading maybe text a home. We'll come back and add content to these views but for now, we can use the Level 3 heading to see which page we're switching between. The AboutView, remove the style. We don't have a script section. We have the wrapper, H3, and the text of about. Inside the views, we need two more files. Click on the "File" icon just about the top. We also need the AdminView, which uses the.vue extension. Create a template, a heading. This is for the Admin. We file the final view which we're going to be using is the MenuView. This one will be used to display all the pizzas which we have from our database and also a shopping basket too. Another template, the heading of menu. Then we can close these down and import these inside of our router, so the router index. These are all in the same directory as a HomeView. Let's copy and paste this three more times. The Menu and the MenuView. The next one is the Admin from the AdminView and the last one is the About. Since we've imported these at the top of our file, we can basically duplicate the Home section. Just below this, create a new object separate it by a comma. The path which we want to use is a string and this is going to be forward slash menu. This means if we go into our project and at the very end, typing forward slash menu, we'll see the menu view inside of the browser. Next, the name, which is the menu link. We'll use this soon in our project. The component which we want to display is the menu which we imported just above. Next, we have the Admin, write new object and separate it with a comma. This one wants to access inside the browser with forward slash admin, the name of admin link and the admin components. Good. The last one we need is About. The path of forward slash about and the order which we place this makes no difference to our project. The name of this link, the About link and finally the components. Good. This is all done. Make sure you have a comma between each one of these routes. Give this a save. If we now go over to the browser, refresh. On forward slash menu, we have the menu texts from our components. We also have Admin. You see the admin text, About, then the forward slash for the home directory. Currently, we still see the content of our four views alongside the existing content on the left. How does our app know where we want these views to display? Well, the answer to this lies inside of the RouterView, which we're going to look at next along with adding these new views to our navigation links. 7. RouterLink & RouterView: Inside of our source folder in the project, open up the App.vue. Inside of this file, there are two main things here to note. One of them is we have this navigation area, and this contains two router links. This has the text of home and also about, and this is the two links which we see inside of our project. The first thing here is these links are not the usual eight elements, which we'll see in regular HTML. Instead, they are surrounded inside of this router link. This is how the view router handles linking to other pages or components. It uses these instead of the a elements so the router can change the page URLs without reloading the page. We can also control the URL which we are linking to include dynamic routes, which may include variables, rather than just having the plain texts like we have here, we can also insert variables too. Also, you'll see this router view component down at the bottom. This is the outlet which displays our page contents, which we have inside of the views. This is where our page content is displayed, giving us full control rather than just replacing the full page content. Here we see this router view component is placed alongside the header. This is why we see them side-by-side inside the browser. Also, you notice if you go up to the top of the file, both the router link and the router view is imported from the view router package. With this knowledge, we can remove the boilerplate code from here and create our own links inside of the header components. Let's clean up this App.vue. We'll take out our router link, we no longer need this. Also remove this header content too. We don't need this HelloWorld example inside of our project, but we can reuse this file and convert it to be the header component. We'll call this the app header, will rename the file AppHeader.vue. Then inside the template we can place this AppHeader exactly where we want this to be. Since this App.vue file is the main component for our project, we can use this to give this some structure. Inside of our projects we always want this router view to render the page. But just above this, we can also place in the AppHeader. This means on every single page in our project will have the header at the top, followed by the page content. Next, remember we've changed this filename to be AppHeader.vue. This is inside of the components folder. Jump inside of here. Then rename the HelloWorld example to be AppHeader. We can also remove these icons. [inaudible] created the project. We don't need this for our project. Let's jump into this AppHeader.vue file. We clear out all of the contents. We currently don't need a script. We'll remove this. Remove all of the default styling. We've all the contents between the templates. I'm going to replace this with a HTML header. Row header place this inside of a Level 1 elements with the class qoute sight underscore title. Rather than just simply adding the text of pizza planet for our site title, we can also place this inside of the router link. This has an opening and closing tag. Then inside of the Tilder icons, the text of pizza planet. You can see we've got this underlying which data is an error with this component. This is because we need to add the two prop as an attribute. Since this is the site title, this is just going to link back to the homepage, which is forward slash. The next a navigation section with our navigation links. These navigation links are going to link to our views. We're going places just below our Level 1 heading. The nav elements. This is a series of relative links. The class of link which we'll link to our CSS very soon. The two attributes. This will be the home link, so we can link to forward slash and the display text of home. Duplicate this three more times. The next one is for the menu, which links to /menu. The last one is to link to the admin below the navigation and placing the Level 2 heading with the text of the Number 1 place for pizza Let's give this a save and check. Everything is working. We've now got the app header at the top and then we should see the router view content just below. Refresh. There's our Level one heading. We have our links, we have the Level 2 heading, but we still have this page content alongside. The content we want it to display in, but we don't have the required at styling. This happens because of the styling which is setup in our VP project by default. We could remove this and use our own.. Into the sidebar. Jump into the assets folder, various style sheets inside of here. Let's remove the base.css. We can also remove the logo, but we can't make use of this main CSS file, but instead we'll clear out the contents and add our own. Select all of the contents and delete this file. It's saved over to the browser. This is our header section. Now at the top. Now I have the page content just below or we can also switch between this page content with these header links. 8. Named Routes: As we've just discovered inside of the router link, we linked to various pages with these two attributes. Then inside it, placed in a hard-coded URL to link to. This works completely fine, but we also have available something called name routes. Name route may be a benefit if we use the same link multiple times. Imagine inside of our project we had many links to this menu page, but instead in the future if we maybe wanted to change this URL from forward slash menu to be something forward slash pizzas, we need to change all of these links manually. Alternatively, we can link to the name, which we gave it inside of the router file, so jumping to the router in the index.js. Remember each one of these links has a specific name. We can pass this in as an object, instead of the forward slash, placing the curly braces to create an object. The name property, which is equal to home. Just like any other view Js attributes, if we're placing in dynamic data inside of here, such as our name, we also need to place in the colon, which is v bind to make this dynamic. This tells a view router, that this is not to be read as a string of text, but instead take a look for this name which we have in our router file. We can also do this with the rest of the links. Copy this and we can place this at the top of our title. The second one. This was for our menuLink, which matches the name inside of our router file. The number three. This was the aboutLink. Then lastly the adminLink. Save this and head over to our project and it should be automatically updated since we're using vite for our development server. Click on all links and everything now still works the same as before, but now what we're using is these name routes. 9. Nested Routes: Our current setup allows for a router structure just like this. We have our RouterLinks in the header, and then replaced the contents of these views where we wanted it using the RouterView. This leads to a pretty simple URL structure. We would have our main URL and each view would add to the end the name of admin, menu, or about, for example. But sometimes, our structure may need to go even deeper. One of these views may also need links and a place to display the content. An example of this is in our projects About Us page. On the left is the About Us view, and we can place in any content which we want to. Additionally, we can also provide extra links as we see here with the history, delivery, and locations. On the right, we have the desired structure which would be the About Us view then the nested links of history, delivery, and locations following. To structure this, we again need to make use of the view router using the RouterLinks and the RouterView to place the contents onto the About Us page exactly where we want it to be. Let's head into the project and we can begin to set things up. These history, locations, and delivery sections which we've just seen will all be components which we can switch between. Let's set these up in the components folder. We already have this setup, so create a new file, and this one is going to be Delivery.vue. The next one, this is going to be History.vue. Then the next one is locations, all with the.vue extension, and we can also remove the components which you don't need, which is TheWelcome.vue, move this one, and also WelcomeItem. Good. Now let's go into our three new components, and we can add some basic structure. We need the template and each one of these three components is going to be pretty simple. We'll just create a wrapper. We'll add a title at the top, an image, and also a little bit of random text. The div, look at the C class of info_block. This will be added to all three of these new components, h3. I'm going to make use of the tilde icons and this is Delivery Info. After this an image source. Let's go into the assets folder where our images folder will lie and then at the image which you want is the box.jpg. Again, you can use your own images if you prefer. Just replace any one of these which you want to. The alt text, pizza in box. Then also a class of info_img. Just after the image, we'll add some text in the p elements, and you can replace this with any text which you want to. But for simplicity, I'm just going to add some random lorem ipsum text. If you're using Visual Studio, you can simply type the word lorem and then hit "Enter". This will give us some dummy text which we can place in our project. To keep things consistent, let's copy all of these contents and then go into the history. Paste this in and all we need to do here is to change the title from delivery to be history. The image, this one is the chef image.jpg. The alt text of chef making pizza. Give this a save. The next one was locations. Again paste in the contents and change the title. The image for this one is tables. Also change the alt text. This is the outdoor tables. Good. These are all three components which we'll use to switch between in the about view and this is what we are going to be looking at next. 10. The About View & Child Components: Now let's head into the About view page where we can now use a router to switch between our three new components. We also need some structure and content in this page along with the router links and router view. Let's remove the Level 3 heading and place in a div with the class of about. This will be the wrapper for this components and then we'll place in various sections. The top section, if we go to the about, this is going to be placed in two areas. If we go over to the about view, this is going to be placed in two sections. The top section will be an introduction about this page where we'll have some texts about the pizza, the history, we'll place in an image, and then below this we'll add a second section which will contain our router links to switch between our freedom components. The first section with the class of intro, and nested inside we'll have two things. First of all is a div, and second of all is the image. The div is going to contain the text and then we'll add the image just below, and then we'll add a new section down at the bottom, and this one will contain our links. Let's start with this first div by giving this a class of info text wrapper underscore is between each word. Level 3 heading with the title of quality pizza for 30 years. Let's check this out. There's our title, and then below our Level 3 heading, place in a p elements with the text of nothing but the best quality ingredients with a friendly atmosphere. There we go, placed in a class so we can add some CSS later of info text. Next, the image. First of all, a class again for the styling of info_image or img. The source jump into the assets folder where our images folder is contained, and this one is going to the calzone.jpg. The old text calzone close-up image. There we go. This is the top section now complete. Of course we'll make it look a little bit better soon with some styling, but for now, let's jump down to our second section, which is going to be for our links. This needs a class of more info wrapper, a Level 3 title, and this is going to say click on the links below for more info. Then we can start to construct our links places in the HTML nav elements, unordered list. Each one of these links is going to be placed inside of the router link. Each one will have the class of link, but our styling. We use the colon because we're going to make the two attribute dynamic by placing in an object, and this object is going to point to the name, which we're going to assume give to each one of these components. This first one will be the history link and the display text of our history. Let's duplicate this two more times. The second one is going to be for the delivery link. A text of delivery. Number 3 is going to be the locations link. Any text of locations. Good. This is everything we need, now we've got our free links sitting below all of our information at the top. Let's check this out. Into the About page. The contents disappear, this may be just because we don't have a match for our names, because we've not created it yet. Let's just jump into the console and we can check this. We've got no match for our name so let's go over to the router and do this now. The router index.js. We first need to import our three components which is history, locations, and delivery. The top, import delivery this time in the components folder the component is delivery.vue. Duplicate this two more times. The next one is for history. The last one is for locations. As with all of our other routes, we need to add these three components into a path. But remember, the path for these ones is going to be nested inside of the about link so what we want. In fact, we can just remove the second about. We've got the component name, that's about so we can just remove the one which was placed in by default. We have this About link and what we want is the path to be about, and then forward slash, we want the locations, delivery, and also the history. How do we go about this? Well, currently the forward slash about link is only pointing to one single components. We can also nest inside multiple components by adding the children property. For the children property since we're linking to multiple components, this needs to be placed inside of an array. Each array contains an object which is structured exactly like each one of these routes so we'll insert the path, the name, and also the components. For the first one to link to history we place in the path of history, the component name. This component name needs to match the exact one which we placed inside of our router links. I've got history link, delivery link , and locations link. This one is for the history link. Then the history components. Separate these with a comma. Close this down. Separate these with a comma and place these onto their own separate line. Let's duplicate this, copy and paste this in two more times. The second one is for delivery, link to delivery link on the delivery component. Next, locations. Let's give this a try. We need to jump into the About Us page and then we can check out exactly how this will look. Currently we're on forward slash about, let's try our links at the bottom, we've got history. We're now about forward slash history, delivery, this is added to the end and also the same for locations. One thing you may be thinking is we currently have forward slash about which we've placed inside of this path here, then we also have forward slash locations. First when we've setup to the child routes, we've not added a forward slash before each one of these paths. Well, if we did this, it will be treated as a root path. Let's take a look at exactly what we mean by this. If we add the forward slash to each one of these and then go into the About. Now, instead of having forward slash about, if we click on one of our links, the full URL will be replaced with this history delivery or locations link. But since we want to add these onto the end of the About link, we can just use these without the forward slash. Finally, to actually see the contents below from each one of these components, we need to go ahead and add a router view. Go to the About view and then just below the navigation and placing the router view. This and let's check this out. We've got the top section and then the locations area at the bottom, the history, and also the delivery too. 11. Named Views: This About Us page has three components which we can switch between. As we know, we can click on the history, the locations, and also the delivery components. As we've also previously looked at, we can make use of components and reuse them in any file which you want to. For example, in the app.vue, we can go the traditional way by importing a components and then rendering this inside of the template. An alternative way of rendering a component is to make use of the Vue Router. So far we've used the RouterViews to display the page such as this one just here. Also the same in the above view which we just added in the previous video we made use of the RouterView. This could display either the top level page or any child components, just like we do here with the about page. We can also insert as many more RouterViews as we want to and each additional RouterViews will act as outlets with various components. If you were to insert more RouterViews, we also need to give them names so the router knows which components to display in each one. Let's say we wanted to show some additional information at the bottom of the homepage, such as the delivery and history components. Here we have a developer. The key here is we have two additional RouterViews. Each of these has a name attributes which will be important in just a moment. Notice the RouterView at the top has no name. This is a default location. This default, along with the two names are set inside of the routers index page. The left shows what we currently have set up for the homepage using a single component. However, when we have multiple RouterViews with names, we need to modify this to use a components object, just like we see over on the right. First is the default location, which is the RouterView with no name. Then the next two names will match the two names which we gave to the RouterViews. Go to the project and we can set this up. Jump into the main top-level component, which is the app.vue and then just below the RouterView we'll place in a div. This div, the styling and layout will have the class of info block wrapper. Then inside of here we can place an additional RouterViews to display any one of our components. For this example, I'm going to display the delivery and also the history components inside of this main page. Placing a RouterView, the name attributes, which is going to be equal to delivery. Copy and paste this. The second one is going to be for history. We only want these two components to display on the homepage. For this, let's go over to the routers index page and go up to the home section. Currently the home router only shows the single component of home view. But to display multiple components, we can change component to have an s on the end. Remove this, pass in an object. Just as we've seen with the slides, we need to add the default view, which is the one which is displayed inside the router view with no name. This one is the home view, the name of delivery. This will show the delivery components and the next one is for history. Just to recap, we've got the default component, which is the home view inside of the app. This will display in the router view, which has no name. Next, we have the delivery and the history components, which will match up with the name of our two additional router views. Now, view router knows which components to display in each location. We already have these two components of delivery in history imported at the top so by now it should be good to go. Save this file, over to the homepage. From the very top we've got this Level 3 heading of home, which we can see inside of our view. What we have here is this title and then below we've got our delivery components and the history. Just to confirm these only display on the homepage, we can go into the additional links and we shouldn't see these components displayed. To finish things off, let's go back over to our app.vue and add a little styling to make these appear side-by-side on the larger view and then stacked vertically on the smallest screen. App.vue. We have this class of info block wrapper. Let's copy this down to the style section. The display type of flex. We'll begin with the small screen view, which means we need to change the flex direction to be column. This will keep our content stacked vertically on the smaller screen. To make this change to be alongside each other, we need to change the flex direction to be row on the larger screen. We can do this by adding a media query. In fact, we can also remove all of the rest of the styling. I don't need any of this. Set up all media query with our media. We want this to target screens with the minimum width of 900 pixels. Let's grab our info block wrapper. We will modify the flex direction to be equal to row. On the larger screens of 900, these in a row and then this drops down on the smallest screen. This is an alternative way of using components rather than the traditional way of importing a component into another file to display. We can also allow the Vue Router to place these into a router view. 12. Project UI- Section Intro: This new section is all about getting content into our application. We'll create all of the structure we need by creating various pages and components. Later on, in the upcoming sections, we'll make these more dynamic by inserting various pieces of information from a database, such as our orders, our users, and our pizzas. But next, we'll start by creating the menu components. 13. The Menu UI: The menu view page, which we're now going to create will be split into two sections. On the left, we're going to include the menu, which will include all of the pizzas in a list. Then in the upcoming section, we'll create the basket section over on the right, which will list all the contents which the user adds to the basket. Let's jump into our menu view components and begin to work on this Into the views in menu view. We're going to move the Level 3 heading for now and then create a div, which is going to be a wrapper for both of our two sections. This the class of menu underscore wrapper. Then two more divs, which is going to be nested inside. The first one is going to be for the contents on the left-hand side, which will be our menu. Will give this the class of menu, and then create a second div, which is going to be for our basket. Give us the class of basket. Then jump up to the menu section and place in a Level 3 heading. Make use of the utilas any texts of authentic handmade pizza. For our second div, which is the basket. Again, a Level 3 heading up the utilas. This one is for the basket. As mentioned before, for now, we're just going to work in this menu section, which is going to be a HTML table. This table will just contain a single pizza for now. But we will come back to this when we start to work with the firebase database so we can loop over all of the pizzas which are stored and then display them into this table. For now, we'll set up our template of how this is going to look inside of the table body. Then at the TR element for our first table row. All of our pizza content will go inside of this one single table row. The cells enter some table data. This is going to be for the pizza name. The pizza name will place inside of the strong tags and place in any pizza name, such as Margarita. Below this first table row, we'll add in a second row. This one will contain the description inside the table data. The small tags and the description of a delicious tomato paste pizza topped with mozzarella. Scroll down and just blow this table row will add a third row. This one is going to be for the available options. For each pizza, we're going to create two different sizes, which will be a nine-inch and a 12-inch. Later on during this class, we will loop over this and do this for both the 12 and the nine-inch pizza but for now, since we're just creating our structure and adding some sample data, I will just place in one single-size, which will be for the nine-inch. This will be followed by the price. Then the third section will be a button which will be used to add the pizza to the basket. Place in a HTML button inside of here. The type of button. Then to get out plus symbol will make use of a HTML entity, which is the code of the ampersands, the hash 43, and a semicolon. The browser let's see how this looks. We got our first row, which is the title, the second row, which is a description and the third section is going to be our option. As mentioned before, we will loop over this third section, which will give us our various options. This is our table for our pizza now setup and then upcoming video we'll move on to the next section, which is the basket. 14. The Basket UI: Following on from the previous video, where we added the menu section on the left, we're going to now move down to the baskets area. This basket will also be a table showing the pizzas which the user selects by clicking on the Add button just here. Followed by an order, total section and a button to then go ahead and place the order. In the future, this order button will be linked up to our JavaScript to then push this order to our database. For the table, sections will contain a single row. This row will have the quantity and a button either side to either increase or decrease the quantity. You will have the name of the selected pizza and also the price too. Below our Level 3 heading for the baskets, create a div. This div will contain the structure for all baskets, which will be a table at the very top, then the text, which is going to be the order total. But now just place in any value which you want to. This will be updated and calculated with JavaScript. Next, a button which will be responsible later on for placing the order. Some text is fine for now. Let's check how this is looking. There's one of our content. Now we can move into the table section. The table will contain a single table row. In the first table data cell is going to be two buttons and also the quantity. We'll have the quantity in the middle, and then a decrease button on the left and an increase button to the right. The first button with the class of quantity_button, the type, which is also button, and then the HTML entity, which is the ampersands, the hash 8722 followed by the semicolon. This will give us the negative symbol and then we'll add the quantity and then to the right a button with the plus symbol. For the quantity which will sit in the middle, place in any quantity for now. Copy our button and paste this in at the bottom and simply change the code number to be 43. Dates on these buttons will be used to increase and decrease this value. Before we add this to the basket, we will also update the order total as we change things too. That's our first piece of table data. The second one is going to be for the pizza name and also the option size and the third one. This is the price of the pizza. Save and over to the browser. This is all the information we now need for the basket and with all of this content now in place, we'll next look at adding a little styling to make it look a little bit better and also improve the layout. 15. Fonts & Base Styles: Inside of our project, we have this main.js file. This is the file which is responsible for creating our app. We import this from the vue library. We also import the main app components, which is this one just here. This is the main entry component and everything else is going to be nested inside. We pass this file to create app, and then we mount the application to the DOM with the id of app. As well as this, it also includes an import at the top with a main.css file, which is inside of this assets folder. The way we structure our styling is completely up to us when it comes to a vue app. But, I'm going to be using this as a kind of general style file containing base styles such as fonts and resets. We can still add specific components or page styling in individual components of vue files. First though, we need to choose our fonts, which we're going to use for the project. For this, I'm going to make use of Google fonts. So head over to fonts.google.com. I'm going to make use of two separate fonts. One of them is going to be for our headings and title, and the other one for the general text. So click on Search. The first one is called Marck script and this is M-A-R-C-K, and this is the one for our headings and title. So select this. Scroll down. This one only has the regular texts which is fine. Click on the plus icon to add this to our selected families, as well as this. Let's search for our second one. Back to the search. For the general text, I'm going to be using Roboto. This is the one we need here. Let's go for a selection of fonts. So 300, which is a lightweight text, the 400, which is regular weight, and also a 500. Add all three of these to the family. Click on these selected families and we have the information which we need to import into our project. So we have a link which we can use or we can use the import syntax. To import this, let's go over to the Assets folder and into the main.css. This is currently empty. We can add this import rule. So copy everything inside of the style tags. Paste this in. As we've seen before in the main dot js file, we have the id of app, which we can select as the main wrapper for our project. So select this with the hash. Inside of here, we can set the font family to be Roboto by copying this second line just here. This would be our general text and we'll override this with the Marck script when we need to. Enter the body. We're just adding general styles inside of here, remember? So we'll add some margin to center our project, by setting this to be zero on the top and bottom, and also on the left and right. The color row text of RGB, placing a red, green, and blue value of 76. Just after the body, we'll target the headings h1, h2, h3, h4, h5, and h6. Remember, the font-family which we are going to be using for this one is the other one which we get from Google fonts. So copy this section, paste in. Remove any default margin which is applied to any of our headings with the browser. I'm placing the color, which is an RGB value of 76,76,76. So this gives us our general heading stylings, but we'll make use of the level three heading in multiple places. So we'll be using it with page and component headings. So we'll set the font size to be a larger size of 2.2 rems. Also, place some margin on the top and bottom of one rem and zero on the left and right. Next, the unordered list, the ul elements. Reset any space in which is applied by the browser, so default margin can be removed to zero, and also the padding to be reset back to zero too. The list items, and in fact, also the links too with the a elements will inherit the color. This will remove that purple link color, which we have by default inside the browser. Also, reset the text-decoration to be none, and also the list style. This will ensure none of our links or any of our list items have any underlines, and also any of the bullets to the side of the link too. So some more general styles, we'll jump into our image elements, and make sure that the maximum width of any image does not exceed 100%. So next onto our classes for our router links, if you remember early on, we have these links up at the very top and you can already start to see our styling taking place. We have these menu links up at the top, which you can see if we go into the components and then into the app header. Each one of these router links has this class of link. Let's grab this, a dot since it's a class, a margin of one rem to give this some spacing, there we go, and we'll also make sure that each one of these changes color when we hover over them. So to do this, we'll target our link once again. We'll use the hover selector, change the text color to be an RGB value of 161. So the red, green of 132, and 132 also for the blue. Finally, for any buttons which have on our site, we'll also set the default styling for each one of these. So the button, will remove the background, the default color, by setting this to be none. A very fine border of 0.5 pixels, a solid line, and the color of this line is going to be an RGB value of 202 for each one of these. Some border-radius of 0.3 rems, some padding to make the button a little bit bigger. So half a rem on the top and bottom and one rem on the left and right. I need to prompt the user that they can click on these buttons will change the cursor to be a pointer. So, if we go over to our project, we can see some of these have taken effect. But remember, these are just general styles for our project, and we'll add more specific styles to each one of our individual pages and components. 16. Menu & Basket Styling: Next we're going to head into the Menu View File and add any specific styling to this view components. Open this up. Then just below the templates, we'll add the style and add this scoped attributes. If you've not used the scoped attribute before, this means that all of the styles will only apply to this components, i.e., the templates which we created above. To see these in action, we'll jump in to the menu section. Make this a little bit smaller. At the top we've got a level 3 heading, which is this one just here. Let's grab this and set the text-align to be in the center. This also applies to our basket. Both this top section and this bottom section will soon be applied to the left and right on the larger view. But first we'll start with the mobile layout, which is the small screen. Let's just below our h3 and say the mobile layout. To switch between our two layouts, we're going to make use of the flexbox. If we go to the very top, you can see that this div has the class of menu wrapper. This will be the one which is responsible for controlling the direction of our menu and also our basket. These two divs are the child elements of menu wrapper. If we go down and we select this, we can make use of the flexbox. Menu wrapper. The display will be the flex. You can see the default flex direction when using the flexboxes in a row. These will be placed alongside each other on the page. This is the layout which you want on the wider view. But for the smaller view, we need to override this to be the flex direction of column. Also, the color. The font color will be an RGB value of 80, 96, and 112. Next we have the two sections which were inside which we just looked at, which is the menu section up at the top and then the basket section. Each one of these has their own corresponding class. We have.menu and also.basket. We set the background for each one of these sections to be a hex color. The one I'm going to go for is faf1e2. The border radius for each one of these sections, small value of three pixels. The height, just in case we don't have a lot of content, we'll make sure that each one of these sections is a minimum of 100 percent of the viewport height. Then some padding on the inside of these elements to give you some spacing from the edge of the screen. This is the direction which we have for the mobile view. If we stretch this to be a little bit wider, we want to flip this around to make the flex direction to be a row. We can do this inside of a media query, where I want the screen to be a minimum width of 900 pixels. Please take effect. Now, what we'll do is we'll grab the menu wrapper section, copy this, paste this inside. We've already set the display type to be flex. We're going to remove this. I've already set the color. We're going to remove this. All we need to do here is to set the flex direction to be in a row. Add some space between with justify content. Then we need to decide how much space each one of these sections is going to take up. The pizza section will probably need a little bit more space. We can make use of the flex direction to give us more space than the basket. For the larger view, let's grab the menu section, set the flex value to be two. Then for our basket, we'll set the flex value to be one. This means that this will try to take up twice the available space as the basket section. You can see this is reflected inside the browser. We can tell the difference between these two sections. We'll add a border to the right of this menu to add a vertical line down the center. Inside the menu, set the border on the right of one pixel, a solid line and the RGB color of 202 for each value. There we go. We're almost done now for this section, but what I'm going to do for these two buttons here is to remove the border, make them a little bit smaller. If we go up to the section, we see each one of these buttons has the class of quantity_button. We can do this in the general section, just above the media query. Quantity_ btn. Remove the border. Let's set the padding to be a value of 0.4 rems. This now reduces the padding now for both of these quantity buttons. Next, we'll continue with the theme of styling. We'll add some styling to our header section. 17. Header Styling: Surely we're going to move on to adding some new components and also some new items to our site. But quickly, just before we do this we'll apply some styling in it to this header section. Let's open up the sidebar, jump into our Components, and then the AppHeader. The AppHeader needs the style section at the bottom, and the style section is going to be scoped to these particular components. Let's take a look at what we've got. We've got the site title class at the very top. Let's start with this. Site_title. Begin with the font size for our site title and we'll go for 3.6 rems. Make it pretty big. The color of Alice blue. We can't see it too much at the moment but very shortly we're going to add a background color to this header section to make it stand out even more. We'll add a transform to make this rotate at a certain value by adding the rotate function. Pass in the amount of degrees which you want to rotate this by, and this can be a positive or a negative number. Eight degrees. You can see this now has a tilt and you can increase or decrease this value. To give it some space from the content below, add some margin onto the bottom of the elements. Let's go for two rems. Then we can grab our header which is the wrapper for this full section. The header is going to make use of the CSS Flexbox to lay things out. For both the smaller and the larger screen views, these contents will be placed vertically in the flex-direction of column. Set the display type. Remember the default flex direction is row which is left to right across the page. We can switch this around to use the column. To place the content into the center, use the flex property of align-items. We can see that the content is pretty much against the top and also the bottom of the header. We'll add some space in with a padding value of one rem on the top and bottom and zero on the left and right. For the menu links, we'll match the color of Alice blue and then place in the background image. For the background image will point to a URL which is stored inside of our project. Let's go into the assets folder, into the Images, and then inside of here we have an image called main-bg. This is dot dot slash, jump into the assets folder, images, main background. We also want to set this to be no-repeat. Since we've added a background, we also want to add to the height property and set this to be a fixed value of 650 pixels. The background size, we'll set this to cover. Then if we take a look at this image inside of the images folder, the main-bg, we can see this image is not centered, it's cut off on the right-hand side. Let's fix this and go back into our CSS. Make use of the property called Background Position. Set this be in the center. This image will now always be centered on different screen sizes. Next little tech shadow to each one of these characters of one pixel, one pixel, three pixels, and the color to apply for this tech shadow is going to be an RGB value of 20, 20, 20. The next thing to do is this level to heading which is the text of the Number 1 place for pizza. We can grab this with the h2 selector and add some spacing. A margin property of two rems on the top and bottom and zero on the left and right. A text color of antique white and the font size of two rems. Good. This is our styling now complete for the header. Our site looks a little bit nicer. Next, we're going to move on to the Admin page and create a new pizza form. 18. Admin: Add New Pizza Form: Inside of our admin view, very shortly this is going to be split up into multiple areas. It makes sense to organize all of these areas into different components, and also place these components into an admin folder. Let's jump into the sidebar. Into the components, and inside of here. Create a new folder with this new folder icon called admin, and we'll group together all of the admin related components. Inside of here, create a new file. This one's going to be the first component for our admin, which will be a form to create a new pizza. We'll call this the NewPizza.vue. Create the template which we need for this section and the section wrapper. This will have the class of admin_section. This will be split up to have a header section with a title and also the form to add our new pizza. The header for this section, the class of admin_section_header, h3. Text of add new pizza. Before we go any further with any more content, let's check how this looks inside the browser, but first we need to import this into our admin. Jump down to the views and the admin view. At the very top, place in our script to import our file. What we're going to do here is add an attribute called setup. We'll come back to this setup very soon in this course, but for now we're going to import the component which we created, which is going to be called new pizza. I'm going to import this from the directory which is @ to take us to the route, the components folder, admin, and the filename of new pizza. We got level three heading for now we can import our components, and place this below. Instantly, as soon as we save, we'll see this is added to the admin section, so go into the admin link and this is the title which we added in the new pizza components. Continuing on and below the title, create our form. We'll remove the action since fueled by handling all of the necessary JavaScript's. Each one of our sections, such as the pizza name, the description, and the various options were surrounded inside of a div styling. This div will have the class of form_group. Then this will have a label and an input. The label, the text of name, the input. The first one for the pizza name will have the type of text and the ID of name which will match our label. Let's copy this div section. We'll duplicate this and paste it in below. The second one is going to be for the description. For this one, we don't need an input, we're going to use a text area, so we could have multiple lines of text or a long description. We're going to move all of its content inside of here. Let's clean this up, and we'll add the rows which we need. Let's set the default value to five and also an ID of description, which will match the label just above. Save this, and we can see our inputs are now appearing on the screen. Next, we're going to create two new sections, which is going to be for our first and our second pizza options. This is going to give us two different options for the pizza size. The text inside the P elements will create these as strong elements to make them more bold. Option 1. Then we can continue with our form elements. We'll copy the font group from before, the very top one, write this in, and this one is going to be for the size. This is going to be in inches. The label of size one. The type of text is completely fine, and we'll also match his ID with the label. Below this, we also need to create a new form group, and this one is still related to Option 1, and this is the price of this particular size. The text of price, the label for Price 1, the matching ID. Then we'll carefully duplicate this section so the price, the size, and also the text. Copy this. Paste in it below to create a second section. This one is for Option 2. Let's change the property names of each one of these attributes to be Size 2. The same for the price. Save this, and over to the browser. We now have a two sections to fill in the sizes. The final section is going to be for a button to submit this form. Again, place this inside of a div with the class of form group. This will keep the styling consistent. Add a button with the text of add. You will now add new pizza form now complete. We'll come back to this later on and improve the styling. But next we'll move on to another admin components, which is to display the pizzas. 19. Admin: Pizzas Component: So we've got the add new pizza components inside of the admin. The next admin component which we'll create is a pizzas component, which will display all of the available pizzas on the menu. I'll jump into the sidebar and create a new Vue.js file again inside the admin to keep this organized. This will be simply Pizzas.vue. As ever, close the sidebar down and create a template to store our HTML code. A section, which is going to have the class of admin_section. Also take a look at the new pizza component which we created previously. This also matches this same section. So we can apply the same CSS styling. This will also have a header. Again, match in this header just here with the same class name. So the class was admin_section_header. This will give us some consistent style for all of our admin blocks, Level 3 heading with the text of menu. So this is all we need for the header section. Let's jump below the header and create our table, which will list all of the available pizzas from the menu. The thead section, or the table heading, place in a row, and the th element to create our two headings. So we're going to keep this pretty simple. All we do is add a pizza name on the left, and then on the right of the table, we'll add a button to remove this pizza from the menu. So the heading of pizza and the second one is going to be the text of remove from menu. I will set up a function later to do this. Below the thead, create the tbody section. The first row, which will sit below our headings. So the first thing inside of here is the pizza name inside the table data elements, and we'll just add some sample text inside of here for our pizza name and we'll link this up to our database later on. Next, the next cell inside of the table data is a button which is going to remove the pizza from the menu. The button has the type of button and also a class for our styling of btn_remove. Then inside a HTML entity, which is going to be the cross. This is the ampersand, the word of times, followed by a semicolon. This is all of the content which we need for now and later on we'll create a for loop to loop through all of the available pizzas and duplicate each one of these rows. Also as with the new pizza components, we need to import this into the admin view, into here. Duplicate this and this one is going to be for all pizzas. This is in the same admin folder. So we change the name, and I'll put this at the bottom of the file into the browser. There's our table with our single row and our single pizza. Next, we'll create the final admin components to list all of the customer orders. 20. Admin: Listing Orders Component: The final admin components which we need will be used to list the current orders. Again, I just have these components inside of the admin folder to keep this organized. Create a new file and this one is going to be the orders.vue. This will also follow a similar pattern to the previous ones where we create a template, we create a section with the class of admin section, we create a header, and then a table to list all of the orders. Let's get started with the template and the section at the very top. This is our wrapper. The section also needs to have a class of admin section header. Again, this matches the previous admin components which we created, as does the header at the top, the class, and if I could just use the wrong one at the top. Let's copy this. This one should be admin section header and the section should be the class of admin section. Good. We're now back on track and we can create inside of the header, our Level 3 heading with the text of current orders. Also just like the pizza's components which we just created, later on when we link this to Firebase, all of our information will be pulled from our database. Meaning it will be dynamic. For now, we'll just place in some dummy data and we'll add five current orders and this will go up and down depending on how many orders we actually have in our database. Next, the table section, the table row, the th tags for the heading. The orders are going to list out the item, which will be the pizza name. Remember that each pizza has two different sizes, so we also need to list this. We need to know the quantity and also the price. We'll add inside of the brackets the word of total because this will be the total value, which will be the number of pizzas multiplied by the price. Then below our first table row, add in the second row, and for now, we'll place in a sample order. The first cell inside of the table data element is going to be for the order number. This cell will also contain two pieces of information. First is the actual order number, and this will go up depending on how many orders we have, and we'll also add a button to remove this order from the database, which will be useful once the order is completed. Inside of the strong tags, we'll add the order number. Let's place in number 1 for now. The button, the HTML entity, which will be the ampersand and times, followed by a semicolon, and this will be a little cross to remove this from our database, the class for our styling of btn_remove, and also the type of button. This order number and this Remove button will be on its own independent row and then the third row, which we'll add just below, is going to be all the values below our headings. Therefore, the first piece of table data we need is the item. This is going to be the pizza name, followed by the size, the quantity. Any values are fine for now inside of here. The last one we need is the price. To see this, jump into our admin view. Now we can import this at the top of our file. This one is the orders. I'll place this at the bottom of the template. Scroll down. Good. We've got a lot of the user interface, so the admin section now in place, and everything now setup for adding some dynamic data from our database. But next what we're going to do is to jump into another one of our views, which is the home view, and begin to add our content. 21. Home View: Next we'll get to work on this home view, which is going to be new components, and it's going to be switched with the view router. Remember, we have this header section at the top. Then at the bottom we've already got two separate outlets placed in via the RouterView. We've got the delivery and the history sections, and is always going to be contained on the bottom of our template. Remember just above this we'll have our RouterView, which is going to display any components inside of our views folder. We have our home view already setup. We've got a simple template with the text of home. We can see this just here. Remember our two components at the bottom which are placed in with the RouterView, are only going to display on this homepage, because if we go into the router and the index page, we've set these two up as the delivery and the history components and the default, which will be the home view. This will be this section up at the top. Let's jump into the home view right now. Then we can get to work by adding some content for our styling. We can set up the class of hot, and then just below this, create a new div section, and this is going to have the class of hot_text. Move up, a Level 3 heading. This will have the text of hot out of the oven. There we go. A section for this homepage is also going to have some text and also feature a vegetarian hot_pizza, and also a image too which we have inside of the images folder, which is inside of the assets. We need this fire.jpg. First the text underneath the Level 3 heading, add a p element for our text. This one will have the class of hot_pizza_name. Pizza name is vegetarian hot. Small text. This one will have the class of hot_pizza_description. Then the text of our signature vegetarian, but with a kick. Then below this text we can place in a router link, which is going to link to our menu. The RouterLink component, the text of let's order. Then as ever, with our RouterLink, we need to pass in the two attributes with the colon, since this is going to contain a dynamic data, pass in our dynamic objects where we'll link to the name of menuLink. This menuLink is the same one which we setup in the router file, which we can see in the router, and then into the index.js. These are the unique names which we added to each one of these paths. Almost there. Now we have this div section and then just below this we'll place in our image. I will point to the image which we just looked at, which was in the assets folder. We'll use../ to jump up one level in our directory into the assets, images, and the fire.jpg. Class styling can be hot_img, and then also the alt text too. There's our content all in place. Let's just check this link works. This jumps to our menu section, which is good. We can jump down below our template and add the style section. This wants to be sculpted to this component. We'll start with the main section with the class of hot, and this is the wrapper for this full section. This will be responsible for changing the layout on the small and the large screen view. To do this, we'll make use of the display type of flex. Since we'll start with the mobile first view, we can shrink down the browser, and then changes to be a column direction on the smaller screen. Flex-direction is equal to column. This will place our content vertically. Then inside of a media query on the larger screen, we'll switch this around to make use of row. We'll do this now with @media, where we'll target the minimum screen width of 900 pixels. Again, we'll grab the class of hot and change the flex-direction to be equal to row. The small screen, make this larger, and now two elements now appear side-by-side. The first element was this div, and the second one is our image. Let's place this into the center with align items. In fact, this just wants to go into the mobile first section, so supplies for the small screen. Outside the media query, we'll go for the hot_image, and also the hot_text. This is a class they'll place it in the dots. We've got the class of hot_text and also our image, which is our two main sections. This is going to target our two content areas, and we'll make them the same size by setting the flex to be a value of two rems. Then the text-align of center. Next we have the heading, and this doesn't have a class, so we can target a Level 3 heading, which is a child of the hot_text. The wrapper of hot_text will select the h3, which is nested inside, and set the color to be a hex value of bb2118. Which gives us this nice red color, which we can see here. Then the hot_pizza_name, which is the vegetarian hot_pizza. Make this a little bit bigger by setting the font size. Let's try 2.2 rems. A little bit smaller too. 1.8. Next for the following piece of text, this has the class of hot_pizza_description. Copy this. Place this in. The font style to be italic, the font-weight to be a little bit lighter, let's go for 300. Let's check this on the larger screen also. This is fine. For now, we can go ahead and make some adjustments to suit your taste if you want to. But next I'm going to move on to continuing with the styling for this project. In particular, we'll add some styling to our tables, and also our form inputs. 22. Form & Table Styling: To round off this section, now let's jump into the main.CSS file, where we can start to apply some shared styles between our forms and our tables, and also some general CSS styling too. From here, particularly inside of the admin, we have some areas which includes some shared classes. For example, if we go into the components and into the admin folder, we've got this new pizza. This has the section wrapper of admin section, then we've got this consistent header section with the same class in all three of these components. We have it here at the top of the orders and also at the top of the pizzas too. We also have a form here which we'll add some styling to. We have various tables for the menu and the current orders, and we also have a table inside of the menu view. We have various styles which are shared between multiple components. Rather than places into individual components, what we're going to do is go into the main.CSS file and add the shared code inside of there. As well as the main wrappers and also the forms and tables, we also have some little details too such as the remove button which we have in the menu, and also in the current orders. We've give this a class of btn_remove. We'll add all of these now into our style sheet. Go into the source, into the assets, and we'll have this main CSS file. We've already added some content inside of here, so let's go to the very bottom below the button, and we'll continue on from this button and go just below and add the class of btn_remove, which we just looked up before. Since we're going to remove something with this, we'd probably want to add a color of red. The shade I'm going to go for is an rgb value of 180 row, 67, and 67. You can see instantly as soon as you save both of these two buttons down at the bottom and now change color. We can also remove this border by setting this to be none, and make it a little bit more visible by increasing the font size to be 1.2 rems. This menu section and also the current orders is contained inside of a HTML table. For the main table element, let's add some styling. We can place in a border radius, just a subtle border radius of 0.3 rems. We'll see this more clearly when we add some different colors. We can also set the text align to be on the left. This will push all of the text to the left of each one of the individual cells. A width of 100 percent display the full available width. Then we'll move down to the individual cells which are contained inside of the th, for our table heading and also td for our table data. The spacing we'll add some padding or some spacing on the inside of the element of 0.6 rems. Next we'll work on this form for the add new pizza section. Grab the main form wrapper, some padding of zero on the top and bottom and 1 rem on the left and right. Also we'll match the border radius of the table by adding this also to the form. Again, we'll see this more clearly later on. Inside the new pizza, we'll see that each one of these form elements such as the name and the description is wrapped in this div with the class of form group. This is consistent for each one of these sections. Grab this. So this is form_group. Some padding space out each one of these groups and we'll add one rem vertically on the top and bottom and zero on the left and right. Make use of the flex box with the display of flex. We'll align the items into the center. Align items center as you can see will give this vertical alignment so the text stays in the middle of each one of these sections. If we don't have this, you can see the text is aligned to the very top of this element. We'll add a border to the very bottom of 0.5 rems for the thickness, a solid line and the line color will be an rgb value of 202 for each one of the colors. Whoops, that's a little bit too thick there. This just wants to be 0.5 pixels. Then we'll remove the underline from the very bottom section. Again, grab our form_group, select the last of type which is the very last occurrence, which is our button, and then we'll remove the border bottom by setting this to none. Next we'll give these labels and also our inputs a consistent size using the flex box. The label will give these a consistent flex value of 1 so they all stay the same. Then because we want the input to be a little bit bigger, we can target all of the inputs and also the text area and give these a flex value of 3.This means all of our inputs on the text area will try to take up three times the available space as our labels. Let's now work with the borders for each one of our inputs. Just below the flex value of 3, set up the border. This will take on the same value as our border bottom. We can copy this, paste this in. This just gives us a lighter, more subtle border for each one of our inputs. Some border radius of 0.3 rems, some padding to make these a little bit bigger of 0.6 rems. By default, with HTML form inputs and text areas, it doesn't inherit the font and the color by default so we need to do is to set these manually by setting the font family to inherit from the parent and also the same for the color. As we've seen before, each one of these admin components such as the new pizza, has this class of admin section, and then we have this admin section header. Let go into our styling and we can target these. First, the main wrapper which is the admin section. Margin of 1 rem and this will space out all of our components. The background color, which is a hex value of faf1e2, and also finally for this section a border radius of 0.3 rems. Next, the header section for each one of these components and remember these have the class of admin_section_header. Set the display type to be flex. Give this a save. This one looks fine and as does this one, we'll have a little issue but Add new new pizza. This shouldn't be side-by-side. We have new pizza, we have the wrapper, we have the header, the form. And in fact we just need to make sure that this header is closed off so cut out the closing a tag for the header and paste this just after our h3. That now looks better and this now keeps us more consistent with the rest of these sections. Back to the styling. And you may be wondering why exactly we have set the flex box when we only have one single element inside of here. Well, this is because later on we're also going to add a little icon over on the right to fold each one of these sections out of view if we don't need it. Therefore, since we'll have multiple sections, we can add justify content to add space between each one of these. Also, to keep these vertically aligned, align items into the center. Then lastly, some padding of 1 rem which will play on all sides of this title. We're getting somewhere now with the styling. This should also apply to our menu section. Then the final section we'll add some style into in this video inside of the home section is each one of these Info blocks down to the bottom. These have the class of info_block, which you can see if we go into the main components, which is the App.vue. This is rendering our delivery and our history components so jump into delivery. This has the class of info block as does the history component. Back to our styling info_block. The display type of flex, the flex direction of column, align items into the center, add some padding to each one of one rem, and also a different background color for each one of our blocks and the hex value of faf1e2. Let's save this and see how this looks. Over to the browser, we can see on the smaller view these are stacked vertically on top of each other. We'll stretch these wider. The two components are side-by-side and we have this flex direction of column for each one. We have the content aligned into the center, a little bit of padding and the background color. There is still some other styling to apply to individual components but we'll add this as we go through the course. But now we have lots of content in place. In the next section, we'll add some JavaScript and take a look at using the VueJS composition API. 23. Options Or Composition?: The Options API is something which you may be familiar with if you've built with Vue.js in the past, in Vue Version 1 or Vue Version 2. The good news is in Vue Version 3, the Options API is still here to stay. But alternatively, we have a different approach we can also use, which is called the Composition API. The Options API is something you may be familiar with if you've built with Vue Version 1 or 2 in the past. It's a traditional way of building a Vue.js applications. The good news is in Vue 3, it is not gone anywhere. The Options API look like this. Inside the script and export default, we had various sections such as a data section. This contained any component data or state, which you can think of as variables. There's also methods. This is where we can add our JavaScript functions and we can call them as we need them. Of the sections not shown here include computed life-cycle hooks, watches, and props. Again, all still valid to use in Vue 3. However, for this project, I'm going to be using the Vue 3 Composition API. The Composition API allows us to write code more like regular JavaScript. This is just like a regular JavaScript variable, a timeout, and also a function. This can now be wrote inside of Vue.js apps, replace this just like the Options API inside of an export default and then into a setup function, which is highlighted here. Things in here such as variables and functions can be made available to the template by returning them down on the bottom. It is also worth noting that we can use the setup function alongside the existing Options API. Here we'll have the data section passed in, but any other section from the Options API can also be added. That is a lot more to it too, such as we can organize the code into separate files, and reuse the path where needed, and many other things which we'll use as we need them. Alongside regular JavaScript, we can also make use of Vue reactivity. Here we import a ref from the Vue package and wrap all variables value with it. A ref is used to turn the contents into a reactive object. Meaning, any parts of our application which relies on it will change if the data inside changes. Basically mean no components are kept up to date with any data changes. We'll also come back to this one very soon. Along with ref, we can also import from the Vue package things like computer properties and also watches, which are available with the Options API. Finally, we also have a shorter way of writing composition code, which is the script setup. On the left is the similar example to previously, where we have a setup section and return any Vue we want to use in the template or HTML. Then on the right is the exact same thing using scripts setup. It adds a setup attribute to our script tags to make the code shorter. Also, notice here we don't need the returns section because all of the data we want to use in the template is automatically made available. The script setup will be the approach which we use in this class project since it's cleaner, shorter, and also has better performance internally. 24. The NewPizza Object & Data Binding: Let's now head into the NewPizza.viewfile. We can begin to add some JavaScripts making use of the scripts setup which we just looked at. We can add a NewPizza objects, which will be the structure for all of our pizzas in our application. Just above the template add in the script tags place in the setup attributes. Then as we've just seen, we need to import our ref, and we import this from the view package, which is in the node modules. Then we can create our NewPizza objects, which is wrapped in this ref. This ref is going to create a reactive object which we can use throughout our application and the state is kept updated. Let's start this inside of a constant or a variable called NewPizza, set up our ref as the wrapper. Then inside we can pass in any type of data we want. This could be a string, it could be an array. But for our use case, we need to create an object that have all of the Pizza properties. These properties consist of the Pizza name, we can say Margherita. But just for this, I'm going to put in Eg. The reason for this is this is going to be the initial state of our form. I'm going to go into our admin, let's just take a look and we see this name. This is going to be linked to our object. The name property here using V-model will be linked to our name. We'll also link the description to the next property. When we first log this form, the text inside of here will reflect what we see just here. This just gives the user a prompt exactly what they need to write to create our NewPizza. Next, a description. Again, we'll give an example of a delicious tomato base pizza topped with mozzarella. Separate by comma, we can then add our options, which is going to be an array. Placing these in an array will allow us to create different options, such as our size. Each one is going to be an object. We're going to have two objects, just like this side-by-side. Each one of these is going to have the size property, let's say nine inch for the very first one, and also a price. The same for our second object, but this one is going to be for a 12 inch pizza and also a price. Remember that all of these values are just starting values. All of these object values can be replaced with the values from this new pizza form. They will be updated before we add these to the database. To link our form inputs with our object properties, we're going to make use of the model. The model allows us to create something called two-way data binding, which as mentioned, means the values from our object will show inside of the form inputs. In any form inputs we type, we can then update to override these form inputs. This will be reflected inside of our object. To do this, let's start with the name. Go down to our form inputs, where we type the name in. We can make use of v-model passing this in as an attribute. We want to link this to our object, which is called newPizza, and we can access the name property, so newPizza.name. Let's copy this. Go down to the text area, and we can link this to the description. After the description, we then have options, and we need to link these to each one of these array values. Remember, arrays begin at index number position zero. This one will be zero, and this one will be index number 1. Let's go down and we can add these options. We've got the size number 1, paste this in, newPizza.options. Select the first value with the index number of zero on the property called size. Let's copy this, paste this into the one with the ID of price number 1, changes to be price. Then down to our option 2 into the size, this is index number 1. Again, index number 1 for the price and also change the object property to be price 2. Now, if we save this to our form, we can see all of the initial data inside of each one of these and you can see we also have the option set correctly. We've got the nine inch in size and also the 12 inch too. Also see the two-way data binding and check that the object is updated from this form. We can now go into our templates. Anywhere inside of here, we can output the value of newPizza. We can see this at the top. Let's remove anything from these inputs. This is now updated. Same for the description this works. Let's try to update the price. Also our second option is linked to. We can now safely remove this and move on to the next video. We'll take a look at refs in more detail and how we can add items into the basket. 25. Ref’s & Adding To Basket: Previously inside this new pizza component, we briefly looked at something called a ref. A ref is a wrapper for a value. In our case, the value is the pizza. In the upcoming video, we'll also use a ref as a wrapper or basket, which a user can add pizzas to before they purchase. It does this by converting the value into a reactive object and the benefit is each time this reactive object is updated, all of the components which rely on this data will also be updated too. Let's go into the menu view to begin to work with these and take a look at refs in more detail. The menu view outside the template, create our script with the setup attribute. Create our import, just like we previously looked at, where we'll import the ref from the Vue.js package. We also need to create a constant or a variable for our basket. Wrap this inside of a raft to make the contents reactive and set the initial value of an empty array. We also need to create a function which is going to update the basket. Let's call this addToBasket. As we mentioned earlier, when we make use of script setup, this will allow us to directly access any of these variables or functions inside of our template. Let's go down to our template and locate our Add button, making sure we're inside the menu side. We can list now for the click using @click and set this up to trigger our function which was called addToBasket. Check this is working. We'll place in a console log. Any string of text is fine. Jump into the console. Click on the button and our text is now updated. Just before we add some more code to our function, I quickly want to go back to our ref and see how this works in more detail. For this, I'm going to create a simple object called user with the name property and also a likes property too. You may be wondering why I've just typed out a unrelated user object inside of here. Well, the contents of this object are not important. The important thing to note here is JavaScript objects have properties. In our case, we have the property called name and the property called likes. Remember earlier when I said when we make use of a ref, Vue.js behind the scenes turns the content inside into a reactive object. As we've just seen, any type of object has these properties. Since our basket is turned into an object behind the scenes, it also is given a value property and this value property is how we can update or read the content inside. Let's quickly type this out and I'll show you exactly what we mean. For now, we'll just comment out our basket. We'll create a knew baskets, which is not going to make use of a ref. This is just going to be a simple object. Again, just to reinforce this, behind the scenes our basket is turned into an object just like this and then given a value property so when it comes to updating a ref, just like our basket, not only do we update it by the variable name such as basket, we also need to update the value nested inside. As an example, when we call our addToBasket function, we'd first access our variable or constant name, then the value inside. Or we could use something such as the push method to push a new object. The name, any content is fine, the price. This is all just example code, it doesn't matter size of 12. Since this is a basket, we also need to know the quantity too. To check this works, check we are updating the value inside, we can go down to our template and output the value of our basket. Let's save this. Over to the menu. We can see we have the value property which is empty. Click on "Plus" and we don't see this is updated. However, if we update this to make use of a ref , uncomment this out. Click on the button. We can see this object is now reactive and any update to our basket is now pushed to our template. One key point to note here is when we're making use of the basket inside of the template, just like we are here, we don't need to access basket.value. This is automatically done for us. However though, as we can see inside our script, when we access or update any one of our refs, we do need to make use of the value property to update or to read the data nested inside. Of course we don't want to output the basket like this. What we want to do is to go down to our basket section and output the content inside our table without our table row. We can add inside the table body, place this back inside and then we can loop over our basket with a Vue.js V4 loop. Inside the brackets we can access the variable name of item and also the index number on each loop. This is in basket, and then Vue.js, when looping also needs a unique key. Now we don't have any great unique key we can use. But as a temporary measure, we can pass in the index number. Later on when we have the pizza stored inside the database, we'll update this key to make use of the unique ID for each value. Now, each pizza inside the basket is stored inside the item variable. We can update the quantity, so item.quantity, the pizza name, this is item.name, the size, item.size, and also the price down at the bottom. If we wanted to, what we could simply do is add item.price. But since we can order multiple pizzas, we're going to multiply this price by the item quantity to display a total for each line. Save. Make a little bit more space. Now each time we add a new pizza, the basket is updated and displayed inside of our table. Next, we'll improve on this by looping over the pizzas and adding the exact pizza name to the basket, rather than the sample data which we currently have. 26. Looping Over Pizzas: Currently, as we know, we don't have our database set up yet to store our pizzas. For now, as a temporary measure, we'll create a ref, which we can push our pizzas to. This will then allow us to loop over these pizzas and display them inside of a table. For this, go into the MenuView file, and then up at the top we'll create a new constant called allPizzas. allPizzas is going to be surrounded inside of a ref. Well, the initial value will be an empty array. Then over to the new pizza.view components, which is inside the admin. It saves a little bit of time. What we're going to do here is to select the full pizza object. This is the demo object just here inside of the ref. Copy everything including the curly braces. We're copying this because this is going to give us the consistent structure, which we need to add new pizzas to the database. Back into the menu view and inside of the old pizzas array, paste this in twice. Make sure that each one is separated with a comma. Let's just change this around a little bit. We'll remove the Eg. Margherita. The second one Pepperoni, remove the Eg. We'll say topped with mozzarella and pepperoni. Just increase the price to make this a little bit different. Now, this can be looped over in the menu table. They display each one of the pizzas. Now to the template. Have this menu and then the table nested inside the table body. We'll do exactly the same as what we did for the basket where we'll create a v-for loop and place in the data where we need it. Back up to the menu section table body v-for. Inside the brackets, we can select the pizza and also the index number. This is in our old pizzas array. Then place in a key, which is going to be the index number for now. Just like we said with the basket, we'll also come back to this later on and make use of the unique key which the database will provide. Great. Now, we have access to all pizzas and instead of displaying the hard-coded pizza name, open up the double curly braces, and we can access our objects, which is pizza.name. The description, pizza.description. Let's say this and over to the menu. There's our two pizzas. We have our Margherita and the pepperoni, which matches our ref from our script section. We're still not quite done with this one though, because we also need to add the pizza options. We need to make the pizza size and also the price more dynamic in much of the options which will have for each one of these pizzas. For this, we again need to go down to our table. Here we have this table row, which is for each one of our options. We need to create an additional loop inside of here to loop through our two options, so v-for. The variable value of option and we'll also take it in the index. We need to loop over our pizza object, which is the one from the initial loop. We'll need to access our options. The key for this one passing-over option. Then inside the square brackets, we can select the first or the second value with the index number. For the first loop, this will select the first option, and the second loop will select the second option. For the option size, double curly braces, the option, and the size property even in the currency symbol, the option.price. Let's test this out over in the browser. We now have our loop creating our two options, and each one has the unique size and also the price. Good. We're making good progress here, but now, when we want to add each one of these pizzas to the basket, we also need to pass this pizza information to the function. This button which you have here not only do we just need to call addToBasket, we also need to add the brackets and pass in the pizza and also the option up to the function, where we can receive these values, so the item and the option. Now, instead of having the name as a string, we can pass it in the variable name of item.name. The price, which is stored in the option, this is option.price. The size, option.size. Let's try this out. Let's try the nine-inch Margherita. This looks fine. A 12-inch Pepperoni, this all looks like it's working perfectly fine. Next, we'll handle updating the quantity inside of this basket if the same pizza is selected multiple times. 27. Checking For Duplicate Basket Items: At the moment, we'll add a new pizza to our basket. Everything works fine but we start to have a problem if we add the same value multiple times. For example, if we try to add the nine-inch pizza more than once, we already have this at the very top rather than increasing the quantity to be two, we get a new line item down at the bottom. This is the same for any one of our pizzas. Instead, what we want to do rather than add a new line item is to increase the quantity each time. For this, before we push our new pizza to the basket, we first need to check if the new pizza already exists inside this basket. To do this at the very top of our function, we can access our basket, the value since this is a ref, and make use of the JavaScript find method. The find method will run a function for each value inside of our basket. If there is any pizzas existing inside the basket, we'll store this inside of a variable called pizza and then we can work with each pizza individually. What we want to do here is we want to check if both the pizza name and also the pizza size is a match. Remember if we go into our menu such as the Margherita, we can add two different sizes. We can have two separate pizza names, but these aren't a match. We also need to check the pizza name and also the option value too. The JavaScript find method will return back the very first value, in our case, the pizza, which matches a certain condition. We want to return true if a certain condition is met. In our case, we want to check if the pizza.name is equal to the item.name. This is checking if the pizza which is currently in the basket, has a name, which is equal to the name of the pizza, which we are trying to add. There should be a triple equals and using the double ampersand we also need to provide a second check. The second check is to find out if the pizza.size, this is the pizza again, which is stored inside the basket. We want to know if this is equal to the option.size. Again, this is the option size of the pizza which we are trying to add. If both of these are a match, this statement is true and this find method will then return the pizza, which we find in the basket. We can store this inside of a constant or pizza exists and before we go any further, we'll check by login this to the console. Log the value of our constant pizza exists save and over to the browser. Jump into the console so we don't have any pizzas crony inside the here, plus, this is undefined, different values is also undefined. But if we try to click on a pizza a second time, we get returned back at the pizza, which is a match for the basket. We see this is a nine-inch pizza let's try with the 12, this one also works too let's just try the pepperoni. The very first time is undefined and if we already have this in the basket, it will then return back the pizza which it finds. With this information, what we can do now is placing an if statement and we can check if our pizza is found with this constant. If it does want to do is to access the pizza, select the quantity property, and increase this by the value of one. Also, if this is true, we want to return, alter this function since we don't want to add an additional pizza to our basket. Let's now try this. Give this a reload. Our first Margherita our second Margherita now increases the quantity. Let's try pepperoni. Two new lines and a duplicate and each time I click on a duplicate, the quantity is increased by the value of one. Next, we'll also come back to our basket and stick with the quantity subject by wiring up our increase and our decrease buttons, along with a function to remove a pizza from the basket too. 28. Removing & Changing Basket Quantity: As we discovered in the previous video, when we add any one of our pizzas to the basket, we now have all of our pizzas shown over on the right. We also see the quantity which is the default of one. We also have these buttons to increase and decrease the quantity. This is what we're going to work with now. Head over to the menu view and jump down into the template section and scroll down to the basket area. Inside baskets, we have our table body, which is creating our loop for all of the pizzas. Here, inside of the loop we have access to the item which you want to change the quantity for. Then here we have our buttons to increase and also decrease the values. For the first one, which is the decrease, we can add a click listener, which will then trigger a function. We'll pass this item to the function and then change the quantity. Inside the open button, @click, and we'll call this function decreased quantity. Pass in the item so we know which item to reduce and then go down to the second button, which is to increase. We'll call this increaseQuantity. Again, this also needs access to the item which we want to change. We'll start with this increaseQuantity function. I'll scroll up. Remember, if we take a look at this push method here, we can see the structure of our baskets. All we need to do is to increase the quantity property on the object. Create a function, increaseQuantity, take in the item which is passed to it, select the item. We only want to work with the quantity field, and we'll increment this by the value of one. Next, the function to decrease the quantity. Also takes in the item, item.quantity. I will deduct this by the value of one. This is the first thing which we need to do for the decrease function. The second thing is to check if the quantity is zero. If it is, we don't want a line item shown inside the basket with the quantity of zero. Instead, we want to remove this from the basket. Of this we can place in an F section to check if the item.quantity is equal to the value of zero. If it is, we want to remove this from the basket. If we wanted to, we could place in the code inside of here to do this. But instead I'm going to outsource this to a standalone function which is going to be called removeFromBasket. We'll also need to take in the item which we want to remove and then create this down at the bottom, the function removeFromBasket, pass in an the item which you want to remove. First, we need to access our full baskets and since this is a Ref, we access the value property. Then we can use the JavaScript splice method. Splice is going to remove an item from our array by first person in the starting position. So we need to grab the index number of this item. Let's do this by accessing the basket.value. This is the full basket. Then we can grab the index number with indexof. We need to know the index number of our item. This is just regular JavaScript. It's nothing to do with Vue js. We find the index position of our particular item inside of this array separated by a comma. We only want to remove one single item. Now, let's give this a save and check this is working. Click on any pizza. If we keep increasing it the value will increase too. Let's try plus. It works fine. Negative, this will drop down. Then once we get to zero, this item should be removed. This is some good functionality which has been created with pretty small functions. Next, we're going to stay inside this basket and get to work with updating the order total using computed properties. 29. Computed Properties: If you've used view dress in the past and made use of computed properties, the good news is they are here to stay. They are still fully supported in view free, both when using the composition API and also the options API. They'll be an ideal use case for us to calculate the cost of all the pizzas inside the baskets. At the bottom of the basket, we currently have this hard-coded value, which is available inside of the menu view. Let's scroll down and find this. This is near the bottom of our template. We could use regular functions to calculate the cost inside this basket, but the problem with a function is it will only run once each time we call it. If we instead write this calculation inside of a computed property, anytime the data inside changes, in our case the baskets, it's value will be recalculated for us. To do this, go to the top of the file. We need to import computed from the view package. Computed acts as a wrapper. If we break things down, we can first create an anonymous function to calculate our baskets level. Down at the bottom of our script. We'll first create a function which doesn't need a name at this stage. What we're going to do is create a variable called total cost which will be initially at zero, and then we can loop over all baskets and push the quantity field from each one of our pizzas. That is total cost. Baskets, again, this is a let, so we use the value loop over with for each. For each, we'll run a function for each value inside the baskets and store each one inside of item. Then we can grab our empty variable called total cost. Use the plus equals operator to add a new value to our total cost on each loop. We don't just simply want to grab the item.price and push this to the total because sometimes we can have multiple pizzas. For this, we need to multiply the item.quantity by the price. We can access this total cost outside the function. We need to return this. And then outside the function creates a variable or constant to store our computed value. We'll call this one the total. As mentioned, computed will act as a wrapper, so we need to place it inside our function. Cut this out, paste this inside of computed. Now, since we have computed as a wrapper, each time the basket or any other data inside is updated, this function will rerun and the total variable will be updated. This total can now be used inside of our template. Jump down to the bottom. You add the total cost in, add an item as much as the price since we only have one. Increase the quantity. This is also now doubled since inside of our computed value we multiply the item quantity by the price. This all now appears to be working correctly. Later we will improve on this by creating a re-usable currency filter to make sure the numbers are rounded correctly. 30. Composable Files: In the script section of our MenuView which we've been working in recently, if we take a look from the top to the bottom, this section is starting to get pretty big. To help with this, we can move the contents into separate files, we can use as many of these files as we want to, which will keep things really organized. These are referred to as composable files. To keep these organized, we're going to create a new folder called composables to keep these in. These will live directly inside the source and go alongside things like our assets and our components. Click on the Source, a New Folder, Name Of Composables, and if you want to, we can change this folder name, it doesn't need to be composables, this is just a naming convention. Another naming convention is the files which are inside. These tend to have the prefix of use, but again, this is completely optional. For those unrelated to our content, we would have files such as useBasket, usePizza, and useOrders, which are all regular JavaScript files. Let's start with a file called useBasket. Place this inside the composables file, useBasket, and this has the JavaScript extension. This file is going to export a function, so export default, a function called useBasket. The contents inside of this function will be all of the basket-related functionality inside of our MenuView or any other files for that matter. Back to the MenuView, let's take a look for all of the basket-related functionality. We need the basket, let's cut this out, paste this inside of our new file. We don't need the pizzas, this will go into a different file, addToBasket function, we can take this. We also need the increaseQuantity, decrease, removeFromBasket, and also the basket total, which is it's computed value, cut all this out and we should just left with our pizzas, paste this into our composable file, making sure everything is wrapped inside of this function. Since we're not making use of our ref and our computed section inside of here, we can also bring over the imports or copy this. We can see we're still making use of our ref inside of this file, but we couldn't remove computed. Then, import these at the top of our file, just outside of the function. Now to make use of any functions or any of these variables in different files, we first need to return this back from this function down to the bottom. Still within the closing tag of our function, create our returns section and this needs to return back any one of our functions or variables which we want to use in other files. We effectively need everything from here, we need the basket, need the function called addToBasket, we need the two functions to increase and also decrease the values, we need the total. But one thing we actually don't need at this stage is removeFromBasket. This is because the only place this is called is within this decreaseQuantity function, so we don't need to call this from any of the files. This now groups together all of the related functions and variables. Remember, we exported a function here called useBasket. To access the contents inside, we can go over to our MenuView and import this into our file. Import, useBasket. Using the add symbol, we can jump into the top level of our source and then jump into the composable folder and then the filename called useBasket. Next, using JavaScript destructuring, we can store all of the returned values from these functions into variables. The basket, the functions of increaseQuantity, decreaseQuantity, we need to addToBasket, and also the total. I was selecting these from our useBasket function, which we imported just above. All of these constants are now available to use inside of our template. All that's left to do is to now save this, go back over to our project and check everything is still working correctly. In fact, just before we do this, we need to actually call this function for these to be available. Over to the project, add these to our baskets, the increase and decrease functions working. We can remove these, and also our total is working too. 31. What Is Firebase?: So what exactly is Firebase and how can we use it? Well, Firebase is a service provided by Google for websites, applications, and also games. Many of these need services such as authentication, storage, hosting, and also a database to. Firebase provides all of these in one single application. I'm going to make use of some of these inside of our project. We'll be making use of some of these, including the Cloud Firestore, which will be our database, which stores our pizzas and our orders. The cloud Firestore is a real-time database, meaning when any of our stored data changes, the application is notified. A use case for this could be if the admin was to remove a pizza, and then the menu would also be updated too. Also, if you have multiple apps, maybe your website and mobile app, the database could be shared and kept in sync. We can also set up rules and roles for security purposes too. But more on this later. It will also provide our authentication system, providing sign-in, login, sign-out, forgotten password flow, along with multiple ways for users to sign in. We'll be making use of an email and password login. For there is other services available such as Google, Twitter, or Facebook login. In addition, there is also lots of other Firebase services which will be adding such as analytics, cloud functions, and storage. So go ahead and sign up for an account if you've not yet done so. The free account is all we need for testing and building our project, and the next, we will set this up and link to our project. 32. Firebase Setup: Once you've created an account and logged to Firebase, head into the console area and you will see something which looks just like this. From here, we can create our new project. We need to give this project the name, well, will be Pizza Planets. You can call yours just Pizza Planet if you want to, but this is the third version of this class so I'm going to call mine V3. Click the checkbox and then continue. Google Analytics, I'm going to say no for this project. Then give this a few moments to complete. My project is now setup. Let's continue on and then this will take us inside of our project. From here we can get the instructions to add Firebase to our app for either iOS, Android. But in our case, we'll click on the web-link. If things do look a little bit different at this stage, don't worry, these websites do have a habit of changing, but it should be a similar setup process. Now we need to register our app by giving this a nickname. I'll just keep it the same way Pizza Planets Version 3. I'm not going to set up any Firebase Hosting for now. Register. Then once this has completed, it will give us some instructions for setting this up. The first thing to do is we can use npm to install this as a node modules package. Let's copy this link, which is npm install Firebase. Let's close down the server. Paste this in. Now we have some reasons why it's been really slow today. We can just close this down and also restart the server with npm run dev. We can confirm this has been installed by jumping into the package.json. They will have Firebase as our dependency. Back to the Firebase website. The next stage after installing, is to copy over some instructions. I will just copy this over and we'll take a look at what each stage does in just a moment. This will keep us organized by placing this into a new JavaScript folder, which sits directly inside the source folder, create a new file. The Firebase.js. Paste this in. The first thing we do is to import and initialize App functions, which is provided by Firebase. We then have the Firebase config object, which is conveniently populated with all the information for our app. Then at the very bottom, we make use of this initialize App functions just here. Then pass it the configuration details, which then gives us a reference to our application. Now we have the app reference. We also need a reference to our Firestore database so we can add and remove records. From this, we also need to import something from the Firebase library. Up at the very top, we'll create a second important where we're going to import something from the package, which is Firebase. But this time, rather than forward slash app, we need to import this from the Firestore section. The function which you want to import from this section is called getFirestore. GetFirestore can now be used to get a reference to our database. To do this, we pass in our app variable. Just this one here. This inside of a constant called Db, which is short for database, called getFirestore. Pass in our app. When working with fire store, it groups together our data into collections. In our case, we'll have a collection for pizzas, will have a collection for orders, and also we can have one for users too. To work with Collections will need to also import it from the Firestore package. Back to the top, get Firestore package, import collection. We can use this to reference our collections by passing in two things. The first one, we needs pass the database reference which you have stored just above inside this constant. Second, is going to be the name of our collection, which we currently haven't created yet. This one is going to be called Pizzas. This now gives us a reference to a unique collection which we can use to read documents and also add new wants too. To make this available in other components, we're going to export this as a constant. The constant name will be dpPizzas&Ref. Let's copy and paste this in. This second reference is going to be for our orders. DbOrder&ref. This will make use of the same database, but the collection name of orders. This will allow us to import these references into any other components are files. We could do things like read the contents or add new data. We'll look at this next. The final step for this video is to tell Firebase we want to use the cloud Firestore and also authentication. Let's go into the project overview section inside the browser. We can continue on to the console. We don't need any more data from this view. This gives us a chance to add various products to Firestore. We can do things like keeping track of performance. We can add analytics for our use case at the moment all we need is authentication and the cloud Firestore database. Let's start with the authentication. Get started. This is going to let us sign-in with various providers such as Google and Facebook. But I'm going to make an email and password system. Click on this will enable email and password. Now I'm not going to enable the email link, we'll just keep it as a straight forward email and password. This is all we need to do for now. This is currently enabled. Back to our project overview. You can see we've got the shortcut now to authentication. Next we'll add the Cloud Firestore, it also appears in the sidebar. Create our database. We'll begin in test mode and we'll look at security rules later on. But for now this will give us full access to read and write to our database. This feature should only be allowed for testing, and we will look at how we can improve this later on. Click on Next. Choose a location if you want to and enable the database. Later on we'll enable some security rules to keep our database more secure. Good. We now have a authentication mode setup and we also have an empty database. With this in place, next, we'll take a look at how we can add some new pizzas to this empty database. 33. Adding Pizzas To The Database: Now we have the setup work out the way, we can use the dbPizzasRef which you previously set up along with our order reference to work with our database. We'll start by adding the new pizzas to the database in the new pizza components. For this, we need to import two things. So let's jump into the new pizza component, which is in the Components folder and the Admin. At the top, create our import statements where we'll import something called addDoc, which is from the package name, which is Firebase/Firestore, which makes sense because this is a database-related function. The second one we need to import is our dbPizzasRef. The file path has conveniently been populated for us. Both of these will be used inside of a new function to add our pizza. Let's go down to the bottom of our scripts. This is going to be async, create function called Add. Since we're working with a database and we could have things go wrong, we need to place this in a try and a catch section. Catch takes in the error, which we can store it in the e variable. For now we'll work inside of the try section. From here, we can call our function, which we just imported called addDoc. Inside of here, we need to pass in two things. The first is the collection which you want to add to, which is stored in our dbPizzasRef. Remember, this will reference a collection called pizzas. The item which you want to add to the database is going to be our newPizza, which is this object just above. Since this is stored in a ref, we need to access the value. We can now call this add function when we click on the Add button. Remember inside of the application and the admin section, we have this Add button. Jump down to the template. Here we are. We'll listen now for a click. The function name of add will prevent the default behavior, which will stop the page from reloading and losing all of the data or form and also, since we don't know how long this is going to take, since we are pushing some data to an external server, we're going to add await since we can wait on the data coming back before progressing to the next stage. And we can use this since we've marked our function as async. Just before we go ahead and test this and make sure this pushes our newPizza to the database, we'll add some error handling and also a message to display inside the browser. This message can be stored inside of a constant. Just above our newPizza, const message, store this inside of a ref, since we'll be updating this. Initialize this as an empty string, and back to our add function where we'll update the value of this message. Since we are making use of async, await, any code which runs below will only happen once we get the information back from our database. On this next line, we'll know if this has been a success or if there's been a failure. Since we've wrapped this in a try section, we'll know that this code will only run if there is a success. We'll add the failure code inside of the catch area. Below, access our message.value, which will be a success. Open up the backticks and we'll say Pizza, place in a variable name. We'll place in a variable so we can access our new pizza and the name, newPizza.value.name has been added. And then if there's been an error, jump into the catch area, we'll access our message.value and set this to a string where there was an error, add in the pizza. We next need to output this message inside of our template. I'm going to do this at the very bottom, just below this button, inside of a span. I'll put our message and the last thing to do is to test this out into the browser. Remember our database is currently empty. Inside Admin, we'll go ahead and add a Margherita. We're going to move the example code. Click the Add button to trigger our function. There we are, things are starting to look good. We've got Pizza Margherita has been added into the database and refresh. This is looking promising. We have our pizzas collection, which we created inside of our Firebase config, which is this one just here. We've then made use inside the newPizza of a method called addDoc, and it's addDoc method then referenced our collection, which is Pizzas, and the value which is newPizza.value. Into the database, this Pizzas collection has a document's ID which is unique and then all of the contents of our new pizza. Let's try one more, Pepperoni. Add this. There we go. We know how to document references and if we click on this one, this one is now the Pepperoni. Why not? We'll add one more back to our new pizza form. Vegetarian. Say mozzarella, peppers, onions, mushrooms. It doesn't really matter at this stage. We'll update the price, say eight and 14. Add this. Then we also, we've achieved our objective for this video, which is to push a new pizza to our database. But just fall wrap things up if we take a look, this text just here, we also need a little space into the left of the message. In the newPizza file, go down to the bottom. Add a style section. We'll give this a class or span, a class of message, access this and set some margin on the left, 1rem should be fine. We need to add a new pizza so we can see this. We'll go for meat feast. Change the description, pepperoni, salami, beef, and ham. Change the price. There we go. Coming up next, we'll see how we can retrieve these pizzas from the database and display these inside of our menu. 34. Getting Pizzas: To retrieve pizzas from our Firestore database, Firebase provides a function called getDocs. Also since we want to make use of these pizzas in both the menu and also the admin section, we can create a shared composable file to do this. Let's create a new file in the sidebar. In the composables, we'll create a new file called usePizzas.js. This will follow a similar pattern to the other composable which we created. We're going to export a function which we can import into any other file. Export default function called usePizzas and create the function body which is going to contain all the code which you want to make available. Outside, we can add our imports at the top of the file. We need to import the ref from vue. The reason we need this is because we need a constant which is going to contain all of the pizzas locally. Const allPizzas is equal to a ref and this will be an array of all of the pizzas. Now for actually getting the pizzas, we need to import some items from Firebase. The first package we need is one we mentioned before which is getDocs. This one is from firebase/firestore. Then we also need to import the reference which we created in the Firebase file to have access to this pizzas collection so we need to import the dbPizzasRef from this Firebase file. Import the dbPizzaRef from this file path. Then just below where all pizzas are constant, we'll create a function, which is going to be responsible for retrieving our pizzas from the database. This function will be marked as asynchronous since we need to await on the pizza data coming back before we can actually use this function called getPizzas. Then we can call our method from above which we just imported called getDocs, pass it in our collection reference, which is dbPizzasRef. We're going to store the return value in a constant called docs and we'll also await this data coming back. Then run this function and actually get the pizza from the database. We have a couple of different options. We could trigger this manually by calling getPizzas or we can trigger this when a component mounts using the onMounted lifecycle hook. From the vue package, we'll do this with onMounted. Then as soon as this code or this component is mounted to the DOM, onMounted will be called, which will then call our function called getPizzas. This avoids needing to call this function manually and it means it will do this automatically behind the scenes as soon as the component has loaded. If we go over to the project and jump into the console, we can test this out by doing a console log for the return value inside the docs. Refresh and we still don't see anything inside the console because this code isn't currently imported into components. The onMounted lifecycle hook is not called. So let's do this but first, we'll return the values which we need to import. All we currently need is this reference to allPizzas. We'll start by importing this into our MenuView which is in the view section. Then just like we did with the useBasket composable, we first need to import this, so we'll import usePizzas from our composable file, and then using this structuring we'll import our allPizzas reference from this function. Const allPizzas is equal to the return value from usePizzas. We've now seen an error since we are doubling up on this allPizzas reference, we are importing this from our composable. We also have this example code just here. We no longer need any of this. We can remove this section. With this being the only code which makes use of ref, we can also remove this from the script. If we reload, we don't see anything logged to the console just yet. This is because for this to actually mount and to call our function, we need to go into the menu component. Select this, we then see a log inside the console. Open up this object and there is a lot of information which is returned back from the database. A lot of this information we actually don't need at this stage, but one thing we're interested in is this doc property. Open this up, and inside is an array containing each pizza documents which we have created. Let's take a look inside. Again, there's lots of information here. We can see the document ID. We can go into documents and data and drill down into the data values, including the name, the options, and the description. What we can do now is to loop over all of the values inside the docs. Unfortunately, we don't need to drill down into all of these levels of our object. Instead, we can call docs as a function. This will extract all of the fields which we need and make these accessible inside of our script. Jump back into our composable. We can move the log, access our returned documents and call it for each loop. For each, we'll run a function for each one of our values. In my case, I currently have four pizzas stored. Then to filter out all of this unnecessary data, I'm going to create a new object with a simpler structure, so const pizza. Inside here we can structure this new object with only the data which we need. As we have seen, each one of these has a unique ID. We can see this if we close this down, this ID, so we set the ID. To access the pizza information on each loop, we also need to pass in a variable to our function. In the first loop, doc will be equal to our first pizza. On the second loop, this will be our second pizza, and so on. We access this and select the ID property. Then we need to pass in the data which would have seen, which is stored inside the documents,.data.value. Now we need access to all of these fields. As mentioned, we don't need to drill down into all of these fields instead, what we can do is to access our documents and call the data function. Using the JavaScript spread operator, we can also extract the values which we need in places directly inside of our pizza object alongside our ID. Finally, on each loop, we want to push the value of this pizza to our allPizzas reference. Just blow this object, access allPizzas.value, and then use the JavaScript push method to push each one of our pizzas. This is everything we now need. Back over to our menu file. Remember we've imported all of the pizzas from our composable. Also, this variable name is also the same as we've been using previously. Now if we save this and reload, the table is now populated with the pizzas from our database. Another thing we can now do since we have access to a unique ID for each one of our pizzas is to change the unique key for each value inside this loop. Rather than making use of the index, we can access our pizza.id. We no longer need this index, so we can just reference our pizza. The same for the options. The options need to consist of two things. The first one, again is the pizza.id and since the pizza has two options, we'll also add this to our option.size. Again, we can move the index and there we go, everything is now complete. Next, we'll continue on with a similar theme by also making use of these pizzas inside the admin section and also we'll discover how we can delete these pizzas from our database. 35. Listing & Deleting Pizzas In Admin: As we discovered in the previous video, if we go into this admin link, we also have this menu components which we created earlier. As we also did with the menu in the previous video, we now need to pull in our pizza items from the database and display these inside the admin. Also, we'll make sure that this red cross, which we have listed next to each one of our pizzas, also removes this item from the database. Let's jump over to our use pizzas.jsfile. This is the composable which we set up in the previous video. We need to import two additional things from firebase, in particular, the firestore package. What we need is something called delete doc, which as it sounds, is going to delete an item from a collection and also the doc and we're going to use doc to refer to any particular document which you want to select. Then this is passed to delete doc to actually remove it. We'll create a new function inside of this object just below onmounted, this will be async called delete pizza. This delete-pizza function is also going to be passed the ID on call it. Because of course, we need to know which pizza we want to remove and then we can access this doc which we just imported from Firestore. Doc is going to take in two things. The first is the database collection reference, which we still have stored inside of DB pizzas ref and then it needs to take in the idea of the actual pizza which we want to remove. This is not actually responsible for deleting any pizza all we're doing here is getting a reference to a particular documents. We narrow in this town by collection and then narrow it down by the ID. We can then use this reference to pass, to delete doc. On to passes, we can store this inside of a constant, let's say pizza, and then pass this to delete doc. We'll await this. We'll await for this operation to complete. Then we'll call our method above call get pizzas. The reason we want to call get pizzas once this is completed is because we need to update our application. We're deleting an item, we then call get pizzas, which is just here. This is then going to call for all the pizzas which you stored inside the database, minus one which we've removed. This will then update all pizzas, which is our ref here at the top. All pizzas is then returned, document is composable, and this will then update our menu, which we've created on the left here, and it will also update the menu inside the admin. To use this function, we need to return it also from our composable delete pizza, then let's open the sidebar, jump into the components, the admin section, and inside of here we have this pizzas.vue, which is responsible for this section. Pizzas.vue needs to import the sections which we need so placing a script tag at setup and import our composable, which was use pizzas. Use pizzas returned both of these values, which we can destructure and store inside of constants so we need all pizzas. Delete pizza, which we can retrieve by calling usePizzas function. Down to our table section where we have some dummy data. We've got our margarita in place and now we can use the table body section to loop over all of our pizzas. V for pizza in all pizzas, pass in a key and now we have the unique pizza ID from the database so we can access this. The margarita can be replaced with the dynamic pizza name so pizza.name. With this being the admin section, we don't need all of the additional information such as the description and the prices, all we need to see is a reference to each pizza. We can see this now over on the left. Each one of these crosses to remove the pizza from the database is stored inside of this button so we can add a click listener and then reference our function which is delete pizza. This also needs to pass the pizza ID which we want to remove. Now we can access this with pizza.id. Let's give this a try. We'll remove the meat feast. As soon as we do this, we see a small issue. If we take a look at the original four pizzas which we had, this is fine, this is the original four from our database but then if we go to our composable, we've removed this from the database, but then we've called get pizza once again, which is then getting our free existing pizzas from the database and also adding these to all pizzas. Basically, instead of replacing all the pizzas, we keeping the existing ones and adding the three additional items. Effectively, this is not a bad thing at the moment because we can see that the three additional items doesn't include the meat feast, which we've removed. That all seems to be working well. To fix this, this is pretty simple all we need to do just before we get our new pizzas is to grab our reference of all pizzas, their value and reset this to an empty array before we get our new documents. Let's try this. Refresh the page. Here's our three pizzas, remove any one of these, and this is now all working correctly. On final improvement we can make is to add an error message if there was an error or an issue, delete in the pizza, or even getting the pizzas from the database. For this, we can create a new message reference in the all pizzas file. Just underneath all pizzas create a new constant to store our message. This will be a ref and by default an empty string. Then inside the get pizzas, we can add some error handling with a try and a catch area. Let's place this in at the very top at the try section and catch. Catch will take in an error. Then we need to move all of our code inside of the try section. If you locate the end of this function, grab everything all the way up to all pizzas.value. Cut this out leaving just the try and the catch area inside of here and then inside the try section, paste this back in and this is the code which you want to run and try to be successful. But if there is an error, we can then go into the catch area and update our message. The message.value is equal to a string and we'll say there was an error fetching pizzas. Please reload the page. Just like we did with the pizzas, we can also update the message and reset this to be an empty string each time we run this. The message.value is back to an empty string. Also the same for our second function which is delete pizza. The try-block, the catch block, which takes in the error, crop the existing code, cut these and replace place this in the try block. Reset the message, it's been an empty string, and then update the message inside the catch block. It's b string, then if you wanted to, you could also include the error message, which we'll get back. But for our simplicity, I'm just going to create a string which says there was an error deleting the pizza. Then please try again. Next, so we can use this message inside of our components we can return this back, jump back into the pizzas.view components, import this, and then just below the header section, create a new p element to display our message inside the double curly braces. Add class for styling of error. What will also do as a temporary measure is to also add some text inside of here. Any text is fine this is just so we can add some styling and see how this looks without having an actual error message. Down to the style section, where we can add some styling to this error class. First of all, a color which will be in RGB value of one at zero for the red, 67, and 67, which will give us this red color. A border of one pixel, a solid line round off the corners with some border-radius, and then some spacing inside and outside with padding and margin. Padding 1rem and also 1rem of margin on the outside, save, test this out. This is how our error message will be displayed inside the components but if we were to remove the text and then leave this as an empty p element I can see here. Removing this error text will still show the red border because the p element is always there regardless of if it has any messages inside or not. To remove this, we can add a v If statement to only show these elements if the message exists. If we have a message and its elements will be displayed, if not, it will be removed from the dom and we're no longer see this red border. 36. Creating Orders: To create an order follows a similar pattern to create in a new pizza. Let's go into the used basket composable, which is inside a composable folder. Open this up and we first need to import two things. The first thing, we need to import addDoc from the Firestore package, so this one is from Firebase/Firestore. Then the reference to the actual orders collection, which you want to push this to so let's import this from our Firebase file. What we need to do is to import this DB orders ref. We already have the basket contents inside of the basket array and this is pushed to when we add something from the menu section. Over on the right here is the basket. Now we need to create a new function to add the oldest collection inside the database. Just below this function here, back to this arrow on the bottom, it doesn't really matter. This will be async, the function name of add new order and then, what we need to do inside is to wrap this in a try and a catch block so it can handle any errors, pass in the error. The first thing we need to consider is that this basket up at the top ,this is currently stored as an array. But Firebase requires an object, so we can use the spread operator to merge in the current items into an object. Let's go into the try section and we'll restructure our order by creating a new object. The first property to add is going to be called createdAt, and this will keep track of the order in which our orders are placed and therefore, we can deal with them or display them in the correct order in the admin. But this will just create a new JavaScript date, install this inside the database. Then as mentioned before, to grab the contents from our array, emerge these into our order to create a pizza's property, which itself will be an object where we're going to merge in the properties from our basket and since this is a ref, we need to access the value just after the order. This is now where we're going to make use of the addDoc method from Firestore to push to a particular collection. A collection is our DB orders reference which we just imported. Call the addDoc method, which takes in the first parameter of DB audit reference, which is a reference to our oldest collection and then we'll push this particular order to this collection. Once this has been done successfully, we'll await this operation finishing and then we can reset the value of our basket to be an empty array. This will clear out any pizzas from the basket, so the user can start again. This is all we need now for the try section and keep the user updated, we'll create a new variable to hold a message. The default message is going to be something like your basket is empty, so we can see this or we don't have any pizzas over on the right-hand side, so const basket text, wrap this in a ref, with the text of your basket is empty. This will be the default value and we can update this depending on if the order was successfully added or not, go down to the function. Let's go in the try section and at the very bottom we'll access the basket text.value, and update this to be new strings, something like thank you, your order has been placed. Into the other section, and just like we've looked at previously, you can also incorporate the error message if you want to, but for simplicity, I'm just going to update the basket text to be a new string, which will be equal to there was an error placing your order. I'm going to say please try again. We can call this from our components. We need to return back our function call and we also need to return back the basket text so we can display this inside the components. As you order the basket text and the basket is displayed inside the menu view, so head into here, and we can now import these from our composable. Here we already have a reference to our use basket composable, so we can jump into the brackets and also import basket text along side add new order. Let's start with add new order, which we can trigger from our button. Go down to the basket div, and down at the bottom we have this place order button, this now for a click, which will trigger our function. Now the final step is to add the basket text and remember by default it says your basket is empty, so we don't want to display this if they're already our items inside the basket and for this same conditional rendering will help. Let's jump up to the top of the basket section, just below the heading. Let's create div sections contain this and inside we'll make use of v-if, to add our conditional rendering where we want to check if the basket, the length is greater than zero, i.e. we have some items inside the basket, so if this is true we'll cut the closing div section and then go down to the end, just below our buttons place order, close this off. You can now see if you go into the basket section, we don't have any of these buttons or the order total. But all this will create a new div, which is the v-else section and this section will only be displayed inside the browser if there are no items inside the basket, which is the current state. But this will place inside the double curly braces, in fact we'll put a p element in to begin with. I'll put the basket text and we're seeing errors let's take a look what this is. We have no adjacent v-if so this is the v-l section and this should be the v-if. We just have this extra div section which we can remove, make sure this is correct. We've got this div, the opening tag, all of the contents, the closing section, v-else. We also need to move this closing div from before this all looks fine now just makes sure that after the v-l section, we have two additional closing divs, so free in a row. Then up at the top of the h3, the div, and then the table. We should now see inside the browser we have the default text of your basket is empty, click on any item and now the basket length is greater than zero. Therefore, we can display our table. Now let's add a few more items to our baskets and we can test by clicking the "Place Order" button , this looks good. We have the funky message, go over to the Firebase console. This is console.Firebase.google.com. My Pizza Planet V3 into the database. We now have oldest collection, we'd an order ID, this looks fine. We've got one 9 inch Margherita and two 12 inch. Let's try one more 12 inch Pepperoni and Margherita, place your order. Good, everything is working correctly and next, we're going to fetch these orders from the database and display them inside the admin. 37. Fetching Orders: Now we can successfully place orders and add these to our database. We now have the task of retrieving these orders from the database so we can use them inside of our project, and in particular the admin section. Earlier, we created a current orders section down at the bottom. We're going to retrieve these from the database, loop over these and place them inside of this table. To keep things organized, create a new composable inside the composables folder. This one is going to be called useOrders. As ever, we need to export a default function, useOrders and place the code inside of there. To begin, we need to have a constant to store all of our orders inside. We'll call this allOrders. We'll also make use of ref which is going to surround our array. We'll initialize this as an empty array and then we need to import this ref from the view package. We also need a function which is going to retrieve these items from the database and push these to our allOrders ref. We'll create a new async function, All getOrders. We could do something similar which we've used in the past. If we take a look, our composable, which was usePizzas. Inside of here, what we've done in the past is we've retrieved our documents with getDocs. We have this function called getPizzas. This one is a little bit more simpler because all we do must call our getDocs method, and pass in a reference to all pizzas collection. If we wanted to, we could do exactly the same by passing a reference to our orders. But for this use case, remember that in the previous video, we also pushed a new order to the database, which included a property called createdAt. When we're pulled in the pizzas from allPizzas collection that weren't in any particular order. This is where this works a little bit different to allPizzas. We want to make sure that we're pulling it the orders in a particular order based off the data in which they were created. Back to useOrders. To do this, we need to make use of something called query. We need to import this. Import query. This is from the firebase slash firestore package. From our firebase.js file will also need to import this reference to allOrders. Now back to our getOrders function, we're going to make use of this query which we just imported. Query. What we need to do here is we need to pass it in three things. The first one is a reference to our collection, which is dbOrdersRef. Then we can also pass in additional search parameters. In our case it's going to be orderBy. OrderBy. This is also a firebase method which is going to order the contents that we're pulling from our database by a particular field. If all we can use this, we also need to import this. Then we pass inside the name of the field, which you want to order this by. In our case, as we can see when we created a new order, this property is called createdAt. Let's grab this, paste this in, and then store this reference inside of a constant called queryData. From here on out, what we do is pretty similar to when we retrieved the pizzas. We need to make use of getDocs. We need to then loop over these docs, and then push these to our constants. With this in mind, we'll copy all of this section. Just above the catch block will copy the brackets. These are the brackets for each loop up to our the documents. Copy this, paste this just under our query data. Again, we can make use of lots of this data. We can keep this constant name. We can search for our getDocs. All we need to do instead of searching directly for allPizzas or allOrdersRef, we're going to pass in our filtered queryData, then we'll loop over each one of these documents instead of creating a pizza we'll structure a new order. The id is accessible in the exact same way since any one of these documents comes back from firebase with the same structure. We can also merge in the document data, which is all the fields from allOrder, such as allPizzas, and our createdAt dates. The final difference is this line here, instead of pushing to allPizzas, we pushed to allOrders. We'll push allOrder instead of the pizza. We'll give this a test before we place this inside of our components, just below our closing brackets for loop, place in a console log or allOrders, no value. But this console log to actually run, we need to import this composable into the required components. This will be the allOrders dot view, which is inside of components. Then inside the admin folder, open allOrders. We don't currently have a script. Create a script, setup, import our composable, which is useOrders. Then using the structuring we can import our allOrders, which I'm not sure we've actually returned, so we'll take a look at that in just a second. Call our useOrders function. Just go back. We need to return all orders to make use of this button inside our function. Return allOrders. They should now run our console log. Enter the admin section. Open up the console. We currently don't see anything inside of here since we have not called our getOrders function. What we'll do, we'll just blow our function is we'll also import or mount it. We'll run our function called getOrders as soon as this component is mounted. This also needs to be imported at the top. We also need to import getDocs from Firestore. Let's give this another try. Open up the console log into the target where we can see we've got an array with two separate orders. These are structured exactly like we created. Inside of this order here we have the id. Then we merged in the rest of the document data, which was the pizzas object and also createdAt. We're almost done now with this video, the next thing we're going to do just to round things off, is to wrap our code inside of a try and a catch block. Jump into the getOrders function. Grab the full contents from the console log of total query data. Place in, try and also catch. Catch takes in error. Then pasting the contents inside the try section. We know this is working so we can also remove this console log. We'll leave this one there. Coming up in the next video, we'll move on with our orders and loop over these and place these inside of the admin view. 38. Looping Over Orders: Now we know we can successfully retrieve the orders from Firebase and store them inside of our allOrders variable. We've imported these inside of the orders.view file, and we can make use of these to loop over and display this inside of the table. This is the table that you see inside the admin view down at the bottom. Just like we did with the pizzas above, we can loop over all of this data and display the correct values. The first thing inside of the current orders, we need to change this hard-coded value of five. We can get the real value with allOrders.length. We currently know we've got two orders, so this looks fine. Then have the table headings. We don't need to loop this section because we don't want duplicated headings, but what we do need to do is to create a loop for both of these following two additional rows. The first row with the order number, and the second row with the pizza details. To do this, we'll create an additional wrapper called template. Cut out both of these table rows. We can pass in an element called template, paste these back in, and we can now make use of this template to add loop two. Template will add a wrapper to this section, but it will not add any additional HTML, so it's effectively an invisible wrapper. Place in a loop with v-for, order in allOrders. We need a key. We know we've got a unique key for each order stored inside of ID. This is order.id. We can also make use of this order.id instead of this order number. Then we can go down to our pizza information which is stored inside this row. Remember though for each order we don't just have one single pizza, so therefore we need to create an additional loop to loop over each one of the pizzas inside of our order. For example, we're currently looping over this first order here. We then need to loop over orders.pizzas to access all the values inside and display these as table data. v-for, and we'll say order item in order. Order is the full orderItem, which you have from the first loop, but we want to access the pizzas. We can see if we go over to the console and jump into the pizzas, we don't have the pizza id stored for each one of these. But each pizza name and size is also unique, so we can create a key from this. We'll say the orderItem.name. We'll see oderItem.size. We can access the orderItem on the pizza properties such as name, size, quantity and price. The first one is the name, the second one is the size, so orderItem.size, quantity. It's up to you how you want to handle this price to display inside the admin table. If you wanted to, you could first of all try to output the orderItem.price. If we go into the admin and then refresh, we can see our structure. We've got the order number, the pizza name, the size, the quantity, and the price will be displayed as a single price of one pizza. If you wanted to, just like we've described here where we said this is the total. I'm going to multiply the price by the quantity. Let's multiply with the star by the orderItem.quantity. This is now the total for each one of these lines. Next, what we're going to do is to come back to these orders, and we'll see how we can remove items from our database using this red cross. 39. Deleting Orders: Next we'll be focusing on how to delete the orders. If we go over to the usePizzas composable file, this uses the same process as we used before when we deleted the pizzas. We had this function called delete pizza, which took an id, we cleared out any existing error messages, we then stored a document reference by accessing our collection, and also the id. We called a method called deleteDoc which took in the pizza we just referenced, and then finally we then called getPizzas, which pulled in all of the rest of the pizzas which were still available. So to keep things nice and simple, we'll copy over all of this function and then go into the use oldest file and paste it in. Of course, we need to make a couple of little changes we'll call this delete order. Again, this will still need an order id passed to it. We'll also clear any existing message. This will be const order, this will be the dbOrdersRef, and we'll delete this order. GetOrders and in the function call this one should match the one which you have just above here. We can then change the message, there was an error deleting the order. Then this will also need some additional import, we need to pull in our doc. We need to pull in deleteDoc, and we'll also need to create this message too, so let's jump up to our imports, and from Firestore, we need to reference our doc, and also delete doc. Since we're dealing with our orders, we already have the orders impulse just here, so that's completely fine. So we have the message. We need to then create this as a ref, const message is equal to a ref, which we'll initially set to be an empty string. We already have this imported, so this is fine. We then need to return some values back from this composable down to the return section. We'll return back, I would delete all the method and also the message to display inside the components. We're then going to go over to our components into the admin folder and into the orders, the orders.view, where we can then create some imports. We're already importing our use orders file, so we can then destructure, delete order and also our message. First of all, we'll call delete order if we take a look at the admin section and then go down. So we all do remember that each one of these order numbers has this little cross, which is a button, and this is going to call our method, which we can pass the order id to. This is the button just here. The class of btn remove. We listen now for a click, which we'll call delete order, and delete order is going to take in the order id, which we're going to pass to this function. I will need to grab this from our loop so we have order the id which you've already used. We can just put this inside the brackets, and this will be passed to our function. Let's give this a try. Into the project. We've only got two orders currently inside of here. We'll try to remove this one. We see this jumps up to be three orders now, but we do need to currently refresh to then see any updates. But this is fine for now, we'll return back to this in the future and we'll improve on this by adding our real-time functionality from our database. This will mean as soon as any database changes will happen, our application is then updated with the new values. Finally, remember we also imported this message at the top. What we can do is display this just below our header inside the P element. Not only do we need to output the message inside of here, but we also need to add some conditional rendering. We don't want this message to always be displayed or take up any space which it currently is. If we take a look and remove this and save, you can see if we comment this out and hit "Save", this section will take up some space regardless of if there is a message or not. So what we'll do to only show this if there is a message, placing the v if section to only show if there is a message in place, and for styling, we'll also add the class of error. 40. Show & Hide Admin Blocks: Inside the admin view may look okay at the moment but imagine if our application was a lot bigger and we had lots more pizzas in the menu or lots of orders. This page could get really long. To help with this, what we're now going to do is to show and hide each one of these sections. It makes the page a lot smaller and we don't need to read all this information. For this, what we're going to do is to create a local variable in each one of these free admin components, which will then be toggled with the text of show and hide. To begin jump into the sidebar. The components, the admin, and new pizza. Well, we'll create a new local constant called show newPizza. Set this equal to ref and set up our ref, which will be equal to the value of true. We'll display this by default. This will be visible inside the admin and then once a user clicks on this, this will be toggled to be false. We'll do this inside of our templates and in the header section just below our title, places inside of the small elements. Inside here we want to show you the text of show or hide. We can also conditionally render this section inside of the double curly braces. What we can do is we can say if show newPizza is equal to two, we display the text of hide. If it's not we'll say show. Currently, this is set to true so we'll see the text of hide up at the top. We can add the class of toggleBtn. Listen out for a click when I click. All we want to do inside of here is to search your newPizza be equal to the opposite of what the current value is. Let's try this out. Click on hide. This will now toggle and then we can use our show newPizza variable to show and hide this full section. The section which we want to show and hide is the form. We can still keep the head in place, which has the button and also the title. Inside the form, add v-show which will only show these contents if the value of show newPizza is equal to two. It's not currently toggling back. Let's try and refresh. That seems to work fine. I'm not quite sure what happened there. We can now replicate this section in the orders and also the menu. Let's jump into the orders. Import our ref vue. Create a local variable. This one can be show orders. The initial value of true. In fact, what we'll just do here is we'll copy over the newPizza section we created, so the full small section. Bring this over so it's consistent. Place this just below our current orders, Level 3 heading, this same class. This time, all want to do is to change this to be show orders. Change this in all of the other sections. We then want to show and hide. Our table would be show equal to show orders. Let's give this a go. It can hide. This has gone. Then show reveals this section. Finally, the menu section, which is in the pizzas or vue. We also need to import ref vue. Our local variable called showMenu, the initial value of true, and then just like before, below our Level 3 heading, we need to once again copy this small section, paste this in, and then replace all of our variables with showMenu. Finally, add v-show to our table. Let's try this out. The menu, hide, and show. Just as a quick finishing touch. If we hover over this button, we can toggle this to be a pointer. We have a class of toggleBtn for each one of these sections. Since we use this in multiple components, go into our assets and the main.css class of toggleBtn. We will set the cursor to be a pointer. There we go. This now works for all three of our sections. 41. The Sign Up Function: This project is going to use Firebase authentication to manage user accounts and allow them to sign up and sign in. If we go into the Firebase console and into the Project overview section, you should see just below we have our authentication and our Firestore Database shortcut setup. This is because in the early videos we already enabled these in our project. But if you don't currently have authentication setup, all you need to do is to go down to the all products section and add it with this authentication link. Since mine is already enabled, I'm going to go back to the project to set this up. Let's keep things organized. Let's create a new composable inside of the composables folder, a new file. This one's going to be useAuth.js. Then, as with all of the other composables, we'll export a default function. This one is useAuth. Then we'll add a return statement down at the bottom before we forget. Since we don't currently have any users in our project, it would make sense the first function we add is to create a new user. For this, we need to import some functions from the Firestore Firebase authentication package. Above our function, we'll do these imports now. What we need to import is something called getAuth. Also to create a new user, we need to import a method called create user with email and password. This is all camel case. After the first word makes sure that every following word begins with a capital letter. We're going to import this from Firebase/auth. Next we'll make use of this getAuth import, which is going to initialize Firebase authentication. Then we'll start to reference this inside of a variable. Jump into our function. Call this auth, which is equal to getAuth. Since this is our method, we need to call this with the brackets, the next auth function, which is going to create a new user. This is going to be asynchronous called sign-up. When we sign up, since we are creating a user with email and password, we also need passes to this function when we call it. We'll take a look at how to do this soon inside the function body or error handling. Create a try and a catch area, passing the error. Then inside of the try section is where we're going to call the create user with email and password function, which we just imported. Let's await the return value. Create user with email and password. This is going to take in three separate things. The first one is a reference to the current of instance, which is the one which we set up at the top. It also needs to take in the email and also the password. Here we are. Also to store any error messages, we can create a new reactive variable. For this, we first need to import ref from view and create our constant just below off inside the function called errorMessage. Then set up our ref, which is going to be initially an empty string. There we are. If this section is now successful, if the try area is all working fine, we can then clear out any existing messages. We'll reset the errorMessage.value to be back to an empty string. Then inside of the error section, we can also update this error message too. If we are doing something like signing up a user, we don't just want to send back a generic error message, such as 'there was an error, there was a problem signing in' or anything like that. It would make sense to be a little bit more descriptive about what the error is. They might not type a password which is long enough, it may not have the correct characters in place, the email may be already in use. For this, we need to access our error, which is passed to the catch area. We have a property called code. This code is a string of text which is equal to an error message, and two common ones which Firebase provides is a string called auth/email already in use. It looks like this. It's auth/email-already-in-use, with dashes between each one of these words. Also, another useful one is auth/weak-password. These will be useful messages to pass back to the user. But first, we need to make sure that we can detect which one of these is being generated inside of our error. This we can place in an if-else statements, we can place in a switch statement, whatever you prefer. I'm going to go for the switch. We can add to this later if we want to access the error, which is passed to our catch object, then we can access the code. Then we can begin to build our individual cases. The first one, we'll check for this string just here. Paste this in. If this is true, if this is a match, we want to set the errorMessage.value. To be a little bit more descriptive, we'll say something like a user with that email already exists, please login. If this is true, we can then break out of this switch statements. We don't need to go any further down another code. The second case is equal to our second string, which we had down the bottom here. Let's move this. Paste this in as our case. If user enters a weak password, this will be an errorMessage.value. We'll update this variable to say the password should be at least six characters long. We then break out of this of this if this one is true. Finally, with the switch statement, we should also add a default message down at the bottom. This is for if any of the above cases are not matched, then fall back to the default section, which will again update the errorMessage.value. This one can be a little bit more generic. We'll say, sorry, there was an unexpected error. This is all we need in our sign-up function for now. The last thing to do is to return our sign-up function and also our error message. Both of these are now going to be available to call from our components. We'll do this next when we create the sign-in form. 42. Creating The Sign In Form: Great with our sign-up functionality now working, we also need a form to capture the user's information. For this, this form is going to be dual-purpose. It will allow the user to both sign in and also sign up to create a new user. For this let's jump over to our sidebar and we can create our new components. Jump into the components, and this one is SignIn.vue. First, we'll create the script section which will make use of setup. At the top, we'll import our use of composable, which we created in the previous video and set this equal to @, which is the root of our source, composable UseAuth. Grab our variables which we exported on the first one is sign up. We also need our error message. Grab these from the above, UseAuth function. This just needs to be from, there we go. Also a form data variable to store the email and password which the user enters. We'll link this to our form using v-bind. This one is called the user data. We'll also need to import the ref from the vue package. Set this up as an object, which is simply going to contain the email and also the password. By default, both of these will be empty since the user has not entered any values. Then import our ref from vue. Down to the template area to create the form. Since this one is going to be a pop-up modal so we can show and hide using CSS, we do need to create a couple of wrappers to enable this. The first one will be a div with the class of modal, which is the main wrapper. Then also nested inside will create a second div with the class of modal_content. This modal will be toggled inside the header. What we'll do soon is we'll create inside the header a link about the top, which will then pop up our modal of all of these contents. Then inside the modal, we also want to create a little cross in the corner, which will then close down this model. The close will be a span and the class for our styling of close_modal. The close will be a HTML entity, which is the ampersand, the hash. Then 10060 and a semi-colon. A p element below, this one is going to be for the text so please login to continue. Add a class for our styling of modal_text. Then below this we'll create a form for the user to sign-in. We don't need an action, we'll handle this with future yes. All we need inside of here is an input for our email, an input for the password. Also we're going to create two buttons. Let's begin with our email section, which will be a div as a wrapper class of form_group. The form label of email address. The input for this one, since this is an email, we'll give this the type of email, the ID of email, the placeholder text of enter email. Then we can also use v model to bind the value of this input to our user data just above. We need to access UserData.email. There we are. This inputs also has the ID of email. We can link this to our label and then copy our div section. This one is for the password. Paste it in just below. The label for password and for the input, the type for this one is also password, the ID is the same. The placeholder of enter password. This needs to bind it to UserData.password. Then our two buttons down the bottom, just below our div. The first one is sign-in. Place in a button with the type of button, the text of sign-in. Duplicate this or copy and paste. Now what we need to do is to change this text to be sign up. Later this will give the user the option to sign-in or sign-up using the same form modal. There we are. We've created a new sign-in modal components. To see this, we need to import it exactly where we want it in our project. As mentioned before, this will be inside the header so we can see the text up at the very top. Let's go into the header up to the script section. In fact, we don't have a script, so let's create this now. Script setup. We'll import our components, which is sign-in. The file path, since we're inside of the components is simply./SignIn.vue. Then place in the components at the top of the header section. You may be wondering why we add in a sign-in form into this header area, but this will become more clear very soon. But placing this inside of the header for now, we'll keep this group together with the sign-in and the sign-up buttons, which will display at the top of the header very soon. We'll click on a button, this will then pop up the form and then we'll be able to close it down with this cross at the top. Also it looks a little bit messy at the moment, but we'll fix this later on with our styling. For now, let's concentrate on the form and signing up a user. Back over to the sign-in components which we just created. Since we've created the sign-up function inside of our composable, we'll work with this sign-up button down at the bottom, and we'll link this to the function with a click listener. We can prevent the default behavior, which is to refresh the page, call our sign-up function, which is going to take in the email and the password as we set up inside of our composable. Both of these are available inside of the script. We have the UserData.email and password. Let's pass in both of these values. First is the UserData.email, then the UserData.password. It should be all we need for the sign-up function. Remember, we also imported the error message. Let's display this just below our p elements with the text of please login to continue. Place it inside a span with the class of error_message. Inside the double curly braces pass in our error message. We did quite a bit there. Hopefully this all works. Let's jump over to the project, gives us a save. We'll create a new user to continue. It's a little bit difficult to see, but we'll fix this in the CSS very soon. Place in an email address, and also a password. Just see this, if I hover over, click on the "Sign Up" button. At the moment, we don't see any feedback, so we don't currently have an error message which is a good sign. We will also close this form down automatically after signing in and signing up. But for now jump back over to the console into the authentication section, but we should see the user, which would just signed up. 43. Modal Styling: [BACKGROUND] We currently have this not so great looking modal which has the login components. Over in the sign-in.view, we can begin to add some styling by targeting all of these classes. The main div which surrounds all the modal has this class of modal. We then have the modal content which is contained inside. These can now be used to style our modal and also make it appear over the rest of the content. We jump down to the very bottom, create the style section, which is going to be scoped to this component. As we have seen, the main wrapper which surrounds all of this div has the class of modal. What we're going to do here is we'll target this modal, which is the full wrapper. We'll stretch this wrapper to be the full width and the full height of the page. We can then set a darker background color so we can't see the content behind as much. Then on the next div, which is nested inside, which is the modal content. This will be a little bit smaller. It will also have a white background color to make this stand out over all of the content behind. First, the modal section. The modal will have a fixed position and it will all stand out above all the rest of the content by [inaudible] index to be a high number, such as 999. This will make sure that this full background appears over all of the existing content. To make this modal stand out, we also need to set the width to be 100vw. The full width of the view-port. The height, 100vh, which is 100 percent of the view-port height. The background, this would be an RGBA color. What we'll do for the red, green, and blue is to set this as zero, which is the dark black color. We'll set the opacity to be 0.9. It's slightly transparent. This is the effect we're going for. It's the darker background color which is slightly transparent. We can also position this to be up in the top left corner, so we don't have this gap up at the top. We can also make use of the flexbox to center all of the content. First of all, the top is zero. We'll do the same for the left. Make sure we don't have any gaps around the edge. The display type of flex. Then we'll make sure all the content is aligned vertically and horizontally by setting the justify-content property to be center. Also the same for align items. It should now be all we need. We have this modal now in the center. All of its contents in the center is going to place on a white background so it's more visible. This has a class if we go back up of modal content. Select this, the background can be any color which you want to. I'm going to use white smoke. It also needs a little bit of spacing, padding. We'll set the width of it, we'll set the border-radius, and also will center all of the contents using the flexbox. We'll set the width property to be 80vw. One remember of padding on all sides to give this some spacing inside , a small border-radius. Let's say three pixels. Also, we'll set the display type to be flex. The reason why we are going to be doing this is because we're going to set, if we go back up to the top, we're going to use this flexbox because of particularly this closed modal button. We want this to be over in the right and also the text which is just below. But we'll come back to this in just a moment. But now we need the flex direction to be in a column, which will reset the contents to be vertical. The color and RGB value of 76, 76, 76. You can also see in this text we've inherited this tech shadow. We can override this way to the text-shadow property by setting this to be non. Let's now move down to our buttons. We can select the modal content. Inside of here, we have our two buttons. We could add an individual cluster, each one of these if you wanted to, or instead, we could use CSS to select the modal content and the button which follows, which is the first of type. This is going to select the very first button inside of our modal content, which is our sign-in. We can use this two spaces out with some margin on the right. We'll also make this look a little bit different by setting the background color to be an RGB value of 163, 204, 163. As you can see, this has selected the very first occurrence of this button. We set the background color and also added some space in it to the right-hand side. Next, for the hover state will add a small transform to this. Again, we'll grab the modal content, select all the buttons inside. Only apply this when the mouse is hovered over. Transform. Make this slightly bigger. We can make use of scale. The scale value of one is exactly what it currently is. To make it slightly bigger, we can set this to 1.03, giving us a nice transform on the hover over each of these buttons. Next, let's move up to the top where we have this close modal section. This is the span which we have just here. Remember we set the modal content to make use of the flexbox. Therefore, since closed modal is a child element of this, we can then use certain flex properties. To do this, jump down to the bottom, grab all close modal. The flex property of align-self and the value of flex-end. What this will do, this will only apply to this current element. Over on the left, this will be the flex start. Over on the right is the flex end. We've just been a button and we can set the cursor to be a pointer. This is now done. What's left? We have the text. Just here, we can place this into the center. This one, if we take a look, has the class of modal underscore text, grab this, set align-self to be equal to the center. It now keeps our texts in the very center of the page. Let's just shrink this down. The width for this section is fine for the mobile view. But if we stretch this out to the larger view which we've seen, we probably want this modal to be a little bit narrower. Let's jump into a media query, and we can do this @media and will only apply this when the screen size is 900 pixels or wider. Select the modal content. I will go to, set this to be 40vw. Let's try this out down below 900 pixels. Then over 900 pixels would reduce the size. Almost there now, the last thing which we need to look after is this span with the class of error message. For this, I'm just simply going to add some sample text. We can see this. Set the color to be red and also some space into outside the media query. Grab our error_message. The color of 255, 104, 104. This is the red color and it also just needs a little bit of spacing on the left. Let's add some margin on the left only. This looks pretty good. Now we can just go back up to the error message. I remove the sample content from here. Here we are. This is now our styling finished for the modal. Coming up, we'll take a look at how we can close this modal when we click on this X. 44. Modal Toggle: This modal now looks a lot better. We'll also need a way way open and close it. For this, let's jump over to our composable, which is useAuth.js. Then inside of here, we'll create a new variable which is going to hold the current open state. This will be a Boolean value, which we'll set up to be an initial value of false. This will be called a signInModalOpen, will be wrapped inside a ref and the initial value of false and then below create a new function which is going to toggle this to be true or false. Function toggleModal. All we need to do inside of here is to grab our variable name, which is signInModalOpen, set the value to be equal to the opposite with the exclamation mark, so signInModalOpen.value. If this is false, this will then return this to be true and if this is true, this will then set this to be the opposite, which is false. Return both of these from this file so the signInModalOpen, toggleModal and then jump into a component which is sign in. I need to import both of these from useAuth toggleModal and signInModalOpen. The signInModalOpen constant will control if the user can see this form, so we need to add this to our wrapper inside this div with the class of modal at v if statements, which is equal to this value. Now, if we save this, because the initial value is false, this is now removed. Then back into our template, where we have this span, which is to close the modal. We can listen out for a click, which will trigger our function called toggleModal. As we know what this signInModalOpen constant is set to the initial value of false so to actually see this modal, we need to create a button, which is going to change this to be true. The problem we currently have though, is all of this content inside the modal is hidden by default. Therefore, to actually see this button, we need to create a button outside of this main div with the text of sign-in. This also needs to listen out for a click, which is going to toggleModal. The styling for the class of sign_in_btn. Give this a save and we can now try this out inside the browser. There's our Sign In button. Click on this. This will now toggle this to be true therefore showing the modal. Click on the cross, which will then hide this from view. Just to finish things off, let's move this button over to the top right-hand corner and we can also remove this border. That's the bottom inside of our style section. Let's grab our Sign In button, so sign_in_btn, all separated with underscores. Remove the border with the value of none. Then using the flexbox, we can make use of a line self. We set this to be flex-end. Now push this over to the right-hand side. To make this more visible, we can override the color by inheriting this lighter color from our parent. Finally, we'll also access our Sign In button with a hover state, and give this a small color change. rgb 161,132, 132. Let's see how this looks. There we are. Now our modal will toggle open and closed, which is working really good. The next steps will be to actually log the user in and also sign them out too. 45. Logging In & Out: Our sign-in form is designed to be dual-purpose. We've already used it to sign in new users, but next we'll also use it to login any existing users too and keep things nice and organized. We'll place this login function inside of our use of composable. Then into use off, we have our signUp function so let's also add to this a sign in function. Before we do this, we can also import a method from Firebase. Before we had to createUserWithEmailAndPassword, but now what we need is signInWithEmailAndPassword. Make sure this is all coma case so each word begins with a capital letter after the first one. This will be responsible for signing in any existing users. Let's create a function which we're going to call this inside of. Once the user hits the "Sign-In" button inside of this model. A lot of the code will be similar to the sign-up function we have the try-catch block, we'll then call our particular Firebase method, and we also need to handle any error messages too. What we'll do is we'll copy all of this sign-up section, I'm going to use this as a starting point. Paste this just below. The function, we'll call it login. Instead of create user we'll change this to be sign in. Sign-in with email and password, we'll then clear any error message, which is fine. After signing in, we also want to close down this model, so we'll click "Open". We'll enter our details. If the sign-in process is fine, we'll click on this and then this will be closed down. We can do this by setting the sign-in modal open to be equal to false. Sign-in modal open is something which we already have set up at the very top and this controls the visibility of the modal. Back to our function and down into the catch area. We don't need the first case of email already in use because if we sign in, in the user, the e-mail should already be registered. What we'll do is we'll grab the message of wrong password. As with the sign-in function, these are all messages which are returned back from Firebase. For this one, we'll say incorrect password. Number 2 is user not found. We'll say no user found with that email. I'm going to keep the default message if it is any other errors. Just before we call the try section, we'll place in conditional statements. In fact, we'll place in two and we'll check if there is no email or password. If either one of these is missing, we'll return out of this function and send a message back to the user. If there's no email, we'll return back a statement which says error message. That value. Is it equal to please enter a valid email? Duplicate this. The second one will test if the password is not present. Please enter a valid password. This will just stop an unnecessary method called Firebase if we don't have all information which we need. Now we'll have the function to create new user, to also log in an existing user. Next, we're going to focus on signing out a user. For this, Firebase provides a method called sign-out. Import this. Then we'll also create a new function, which is going to call this. Just a button above our return statements. Create a function called logout. We'll call our sign-up method, which also takes in the off object so we know which user to sign out. In fact, we'll also wrap this in a try-and-catch area so we have some error handling. Places in the try section will catch any errors, and then send an error message to the user with the value equal to error.message. He will access the error object which is passed to the catch area, and then access the message property. Both of these two new functions also need to be returned. The first one of login, then log out. Both of these need to be accessed inside of the sign-in components. Add into that, components, signin.vue. Import these at the top, so login and logout. The login function, we need to place this down inside the form, we'll link this to the login button. We have the sign-up already being called. We have the sign-in just above. We listen off the click with this one, prevents any default behavior, and we'll call login. And then, just like with the sign-up function, we also need to pass the e-mail and the password which we have stored in the user data object up at the very top, which is just here. And this is bound to our to form inputs. So the email, the password, we'll click "Sign-in". Our function will then be called pass in the email and also the password. But what about the log-out function? Where are we going to call this from? Well, currently we don't have any logout button inside the modal or also inside the header. But we do have the sign-in button in the corner. It will be nice to toggle the text here and only say sign-in when the user is actually signed out. And also the opposite, when we need to use it to sign out. So let's do this up at the top of the template, back into the sign-in components, and up to the top of the template, we've got the Sign In button. Let's duplicate this. I click, say log out, change the text to be signed out. We'll come back to these buttons very soon and only show one of them at a time. But now we can see our sign-in and sign-out buttons at the top. But just before we go ahead and test this, how can we tell if we even logged in in the first place? Well, one way is to get the user object returned back to us after we call the login function. If we go back over to our use of composable, the way we can access this inside of our login function is by storing the return value from the sign-in with email and password method. We saw this is inside of a constant called user, and then just below, we can give this a test with a console log. Place in the user, onto the browser and open up the developer tools. Jump into the console, sign-in, enter the e-mail which you signed up with. This time we'll hit Sign-in. We see an error. Sorry, that was an unexpected error, but we do seem to be getting back the user credentials. Let's take a look. We have the user, we have the email, the access token. This all seems to be fine, but we're still getting an error message inside of here. So let's take a look. This is the message inside the switch statements. Let's do a little bit of debugging inside of the default section place in a console log. And here we'll output the full error object, and we'll see if we can get a better idea of what is going on. Sign in again. Sign in. So we get the error, we have the user credentials and there's our error. Assignments are constant inside the use of so sign in modal open. That's the problem. This needs to be.value. Let's try this once more. We do the refresh to make sure that any errors are cleared. Sign-in, great, the message has all cleared. We still have our user object and the modal is closed down since we've set this to be equal to false. Let's remove the console logs. We don't need either of these. Returning back this user object works completely fine and this is one way of detecting if the user is logged in or not. We can also access the user properties such as the email and ID. But this is just one way of doing things. Another possibly better way of doing things is to make use of a Firebase-provided method, which we're going to take a look at in the next video, and this will listen out for any changes to our signed-in user. 46. Detecting Auth Changes: As mentioned in the previous video, when we call this method which is sign-in with email and password, this will return back a value. If successful this value will be the contents of the user object, which contains all the information we need, such as the user ID and also the user email. This is fine, but Firebase also provides an observer, which is a recommended way to not only get the user, but to also notify us if there is any changes such as a user signing out. This observer is going to be imported into our use of file so jump up to the top and inside of the off import, we'll also import on/off state changed. To call this we'll jump down to the bottom of our file, just above the return statement and we'll call this at the bottom. In this painting method, we also need to place in the brackets. We also need to pass in two parameters. First is the occurrence of instance, we have access to this. We'd have our off constants separated by comma. The next parameter is going to be a function to call if a change in the users of State has been detected i.e., this function will be called if the user logs in or logs out. Passing a function as the second parameter and its function is also going to take in the current user. Open this up. Now we'll have access to the current logged in user, we want to know our place in a conditional statements. We can check if the user exists. If it does, we'll then set the user. If it doesn't exist, we'll then set the user to be equal to null and where we're going to set this, well, let's create a ref up at the very top of our file. Just below the rest we say const. User data is equal to a ref. By default, we'll set this to be equal to null. Back down to our observer, we can place in a conditional statements and check if the user exists. This is a user which will be passed to our function. If they are logged in, we'll set the user data value to be equal to this user. Else the user is not logged in so therefore, we can return the user data to be equal to null. Let's then return this user data back from our composable. This is going to be used in various components. One of them can be the sign-in components to make sure we only show the relevant button at the top. Jump into sign-in, import this at the top and then go down to our first two buttons we see here. We only want to show the sign-in button if there is no user data presence. Passing VF will say if there's no user data. If we don't have a user we'll offer the chance to sign in, if not passing VLs where the user is currently logged in and they have the option to sign out. Now this will give us a chance to properly test this. But just before we actually go ahead and test this, we now have a clash without two variable names. I'm just going to change this one to be form data. Let's copy this and we use this down inside of our form. Form data dot email, the same for the password and also changes in our login and sign up functions. I think this is everywhere. Let's just check such a user data and we only have this in our two locations. Good, we can now go ahead and test this by putting the user at the top of the template inside the curly braces we say user data, the e-mail but remember sometimes we won't have access to this email because the user data will be equal to null. Facing the question mark to make this fail silently if we have any errors. If you are unfamiliar with this question mark, this is JavaScript optional chaining. This will stop an error appearing if no email is present, which will be the case if the user is logged out. Let's say this go over to the browser. We see we're currently logged in, the e-mail is about the very top click Sign out. This n is removed so let's try signing in one more time. There's our email now returned. So to remove this output from the template and everything now appears to be working correctly. This is now the end of the authentication section. Congratulations on reaching this stage. We're not done yet though we have plenty more features to add to our project, which will add to in the upcoming sections. 47. Styling The About View: Coming up we have some finishing touches to our project. We're going to begin with some CSS styling for this about view. At the minute, it doesn't look too great below the header. Let's go over to this section, which is in the views, about view, select the above view, and then we can create a style section at the bottom. Let's take a look at what classes we have. That's all we have this wrapper which wraps all of our content with the class of about. Then have the intro section, and this section includes our title and our image. It's basically everything above this links area. Let's start with this class of about. We'll start with setting the display type to be flex, and we'll make sure that this is in a vertical direction by setting the flex direction to be in a column. Using the flexbox and this vertical direction will allow us then to align the items into the center. Align items into the center, which will then push the contents into the middle of the page. This is the full page wrapper. Next we have the intro section. The intro section, as we've just discovered, is everything down to this image, which is image and the two lines of text above, so we'll also set the flex direction to be column on this one. The display, or flex direction of column. Currently, you won't see any difference if we go over to the browser. This is because we're going to make use of a media query very soon, and make this appear in a row on the larger screen. Currently, this will apply to the small screen, and then on the larger screen view, we'll set this up to be a flex direction of row. Places inside of the art media rule where we'll target the minimum width to be 900 pixels, and then we can override our intro section by setting the flex direction to be a row. These appear in a row. Then below 900 pixels, this will revert back to the column direction. Let's now jump back into the intro section. If we scroll up to the full section inside here, we have this div with the class of info text wrapper alongside the image. Effectively, we just have two sections we need to work with. We can again make use of the flexbox to set both the left and the right right to be the same size. We also need to add some spacing and alignment, and we'll do this just outside of our media query. First, the image was info underscore image will also grab our info text wrapper, info IMG, and also info text wrapper. Since the parents of intro has the display type of flex, the child elements can make use of flex, and we'll set both of them to try and take up the exact same value. Some padding to give both of these at some spacing of one RAM. Align the text with text-align into the center, and then to set the vertical alignment. We can also make use of align self. Align self into the center. Again, this works because the parents of intro has these display type of flex, and align self will work because this is a child item of the flexbox. This takes care of this section. Let's scroll back up. The next one is the more info wrapper. This is the section for all of our links and this title just here. Let's target this section again outside the media query. More info wrapper in display. Again, we'll make use of the flexbox. We'll set the flex direction to be in a column text line into the center. We can make this stand out by adding some background color. Make this a little bit different from the current white color. Let's go for double F, double E, D2. Currently, this background will only surround the content, so to make this the full width for the page, we can change the width to be 100 VW. There we go. To give this some spacing, just add a little bit of padding of one RAM. We can also change the background of this section too much, so let's copy this. If we go back over to the source, the assets, the main.css. In fact, we can just remove the background for the info block and this will now match the rest of the content. Let's try over sections and also the homepage, and make sure you will have no issues. Infact here, we do need to add the background. Paste this in. There we go. This now works for both of our views. 48. Completing The Admin View: Let's continue with our finishing touches inside of the admin section. Inside here, we're going to make use of the flex-box and place in new pizza on the menu components side-by-side on the larger view. Let's go into the admin view, which is in the views folder. At the moment we don't have much content, we just display the level three heading and all three components, so that's the section of our new pizza and also the pizzas component. Cut this out, wrap these inside of a div, paste it back in, and give this div the class of row. At the bottom, create the style section. We'll also scope this to our components. Select the row. In the initial section here, we'll set the display type to be flex, a new flex-direction to be column. This will work on the small screen view. I'd shrink this down and we can see these in the column direction. Then set up a media query with the art media rule where we'll apply all of the styles inside here over 900 pixels. Select our row, or you can override the flex-direction to be a row. The smaller screens will have a column. Stretch is to be over 900 pixels. These are now side-by-side. We also need both of these components to be a little bit wider, so they span the full size of the page. We can go into the new pizza and also pizzas inside the components under Admin. This one has the class of admin section. If we go to pizzas, we can see this one does too. Let's go into the assets folder, into main.css. Do a search and we already have the admin section reference just here. Set the flex value to be equal to one. All of these sections will try to take up the same available space. With this being an admin area, we also want to introduce some measures to make sure only the correct people can view this page. For now, we'll make sure they are logged in by first importing the user. Back to the admin view, into the script. We need to import our use of composable where we can extract and store inside of a constant our user data. Remember, the user data is either equal to our object which contains all the user information or it's equal to null. We use Auth to call our function. Therefore, user data can be used with some conditional rendering to show and hide the page content. Let's grab everything inside of our template, place in a div as a wrapper, add the contents back inside, then make use of conditional rendering with v-if, where we can check if the user data is present. If we're logged in, we should be able to see all of the admin section. If not, create a p element, which will be for the v-else section, with the message of you must be admin to view this area. There's our message. Before we log in, we'll just add some styling to this section by adding a class of warning_msg. We'll style this in just a second. But before we do that, we can also add a welcome message if the user is logged in. Just above the level 3 heading, add some text with the class of user_email. The texts are, welcome. Then inside the double curly braces, we can access our user data.email. Remember that the user data is not always going to be an object, it can sometimes be null. Therefore, we can make use of JavaScript optional chaining with a question mark to only display this email if it's set. Finally, the styling for our user email and the warning message go outside of the media query, so user_email and also the warning_msg. Margin of one rem it just pushes this outside of the corner. Let's try signing in. Everybody, the content is now visible and also we can see a welcome message at the top. Also just to push over the level 3 heading, we can also apply the same amount of margin. Add the H3. Good, this looks a lot better now. Let's go over to our sign-up button and click on this. This now takes effect and removes our admin section from view. 49. Restricting New Pizzas: Another check we can make is to see if our user is logged in before they can add a new pizza to the database. Currently, we don't have any way of checking if the user is a regular user or an admin. But we'll come back to this one later, and why we'll do this is once again to import our user data into our new pizza components. Components, Admin, New pizza. Place an import at the top from our composable. Import, useAuth from our composable file. Then we can extract the user data. If we scroll down, we can see we have this Add functions at our new pizza to the database. Before we add this, we can check to see if the user is logged in. We'll say if there is no user data, and since we're inside the script, we also need to access the value. If this is false, i.e, this will be equal to null if the user is not logged in. We can now return out of this function before adding our pizza to the database. Under the current circumstances, it is going to be pretty difficult to test this. Because if we were signed out, we currently can't see the Add new pizza component to even give this a try. What we can do as a temporary measure is to disable the conditional rendering inside the admin. Jump over to the admin view. Then inside the templates we'll comment out the if statements, the closing div, and also we'll need to do the v-else area. This now gives us access to the admin section when we are currently not logged in. Let's add, and now every time we try to add a new pizza, we don't see the message to say this has been added, and we also don't see this added over in the menu section. Good. With this all working, we can now reinstate the conditional rendering inside the admin view. We're not logged in, so all the content should disappear. Next, we'll stick with this subject of the user object by adding it to our orders. 50. Adding The User To Orders: The upcoming steps are all related to all orders. What we're going to do is we'll make sure that the user object is placed on our order before pushing to the database. We'll also make sure that the user is logged in before creating a new order, and to do this we'll jump into the use basket composable and in part of a user. Composables, useBaskets, and this file contains the addNewOrder function. But before we can actually test if the user is logged in, we also need to import the user data from our off composable. So any composable can be imported inside of another composable, and this makes these files really flexible. Just like we do in any of the components, we can import our composable. This is useAuth, and this is./, useAuthf since this is in the same directory and then into our function, we can extract our user data from our useAuth function. The ID here is we'll check if there is a logged in user. If there is, we will add them to the order, if not, we'll add some buck a user message to login. And we need to store this message inside of a variable or a constant. So const, signInMessage is equal to a ref with an empty string as the initial value down to the addNewOrder function and we can first check if the user is logged in. Inside the try section, we'll say if, userData.value, open up the curly braces. If this is true, we want to run some code inside of here, if it's not, we'll create an L section. The L section will update the signInMesage, so this will return back to the user a message of please sign in to place an order. Then, in our if section, we can copy or cut out the order section all the way down to our basket text without the original content. Then place this inside of the if section, so this will only run if the user is logged in. This is all we need to do for now inside the function, we can then return back our signInMesage, which can then be imported inside of our menu view. The top, we've already got access to use basket so we can just enter the end our was sign-in message. Then, render this message inside of our template, so scroll down to the order button, which is in the basket section, look for this button with the text of place button, and then just above this, add a new P element, outputs, our signInMesage. We can now test this when logged in and logged out. Since we've been asked to sign in, we are currently not logged in. We can confirm this with the admin. Let's try to place an order, click on some items. There we go. Please sign in to place an order, click on our model and we can login. Sign in, click "Place order" once more and this is now all worked. If worked, however, log out and clear the page and then add any new pizzas to order. Again, if we tried to place an order, we'll still see our message just here. You may have noticed from before that as soon as we login via the modal, this message will still stay intact. Let's just demonstrate this. If we sign in once more, click "Sign in" keeping this message intact can cause some confusion to the user. They may think that they still need to login. All this means some error with the login process. To clear this what we can do is, view provides a watch method which will trigger a callback function If there is any change to a reactive state such as our user. What this means in practice is if we go back to our use basket composable up at the top, we can then watch our user data and as soon as any changes will happen, we can then run a callback function. The watcher has automatically been imported from the view package. If yours didn't happen automatically, be sure to add this in. Then each time a change happens to other user data, the function will run where we can clear our signInMessage.value by setting this equal to an empty string. Let's go back over to our menu and refresh. We can see this all taken effect at any new pizzas. Place an order. This is fine because we're logged in, sign out, place an order when logged out, and we see our error message. Now, if we try to log in once more, sign in. As soon as this happens, we then remove the error message so the user is now free to place their order. Good, this now leaves us with the final task of adding the user to the order. Let's go back over to the use basket composable and then down to the addNewOrder function. Just inside the if section, remember that the user data, which we get back from firebase, contains lots of information and many of the properties which we don't need. For simplicity, I'm going to create a new user object and only placing the user's ID and email, the ID property, we can grab from userData.value.uid and user email. This one is from the userData.value.email. Then below, when we create our order, we can also pass in the user. Let's now save this. Let's try to place a new order. Place our order, go into the console, into our Firestore database and check out the orders. If we scroll down, we can see that the latest order which we just placed also has our user object attached. 51. Filters & Global Properties: You notice if we go into the menu section, begin to add some pizzas to our basket. If we start to increase the quantity, lets increase this up to six and see we have some inconsistent formatting. The first one is down to one decimal place, the second one is two decimal places and the order total is all messed up. If you've used Vue version 2 in the past you may have used something called a filter. A filter was available to provide some simple text formatting and were ideal for creating things like currency filters. However though in Vue version 3, filters are no longer available. They can be replaced with something like a method or a computed property inside of a component. However though, if you want this to be available globally so we can reuse it we can also make use of a filter like setup in Vue 3 by accessing something called global properties. Global properties is an object which we can add to, which is available in all of our components for us to reuse. Do be careful though not to overdo things and make everything globally accessible. But a currency filter which we can use multiple components is a good use case. Let's go over to the main.js and set this up. So just underneath where we create our application, way to do this is to call app.config.global.Properties and then we can give this property name such as dark.Mode and we'll set this equal to true. Now we can access inside of any of our templates is darkMode property so it acts like a variable. Let's go over to the menu view and anywhere inside this template is fine. We output this inside of the double curly braces, now we see this up at the top. This is a really good use case for simple strings or Booleans just like this and we can access these in any components. We could also set up as many of these as we wanted to, we could just duplicate this and we could create different variables. Or we could also add multiple properties inside of an object. So let's keep this section but remove the variable, and what we'll do is we'll create an object called filters and then inside the curly braces we can add as many filters as we wanted to. For our use case we'll create a function called formatMoney. FormatMoney needs to take in the value which you want to format and then we can return the new value which is formatted. A simple way to do this is to call value.toFixed and this will fix this to two decimal places. Also as mentioned, since this is an object we can also add multiple properties inside of here. But although formatMoney is all we need so let's go back over to our menu view and we can replace the pricing with our filter. If you wanted to you could add this to the menu section but currently our numbers are already added into the database so this should always display fine. The problem occurs when we add multiple pizzas and start to multiply numbers. What we can do is go down to the basket section, we have the order total, we have this price just here, so instead, what we want to do is to cut out this multiplication , access our filters.formatMoney. FormatMoney was a function which takes in a value to format so we need to also pass this in which is the multiplication which we just cut out. These are the line totals and we also need to do the order total. So cut this out then we'll access filters.forfmatMoney, paste this back in. Let's try this out. Up the quantity and you can see when we get past six which was the problem before, these all correctly formatted to two decimal places. Since our filter is a global object we can also use in other components too where we display money values. Let's jump into the components, into the admin and into the orders.view. Jump down to our table, just like in the basket we're also multiplying the price by the quantity, so we can do the same here we can say filters.formatMoney. Pass this back in, let's try this out over in the admin section. This also looks fine if we look at all the totals over on the right. This is a really simple but useful way to repeat simple tasks throughout our app. Is currency formatting is a little basic though and we can improve on this by using the JavaScript Intl object. This is the JavaScript internationalization API that provides language-based number, time, and date formatting amongst other features. Let's improve on this by going back over to our main.js. We can replace our current example just after the return statement and we'll declare we want a new Intl.NumberFormat. NumberFormat is one of the many methods which are available on the Intl objects and this NumberFormat allows language sensitive number formatting which is great for currency. Next, within chain onto the end the format method which takes in the value which you want to format. In our case, we're still making use of this value which is passed to the function and as well as this the number format method will also take in some arguments to specify what locale we are using and also some options. First, the locale of the end-user which sets the formatting style of the currency. The reason why we want to do this is because currency is displayed and formatted differently in different parts of the world. For example, some countries use decimals or commas as separators. Let's say 10,000 could look like this or this and also some countries use special symbols too. If we currently save this and go over to the browser, let's refresh. We can see without adding any different options inside of here by default my currency is to two decimal places and uses the decimal point in-between. But if we added for example, a German locale which is a string value of de dash over case DE, as soon as you save this, this is updated and separated with a comma instead. My use case I'm going to change this to be English, dash GB, which should revert this back to the decimal place. Then separated by a comma we can also add an options object. Inside this object we can pass in various things such as the style and the currency options. The style is going to be equal to the string of currency and then we also need to pass in the currency which you want to format. This can be any currency such as Euros, EUR, we can see this just here, also remove this $ symbol in just a moment, it can be $, I think USD. See US just here, but mine is going to be in pounds. As well as currency this can also be used for other things too. We can include units of measure and also percentages, but these options are great for our use case and also feel free to set them up with your preferred currency. Just to finish things off let's go back over to the orders dot view. We're going to move the currency symbol which we have hard-coded in place by the menu, we also need to do the same here and also here. The menu view, remove this. Almost there now we just have the menu section which is just above. This is the option no price, we can move the currency symbol put this out, access our filters, we've got formatMoney , paste this back in. Now our number formatting has now taken effect. 52. Realtime Pizza Updates: When we update our site, some things updates immediately and some things do not. For example, if we go over to our Admin and try to add a new pizza, just all the dummy information for now. Click "Add". Then this new pizza is not automatically updated in the menu until we hit "Refresh". But if we delete a pizza by clicking on this X, the item is removed from the Admin. Why do you think this happens? Well, to see this, we need to take a look at some files in more detail. First, if we jump into the admin section of the components and then open up NewPizza.vue, inside of here we have this function to add a new pizza to our database. All we do here is we provide some checks and then we add our document to our database, we update the message. Then if this is successful, all is good. The pizza is stored in the database, but our app is not notified from Firebase of any additions. However, though, if we go over to what we use pizzas composable and take a look for our getPizzas function. Currently what we need to do after we've added our pizza is to refresh the page because this will once again call our getPizzas function, therefore updating the menu on the right. This one, it seems to make sense, but remember when we deleted a pizza, the application was updated with the changes. The difference between these two situations is if we go into our usePizzas composable and take a look for deletePizza, the key difference here is as soon as we've deleted the pizza, we then once again call our getPizzas function which will then pass the correct information to our menu components. If we wanted to, when we added a new pizza, we could also again call our getPizzas function. But there is also a better way of doing things and this is to use a real-time functionality of Firebase. What this means is as soon as we make any changes to our database, the app is notified of these changes. For example, when we go over to the Admin and add a new pizza, we click on the Add button and this is then pushed to our database. As soon as this has been successful, Firebase will then let our application know by person the updated information. Then our menu will update automatically. We do this with a method called on snapshot. Let's go over to all usePizza composable. We can import this from our firestore, so onSnapshot, I'll do this inside of getPizzas. What we'll do now is we'll comment out all of the existing code which we use to grab our pizzas from the database and we'll replace it with the onSnapshot method. Passing on snapshots and onSnapshot also takes in the pizza collection reference which is stored in dbPizzasRef. This is followed by a function which we're going to run each time there is a change inside the database. The function is going to take in the docs which is returned back to us. Since we have a matching name of docs just here with the docs which we're looping over from before, we can now uncomment out this section, put this out, and then paste this inside of our function. This is doing exactly the same action as before. We're getting back the documents. We're looping over each, we're creating a new pizza objects. Then underneath for allPizzas ref. As soon as this updates, it will then update all of the views or components which relies on this data. So let's give this a try over in the admin. So we'll jump into Add new pizza, click on "Add", and as soon as we do this, our application is updated, but we do see a small issue. Not only do we get the free pizzas which are returned back from the database, but these are also added to the existing two which we already have. A simple way of doing this is to clear it out our allPizzas array before we get the information back. If we go into the try section, we can see we have already clean out the existing value and certainly it's been empty array. All we need to do is to move this inside of our snapshot. Let's save this and refresh as our free existing pizzas. Let's add one more. Click "Add" and now our app is now notified of any changes. With this in mind, we can also go back over to our deletePizza function where we could remove the manual, getPizzas call. I'll take a look for our deletePizza function. We no longer need to manually call getPizzas, this is done for us. Now if we also delete pizza and our admin is now still updated when we remove a pizza and coming up next will also repeat this process for our current orders. 53. Realtime Order Updates: To make the orders update in real time, follow the same pattern as we did with the pizzas. First, we go over to use orders.js inside the Composable. At the very top, we need to import our own snapshot method. Then we can replace the existing get docs method we'd on snapshot. We'll go down to getorders, which is just here. We still need this queryData, which is at the top. But we'll comment out everything afterwards from get docs down to the bottom of our try section. We'll call onSnapshot, which is going to take in our queryData. Then a function to run after each change. Again, to keep things consistent and to reuse our data, we'll call the available data inside the docs. Then our existing code, we can uncomment out, put this out of place, place this inside of our function. We no longer need these docs. We can cut these out since we're getting these automatically passed to our function. Then to avoid the same problem we had with our pizzas, if you remember back in the use pizzas file, we scroll down to overuse pizza's function. Inside of here, we had to reset the value of all pizzas to be an empty array, since we had the existing pizzas. Then after each update, the complete set of pizzas were then added to our existing values, rather than replacing. Do the same over in use orders, jump into the onSnapshot where we can access allorders.value. This equal to an empty array. To test this, we need to open up our project in two separate tabs. We'll place these alongside each other. The reason we do this is because our orders are placed inside the menu components, but we view these inside the admin components. If we were to just simply add a new order, then click on the "Admin", this would then automatically pull in the current orders. We wouldn't see the real-time functionality, but we can see this if we go into the menu, create a new order. We have five orders, click on "Place". You can see straight away that this jumps up to six new orders. Let's try one more. This jumps up to seven, and our latest order is now down at the bottom. 54. Unsubscribing From Updates: When using onSnapshot, the process of listening for our updates is continuous. However, though, we may no longer need any updates. We do need a way to detach this listener to free up all the resources. Doing this is relatively straightforward. When we use on onSnapshot, it will turn back an unsubscribe method, which we can then call inside of our app. One use case for this could be inside of the admin, for example if we go down to the bottom, we are currently listening out for any of our current orders. But as soon as we leave this page and go and visit a new one, we're no longer need to listen out for any new orders. We can detach this listener and free up any browser resources. To do this, go to use orders.js file, where we can store the unsubscribe function into a variable. As mentioned onsnapshot will return back an unsubscribe function, which we can store inside of a variable or constant. Currently the problem is this unsubscribed constant is scoped inside of this function. We need a way to access this outside. One way of doing this is to create a new ref just outside of here. We'll say const, unsubscribe from orders. By default this will be an empty ref. We can update this with the value of unsubscribe. Let's jump into get orders. At the very bottom of the tray section, just above the catch block, we can grab our ref, which is unsubscribed from all others and set the value to be equal to unsubscribe. Now we have a unsubscribed from oldest function, which we can call when we no longer need to listen out for any orders. We can do this inside of a lifecycle hook called on unmounted. We need to import this from the vue library. We already have onMounted, which is run as soon as the component is mounted to the DOM and then onUnmounted as soon as we leave. We'll place this in onUnmounted. This is going to run a function. We can test this out with a console log just allows knowledge to work in and we'll say unmounted. Then access our ref, which is unsubscribed from orders dot value. We'll call this as a function. Save over to the admin and we'll open up the developer tools into the console. Then if we unmount this component by moving to a different page, we see our console log. This should also call out our unsubscribe function. 55. Adding New Users: Our application currently has the ability to sign up new users. But any new user which signs up can access our full site. This includes any one of our pages, and more importantly, the admin section. We need a way to set up our users to only allow authorized admin users into this section. Firebase does have a solution to control users' access using custom claims. If you're using a production app, this is a great way to go and you can find out more information with this link just here. Custom claims do need to set up on the server side for security. We can set these up using Firebase Cloud Functions. But unfortunately, Cloud functions require an upgrade to a paid plan. For this reason, we'll be using a different approach. This will involve setting up inside of our database a users collection. Each one of these documents will be linked to a user inside of our authentication section, we'll use this user ID as the document's ID, just like we have here with our orders. But this also means we can create a new user record which will contain some additional information. One of these pieces of information will be the user's role. In the past one, we've created our pizzas and our orders in the firebase.js file. We've created reference to each one of these collections. Then we've used these to create our new documents. For example, use baskets and we created a new order with the addNewOrder function. We've used the addDoc method. This is pushed to this reference, a new order. But the key part to notice here when using addDoc is we don't push a new ID for the documents. Each one of these documents has a uniquely generated ID. But instead, we want our user's collection to have the document ID which matches our user inside of the authentication section, which is this one just here. Instead of using the addDoc method, we're going to use something called setDoc. This will allow us to push some new data to a collection also set our own ID which we want to use. To do this inside of our firebase.js file, we export our database, which we can now use in the use of.js file. Jump into this composable. Now we can import the items which we need. The first one, we need to import the Doc from Firebase. Also, the method which we just talked about which was setDoc. This is available from firebase/firestore. We also need to import this reference to our database. Import DB from our custom Firebase file. This will be the method which we use to create our new documents. But we also need a way to access the current ID of the signed in user. Let's go down to the sign-up function, which is just here. When we create a new user with this method just here, it returns back an object. We can destructure the user property to access the data which we need. We can then use this data just below to construct a new user object. Const, useObject. We can add some additional information to our user, which will be stored inside the database. First, a property called createdAt, which is equal to a new JavaScript data. We don't actually need this date for our project, but it's always useful to add this in case we need this in the future. We can also store the user's email, which we can access our userobject.email. Then the key part is a new property called isAdmin. To begin, we'll set all new users to have the isAdmin property equal to false. Also still within this try section as soon as the user has signed up and this has been successful, we'll also close the modal by setting the sign-in modal open value to be false. Processing. Access the value and set this equal to false. Just below our user object, we can then create a new constant called newDoc. This will be the new document which we're going to push the Firebase using the doc method. Doc method needs to take it in a reference to our database, which is one which we just imported. Then separated by comma, the collection name of users. The third value is going to be the user ID which you want to use when creating these documents. This is accessible from our user. Access the UID property. Then to push this to Firebase, we can await setDoc. The documents, this is a reference which we created just here. Pass this in as the first value. Then the second value is the data for our record, which is stored inside of the user object. Just to clarify what we're doing here, when a new user signs up, this new user will be first created inside of this authentication section. Then we're going to construct a new user object using this ID. Install this additional information inside the database. But just before we test this, since we already have some users signed up in the authentication section, which is not linked to our database, we first need to clear out these users. Let's leave this account. Also this one. Clear out any existing users. This should not be everything which we need. We have the new user object. We have our documents was set in the doc. We should have all of our imports database. We just also need to make sure this is exploited too. Let's go back over to our project. Create a new user , click Sign up. Over to Firebase and the authentication section, we have our new user. Notice user ID, which begins with 08. Yours will be different, of course. Jump into the database as the user's collection, which begins with the same document reference. This is going to be useful because when we first authenticate our user, we can have access to the user ID. Then we can also access the isAdmin property. To get started, what we're going to do is we're going to click on the isAdmin field. Changes equal to be true. We have at least one admin user. Then we'll create a second user with the isAdmin value set to be false. Sign back in, create a new account. Sign this one up, there's our second account with isAdmin set to be false. Now we have two users with different access levels. We'll use this in our app to only allow admin users into our admin section. 56. Retrieving Admin Users: The first step to only allow an admin access inside of our app is to retrieve the current user from our database and check if there isAdmin property is set to true. Since we will be retrieving users, we can create a reference to the database collection inside of our Firebase file. Just like we did with pizzas and orders, we'll create dbUsersRef, which will point to our users collection. Say this, and then into the useAuth.js file. This tip our import, and we'll make use of this very soon. Then inside of our function, we'll create a new ref called userIsAdmin, which will be initially set to the value of false. This default value of false will be updated after we create our function. This function is going to grab our user data, will grab the logged in user's ID, retrieve the record from our database, and check if the isAdmin property is set to true. Let's create a function, this will be async, called checkAdminRole. First create an is statement where we can check if our user data, which is this one just here,.value, we can check if this has the uid. We'll also add the question mark because sometimes this value will be null. Then inside we can retrieve the current user details from the database. We can do this by creating a document with the doc function which we want to access. First, we'll access the dbUsersRef, which is our collection, then we can access a particular record by the ID. This ID is retrievable from our userData.value.uid. Copy, paste this in. It is inside of a constant called docRef. Then to actually call for this information, we can await a function called getDoc. We'll pass in our document reference this inside of a constant called user. Then we need to form two checks inside of an is statement. First of all, access our user.exists, this exists method is available on the user, and check if a document is found, and then using the double ampersand, we'll also perform a second check, and this is to check if the isAdmin field is set to be true. We can also do this on the user object, which has a method called data, which will give us access to any one of these properties, so data.isAdmin. If both of these conditions are true, we can then update our userIsAdmin.value to be equal to true. If not, inside of an else statement, we'll set the userIsAdmin to be equal to false, so paste this in. Set this to false. We'll also need to import the getDoc method from Firestore. Our userIsAdmin value will be used in other files and components to check if access is allowed. We don't need to return this down at the bottom. Remember though all we've done here is create a new function. We haven't yet called it to run the code inside. The place to run this code is going to be inside of the authObserver. Scroll down to onAuthStateChanged and the code inside of here will run each time a change of user has been detected. If I have access to a user, if somebody is logged in, we'll then check the admin role. However though, if the user logs out, we want to update userIsAdmin.value to be equal to false. With this now all in place, let's go over to our admin view. We can access userIsAdmin, and remember, this current check for our admin section is only checking if the user is logged in. It's not actually checking if they have access to this page. We can improve on this by switching this out to be userIsAdmin. Let's try this out. Into the admin section, let's sign in. First of all, this is the email which is entered, which is not an admin. Add the email and password, sign in. Good. This is not giving us access to the admin section. Sign out and log in with the admin user. Sign in and as soon as this happens, we check the admin role, which is currently true. This will then update our variable value of userIsAdmin, which gives us access. We can click Sign out. As soon as this happens, the AuthStateChanged function will run, set our userIsAdmin to be equal to false, hiding our admin section from view. 57. Updating Regular Users To Admin Part 1: When we have this project finished, we hand it over to the customer. The last thing we want to do is to make the customer go into our database and update this admin property each time you add a new user or to remove the admin access if an employee leaves the company. What we're going to do to improve on this is to create a new section in the admin, which will allow an existing admin user to update this. This process will involve creating a new admin component, where the admin user can enter an email address of the user which we want to update. Since this will be also placed in this admin section, it also means only admin users can perform this update. Once we search for the user by email, it will then display a message to say if the user is currently an admin or not. Finally, we'll have a button which will then toggle this admin role to be true or false. First, let's create our new component in the components directory and into the admin, new file. We'll name this ToggleAdmin.vue. Then we'll create our basic structure. Our basic structure is going to be similar to the other admin components. We can go into any one of these other admin sections and down to the template. Let's copy this template sections, give us the same structure. Paste this into our Toggle.Admin. Let's simplify this. We can move the form, so crop everything from the closing form to the opening form tag. We'll keep these same classes for styling, but just the level 3 heading to be Toggle Admin Privileges. We also need to update this variable name. We haven't yet created this, but this one will be called showToggleAdmin. Make this camel case. We'll also use this in the other locations and create this up in the script section subscript. This will also use the script setup. Need to import ref from view and create our constant which is showToggleAdmin. Wrap this in a ref with a default value of true. This means, by default, we'll be able to see the section inside of the admin, and then we can click on the Hide button when we don't need to see this section. Over to the views and into the admin view to import this. Let's duplicate this one. This is ToggleAdmin. This is also ToggleAdmin. Place this just below our orders. Go to the Admin page, down to the bottom where we can see other components. Back over to this ToggleAdmin.vue. What we're going to do is create two new sections. First will be a form where we can find a user by entering the user's email. We need a constant to hold this email. This could be ref, so const email, and the initial value will be a string. We only want to show this content which we're going to now place if the showToggleAdmin is equal to true. Below the header, create a new div. We can make use of conditional rendering with the if. We'll only display this section if showToggleAdmin is equal to true. Place now form. This form will be pretty simple. We'll create a div, the class of form_group. Then inside here, we'll add a label and also an email input so the user can search by email. First, the label or email with a text to find user by email. The input. This will have the type of email ID. Also email a placeholder text value of enter email. Then we can use V-model, which is going to bind the value to our email up at the top. Next, just below this div section, we'll also place in a button which is going to submit this form. The type of button, this will then trigger a function to find our user. We've not yet created this function, but we'll add it in for now. [inaudible] click will prevent the default behavior and this will trigger off function, which will soon create the call, findUser. FindUser also needs to take in this email address which we have stored up here. Input a text of find user. Then all the functionality for this will be over and I will use off composable. At the top, the first thing we'll do is create two more constants. The first one is going to be for a message. We'll call this the toggleAdminMessage, which will make use of ref and an empty string to begin with, and a second constant called selectedUser. This will be a container to hold a user which we search for. We'll set this equal to ref and the initial value of null. This toggleAdminMessage will hold the value, which we'll say if the user is an admin or not, or if they cannot be found with the email provided. Let's create our function, so async called findUser. Let's use past the email of the user we want to search for. Placing a try section and a catch as an error. Then we can get to work inside of the try section. The first thing we'll do inside of here is to clear any messages which we may previously have. This is stored in toggleAdminMessage.value. Set this to an empty string. Then we'll also create a conditional statement and this will check if the user is admin. Remember we currently have this property set here. If this is equal to false, then the user is not allowed to update the admin property, so we'll return out of this function. Remember those things we actually call on this from the admin section. This section should already be protected anyway, but this is just an additional check just in case. Next, we'll use the Firebase query method. This will allow us to search for a particular record if a single person is the user's collection which is stored in dbUsersRef. Then we'll filter this down using the where method. We want to check the email property and check if this is equal to a particular email address. This email address we want to check against is the one which is passed to all function on this end, and this will check all of our users against the email property and check if it's the one which is passed in from our form. What is inside of a constant called query data. Then we can make a request to this from Firebase by waiting getDocs. Parsing the query data and then store this inside of a constant called user. This user will be the returned value from Firebase. Using this user object, we can then construct a new user object containing only the properties which we need. Remember when we've looked at these objects, which we get back from Firebase in the past, we get back lots of information, and most of it, we don't need. We create a new user object, place it in the ID, which we can grab from our user, the docs, and getDocs is going to return multiple documents, but we should only ever have one user with a unique email. For this reason, we can select the first document with the index number 0. Select the ID and then let's duplicate this twice. The second property is the email. Change both of these. The third property which we need will be isAdmin. Even as a simpler user object which contains only the properties which we need. We can then set our selected user to be equal to this object. Remember.value. This is the case if this is all successful. If not, we can update the selectedUser.value to be equal to null. Then also update our toggleAdminMessage value to be equal to a string. You can place in any message which you want to inside of here, but I'm going to say no user found with that email. We also need to import these methods from Firebase, which we'll just use. We have the query, we have where, we have getDocs. In fact, just before we do this, we need to also call the data method before the email. The same before isAdmin. This function will extract the properties we need from each document, so onto these imports up at the top from our Firestore, we have getDocs, the query, and also where. Then finally, down at the bottom in the return statement, we can return our function which was findUser, the selectedUser value and also the message which was toggleAdminMessage. There we are. Save this file and we just completed. Next, we'll import these and use these over in the toggleAdminComponents. 58. Updating Regular Users To Admin Part 2: Previously we created a function to find a user by their email, and then we store this value into this selected user constant. We'll now head into the ToggleAdmin.vue component to import these at the very top. We'll import useAuth, and this is from the file path of @/composable/useAuth. Then we'll extract and store these return values inside of a constant. So we need find, we need the selectedUser, and also toggleAdminMessage is equal to useAuth, call this function, then scroll down. Just under this form section, we'll create a second section which will display the user if one has been found. It will also have a message to show if a user is currently an admin or not, then a button to trigger a function to toggle the admin property. So create div for this section. We want to use the if to only show this section if the selectedUser is equal to a class of selected_user. To p element where I can output the admin message. This was toggleAdminMessage and then a second p element just below. This one is going to have a string of text where the value of the user is currently set as an admin or not admin. We'll say user, and then pass it in at the selectedUser.email is currently set as, and then we want to dynamically set if this is or false. The way we can do this is by selecting our selectedUser.isAdmin. Using the JavaScript ternary operator, we can display the text of admin if this is true. If not, a text of not admin. The final element to go inside of this section is a button. This button is now for a click, prevent the default. This will trigger a function which we've not yet created called toggleAdmin. We'll come back to this function in just a moment, but for now, the text of click here to toggle admin setting. Save this and then we can go back over to our use of composable, where we can create this toggleAdmin function. Async, toggleAdmin, create the try and the catch block. Just like we've done previously, we'll start with an if statement to check if the user is the admin value. If not, we'll return out of this function because they are not authorized. This is found from userIsAdmin.value. This is false, we'll return out this function before making any changes. Now we need to create a reference to a particular document which we want to update, store this inside of a constant called docRef. Then we can construct this with this Firebase doc method. It takes in a reference to our database we want to refer to the user's collection. Then finally, we want to find a document by the ID. We can do this by grabbing our selectedUser and accessing the ID property. So selectedUser.value.id. Remember what we want to do with this document is to update the isAdmin property to be the opposite of what it currently is. For this, we can write a Firebase method called updateDoc. We will pass in the above document reference. Then as an object, we can set exactly which properties we want to update. We only want to update the isAdmin property to be the opposite of what it currently is. We could do this by accessing our selectedUser.value.isAdmin property and set this to be the opposite of what it currently is. Once we've updated our Firebase document, we'll then once again call findUser, which will then retrieve and set our user data and update any components. We also need to pass this, the user's email, which will have in selectedUser.value.email. We'll just place it in a simple console log into the error section. Also since we've used the updateDoc method from Firebase, we need to import this at the top and then return the toggleAdmin function down at the bottom. Back to the toggleAdmin.view where we actually call in this function from our button, which is just here, but we still need to import this from our composable. This should be everything now in place. Let's go over to the admin. Is email address is currently the admin, but the other one is set to be false. Let's toggle this now. Click "Find user". This user is unretrieved. We can see the property of isAdmin is set to be false. Click "Toggle". This is then updated to be an admin user, which you can also see inside the console. This is not true. We'll then reinstate this back to be false, which we can also see updated in the database. 59. Firebase Rules: This is now going to be the part where we increase the security for our application on the database side. What we'll do inside of the Firebase console if you go into the Firestore database, and then we have this option called rules. This will allow us to add some security rules to our database to determine who can do what. We can set who want to create, read, update, and delete data from our database, and we can be more specific about which documents or collections these rules will apply to. At the moment, our database rules are not secure. If we take a look, we have this much here which matches a certain collection of documents in our database. At the moment, this is pointing to the outer entry point of our database. Then nested inside we can add additional matches to be more specific about which documents or collections the rules inside will apply to. This current setting of document equals star star will match all the documents in our whole database. Our database is not secure since it allows full read and write access as long as we are within one month of the database being created. This one month only rule is a security feature, so don't forget to switch off the full access. This will again protect our database if we forget to come back in and adjust these rules. To add our own rules, what we'll do is remove this inner match and begin to write our own rules. I'll remove this and leave the outer match, which again points to our full database. Then we can create a nested match. We'll start with our pizzas collection, so /slash pizzas, and then we also have the orders and also the users. We can add these rules too. Then remember, any one of these pizza documents is stored in a unique documents ID. We can place in a variable and give this a name of doc Id. This will allow us to access the document ID inside of this section. For pizzas, we'll start by saying allow read. We allow read because we're fine with anybody reading the items from the menu. However, though, when it comes to creating, updating or deleting pizzas, these needs to be controlled. We can also group these operations together. We can say allow, create, update, and also delete. As long as some certain conditions are met, we only want to allow these three operations if a certain condition is true. This condition is they are an admin user. The first thing to do is to check if the user is actually authenticated. We can pass in an if statement where we can access the request object where we can access the off property and we can check if this is not equal to null. This means that the user which is making the request for this particular documents inside of our pizzas is currently authenticated. This is the first step. We have an authenticated user, or we also need to access a user's property of is admin inside of the database. We can do this by using a double ampersand, the chain on an additional check. We can access a get function provided by Firebase, which will allow us to add a file path to a particular document. Inside here we need to begin from the root path. The very outer match, which is pointing to our database. We can copy this section and paste this in. Then just like we've done here, we can add our collection name. We want to point to /users, and then a particular user Id. We can access the current user Id from this request.auth object. Just after /users, we can insert a variable with the dollar symbol, open up the brackets, personally request.auth, and then we can access the uid. This takes us to the particular document which you want to access. Then just after these brackets, we access the data and then the property which is isAdmin, capital A. Once we access this isAdmin property, we'll also want to check if this is equal to true. Just to clarify, we're do in two checks here. We're checking if first of all, the user is actually authenticated, meaning they have successfully logged in. The second check is to jump into the user's collection and then check if the isAdmin property is also set to true. Before we go any further, let's test these rules inside of the rules playground. We can expand this and we can create a different simulation types to get our data, to create new data, to update and also delete too. The location. This is the path which we are currently checking. It's /pizzas /docId. We want to test this with an authenticated user. Our authentication provider is the password, then the Firebase UID. This is the user ID which is making the request. I'm going to grab this. Without moving out with this page, I'm going to right-click and open the authentication in a new tab. Let's copy. In fact, we'll go into the database, into the users. This one is a unique ID. Let's copy this and this user is the admin. I'll paste this in. Click on Run and we making a get request. Here we can see this is highlighted on the pizzas collection. Anybody can read this data. We would expect this to be fine. But the restrictions lie when we create, update and delete data. To create data, optimistic given us an error. Let's check this out. This is pointing to our database match. The database is, a database, documents and I think this just needs a dollar symbol since it's a variable. Dismiss. We also just need the regular brackets to match this one here to insert the variable. We'll try this once more. Run this width at the Create. This is fine. Update. This is fine. Remember we are an isAdmin user. Also the delete works fine. Let's change this over to be the user which is not authenticated, so isAdmin is equal to false. Copy this, paste this in. We are running a delete request. This is denied since we're not an admin, but we should still be able to get our data since anybody is allowed to read our pizzas. Create, there should be a fail. Finally the update 2. Good, so now we know this is working. We can repeat it for other collections. But our orders, we want anybody to be able to create an order. But any of the other three operations should only be performed by an admin user. Let's copy this match. Then paste this in just below. This one is for our orders. You can also rename this variable if you want to. For here, we'll allow anybody to create an order. We want to add some restrictions when we are reading, updating or deleting. The same rules apply. We want to check if the user is logged in and authenticated. We'll also get the isAdmin property from the user's collection. This can all remain the same. Test this out. This is orders. That's well. We'll go to Create. Remember anybody can create a new order and we're still with the user, which is not an admin. It should be fine. Now we can test the other three operations. Get, this is a fail as expected. Update. Delete. Then just to confirm, we'll jump into our user's collection, and grab the ID for the user, which is an admin. We choose one. I'll go to the user which is an admin, can make a delete, an update, a create, and also a read. The final one we need to do is to match our user's collection. They should allow anybody to create an account to begin with. We'll create a match or /users, pass in the variable of docId. Then we want to allow anybody to create a new account, and we also want users to read their own data. We'll allow read. But we'll also restrict this. We'll also check if the user is authenticated. Paste this in. Also to allow the user to only access their own data, what we're going to do is to check if the user; which is available on the request object is equal to the same user ID, which is passed into this variable. The double ampersand form a second check. Access request to auth the uid. This is the ID of the person who's actually making the request. We want to check if this is equal to the document's ID. Therefore, if both of these match, the user is reading their own data, but as well as users reading their own data, we also want to allow a user to be able to read any user's data in addition. As well as this, we also want to wrap this inside brackets, and then perform a second check. Then also allow this to be true if the user is an admin. We already know how to get this information, and this is with this get function just above. Copy everything from get right to the very end where we check if the admin is equal to true, and then be careful to paste this inside of the brackets. This will now always check if the user is authenticated, and then either check if the user is an admin or if they are accessing their own data. In fact, this needs to be the JavaScript OR operator rather than the AND, meaning either one of these sides can be true. The last one to check is to allow the update, and delete functions if they are an admin user. We'll allow. Combine these into update, and delete sections. We'll add the same conditions as any one of these above. We'll check if the user is logged in, and if they're also an admin. Copy this line, paste this in, and this is now all the rules in place. This could also be extended to allow users to update their own information too but this is fine for now. But it also includes lots of duplicate code where we check if the user is an admin with the get function. We can improve on this by creating reusable functions. We'll create two functions. One would be to check if the user is an admin, and then one to check if the user is accessing their own information. We'll put it all inside of the outer match. Put some space. Then we'll create a function just like we'll do in JavaScript, so isAdmin, and then a second function called isAccountOwner. IsAdmin, this is the one which we use quite a few times now. We want to copy the request auth is not equal to null. All the way to the very end of the get function, copy this. Go into the isAdmin function, and then we want to return this value, so paste this in. This will mean our function will return either a true or a false value. So isAccountOwner, this also needs to check if the user is authenticated, so we'll return requests auth is not equal to null. Then just like we did at the bottom with users, we also want to check if the ID of the person making the request is equal to the document's ID, so copy this. Chain this on with the double ampersands, paste this in. But remember here we're accessing the document's ID. This document's ID is only available inside of this section, so what we need to do is to pass the function, the ID, and then we'll replace this one. We'll add in this document's ID when we call this function. We're almost there now. What we need to do is to remove all of this cubicle code, and replace these with our two functions. The first one is isAdmin. This is checking the user is authenticated, and also if they are an admin so we can remove all the code from our pizzas' match. Just after these statements, remove all of this line, replace this with our function exactly the same for our orders. We're checking the same thing. Cut all of this out of place. Put function in. The last stage is our users, and this is going to use both of these functions. For the read, we'll cut out everything after the if statement. This is going to make use of both functions. If isAccountOwner, the ID will be the document's ID from above. All is admin. This will perform the same two checks which we had before, and for the delete request, we're going to move everything after the if statements, and simply pass in the isAdmin function. This now looks a lot cleaner, and a lot more easier to read. The final thing is to test our user conditions inside the playground. First of all, the location we are testing the user's path. We'll begin by copying the user ID of the admin user. IsAdmin is equal to true, paste it in. This one should be allowed since we are the admin user. We'll also perform the second check to see if the account owner is making this request. Even though we are not the admin, let's copy the non-admin user. Paste it in, in the code. This simulation is now denied. The reason this is denied is because although we are an account owner, we're not accessing our own documents. We're just accessing any generic documents inside the user's collection. To test an access to our own documents, we're going to move the variable, and paste in the same user ID as we have below. Run this. This now works since we are the account owner of the documents which we are checking. Create. Let's try this out. This works. We can also replace the variable. It should work for any documents. We'll try one more. We'll try a delete. Run this as the non-admin user, and this should be a fail. Switches out. Copy the admin user. Paste this in, and this one is allowed since we are an admin user. There we go. This looks like everything is working fine with our security rules, and you can also update or play around with these rules if you want to. But for now, our app is much more secure, and we'll publish these changes.