React Expense Tracker App - Build & Deploy A React Firebase Beginner Project

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hey, guys. How's it going? I'm back here with another video, and today I'm going to be showing to you guys how to build a full stack application using React and Firebase, where the users are going to be able to track their expenses. Now, you've probably seen a project similar to this in the past, and the reason why I chose this project for this video is because it isn't that long. You can see by the length of the video, but also it teaches you a lot of the good fundamentals that are important with any full stack application now differently from how I usually make my videos, I'm going to be focusing a lot on establishing good practices while writing my code. So although there might be simpler ways to do certain things I'm going to be making in this video, I'm going to teach you guys how someone working in the industry would actually build it. That means since racing react, I'm going to be using creating a lot of custom hooks, organizing the code in a way such that it's efficient for multiple people to collaborate in and just make it high quality code. If you're interested in, just check out the code. It's totally fine by me. The code will be in the description. I'm going to give you guys a demo real quick right here. What we're building is, like I said, an expense tracker application. The UI isn't perfect. I try to make it as cool as it can be. However, like I said many times in the past, I'm not perfect with success. That's not what I usually do. So for me really doesn't matter. It's more about the functionality, right? So this is how the application goes. You can sign in with Google. You guys can add other ways to sign in as well. Then you can choose your account. I'm going to choose this one over here and as you can see, it will log into your account over here and it should keep track of all your previous expenses. Whatever you did. You can see one thing that is there. My opinion it looks kind of weird now is just the picture is kind of pixelated, but that isn't a problem with the coding itself. It's just that for some reason I put a picture of those pixelated with this Google account. What this does is it gets the profile picture with whatever email is associated. Whenever you log in and you can see it's all it tells us. The total balance of our tracking list, how much income we earned, how much expenses we expensed. Right. And the list of transactions that happen. So if I, for example, went to I don't know, I just did a paint job. I'll put a paint job. And I actually did this to a friend who offered me $200. I would come over here and do this. I would choose between income or expenses. In this case, I'm gaining $200, so it is income. And then click on add transaction and you see my balance increased, my income increased and this transaction is down here at the bottom. But if I decided that for some reason I want to buy a gift for a friend, I can put over here gift, gift and then put that it cost, I don't know, $80 and then put expense. You'll see that same thing happens. Gift appears over here and our balance should have decreased. So it also works like there's a lot of minor details that I made sure to add to this so that we can make this look as good as it can be. For example, if I had to pay rent and for some reason the rent was like $2,000, you know, the thing will become negative. So the the actual thing handles the negative sign over here and where the the dollar sign appears over here, all of that you'll see as we play along and we code in our project. And by the end, if you want to change your account, you can just click on sign out and remember that all of this is is persistent per your page, right? It is all stored in this case on the local storage. So you can log out, you can refresh the page, you can change tabs. It doesn't matter. It will keep all the data saved. So this is basically the project we're going to be building. Before we get into the video, you can leave a link and subscribe out. Massively appreciated. And let's get into the video. before we get into the video I would like to talk a little bit about today sponsor Skillshare. For those who don't know, Skillshare is an amazing online learning platform that has literally transformed my approach to coding. As you might know, I have great relationship with Skillshare since they have been supporting my channel for a while now. However, this relationship started way before my first video because, as you might know, I am a self-taught developer and I actually used Skillshare to tune and improve some of the skills that I was lacking through my learning journey. For example, I remember I always had issues with access, as you might know, through my videos, but I decided to take this fantastic course called Modern CISSP, writing Better, cleaner, more scalable code by the author Harry Roberts. And not only did it help me fine tune my skills, but it literally changed the way I approached access coding as a whole. I literally can't recommend it enough. But Skillshare is not just about access or coding. It also offers thousands of inspiring classes on everything from illustration to entrepreneurialism. The best part of all of this is that you can literally access all of this for free. Yes. You're literally heard me right. Skillshare is offering a one month free trial for the first 1000 of you guys who decide that you want to explore and learn from their incredible class library. So I kind of have this challenge for you all. If you have any skills or anything related to programing or even outside of programing, it's always good to improve your different skills. You should go try at least one month of Skillshare. Take control of what you're learning and redefine what you're actually working with. Set new goals and see what you can achieve from all of this. Again, thank you Skillshare, for sponsoring this video. If you guys want to check it out, don't forget to check out the link in the description. Now, let's get into the video. Okay, everyone. So let's start writing the code for this video differently from a lot of my videos. I'm going to literally start everything from scratch. The only thing that I won't be focusing on this video is the cassettes, which is standard for me because the video's purpose is to just focus on the functionality and the technologies that we're learning and not having to spend 30 minutes writing the thesis that maybe you guys won't even copy them today. But I did register this as a go. So in the beginning, so as I go through the video, I'll be pasting some says I will be writing the class names for each element and I will be leaving all the code in the description if you guys are interested in it. So let's start by building our project so that we were going to do this is you can use NPM or yarn. I like to use yarn, so I'm going to run yarn, create, react just like this. And then we'll put Dot because we're going to create everything inside of this folder. So we're going to start out when this is done by actually setting up our folder structure first so that you guys can have an idea of how our project will be organized. Okay. As you can see, we just finished our running. The command create backed up so now we can actually start working on a project. First of all, I do want to delete some of the files that come with the great racked up. I always do this. I want to delete the set of tasks, report web vitals, logo index stats. Yes. And the app to test that. Yes. I do this as always, because they're not going to be useful for my video because we're going to be testing this video. I have videos and testing if you guys are interested. And this is not going to be a real project. So it really doesn't matter. End of day and it will make the folder structure a little bit less confusing. Most are going to clean up some stuff like deleting the initial boilerplate that comes with the application and some of the imports so that our app can run smoothly to test out if everything is working. I'm going to just run yarn start and hopefully the browser over here. It should reload to an empty white page as you can see. So what I want is a following. I want to start out by organizing our project in a way that it will make our project efficient to work alone and also efficient to work with other people. Like I said, I'm going to be focusing a lot on best practices and good practices in the long run. So I won't introduce this to you guys. So the way we're going to run this is we're going to have two pages in our application, which means we're going to have two routes in our application. I like to structure my pages and routes by creating a folder called pages. in this folder, whenever I have a page, which I'm going to have two of them, I will create a folder for each individual page. For example, as you saw in the beginning, we had the first page, which was a page where you can sign in with Google. So I would call that page authentication for us. And then we also have another page, which is where we actually have the dashboard where you can add the transactions and do all the expensing and all the tracking. So with that page, I'm going to call it expense tracker, just like this. I wrote expense wrong, so I'm just going to fix it. So I'm going to have those two pages for now. Will leave those folders empty because we don't want we're not going to read the files yet, but we're just creating the structure right. Then what I also want to have is we're going to be using Firebase for this project. And if you've ever worked with Firebase before, you know that there is a little bit of configuration locally. With Firebase, the configuration is extremely simple and can be done in almost just one minute, but we do need some space to store all of the configuration files and in the future, if we need more configuration files, I can also put all of them inside of the folder called config, which I always like to have inside of my projects. Then what I want to do is finally we're going to be using some custom hooks to work on this project. So I will create a folder called Hooks. If you've never used custom hooks as a way to improve the efficiency and the quality of your code, don't worry, I will be showing us exactly how they can help. Just just keep in mind that we're going to have them and we're going to put them inside of this folder. And now this is pretty much a folder structure done. If you're if you're going to put components, you can create a components folder, you can add more stuff as you go. You can even put individual hooks inside of the individual pages if they make sense for those pages only. But in this tutorial, this is the structure of what we're going to be working on. Now, what we want to do is we want to start by adding the packages that we're going to be using in this project. And to be honest, for simplicity reasons, we're actually just going to be using two dependencies. So the first one is going to be and I'm going to add it by running your and I'm going to run React router done this over here is what's going to allow us to have different pages and different routes in our application. I believe you guys are all familiar with this library already, so I won't be going too much in detail about what I'm going to be doing with it, but it's one of the most popular libraries in React, so I expect most of you to already know it. Then we're also going to add Firebase, which as you might know, it will install everything related to Firebase for us. But we're going to just add those right now and start building our application by actually setting up the initial stages of our Firebase app. So to do that, we're going to open up the Firebase console inside of our browser. So the way I do this is you go to Firebase Dot, Google.com and you'll see this probably you have to probably sign in with a Google account. I already have mine and I'm going to go to console by clicking on this button. It's going to open up your console with a bunch of projects that you have used in the past. This is my Firebase account that I use for my videos, so there's only stuff related to my own personal videos, not really any projects that I work on my own, but I'm going to click on Add Project over here and we're going to start setting up a new project. Now let's call this expense tracker since it is what we're going to be building, but you can call it whatever you want. Now it's going to ask us if we want to set up Google Analytics for our project. Now, if this was an actual project with with like I was going to deploy it, of course I would do this and I do recommend you're doing this. But what we're going to be doing is just creating the project and at the end I will deploy it, but it won't be used by anyone. I would just deploy. It just showed you guys how to deploy this application. However, we don't need analytics. In your case, you might need it, so I would recommend actually using it then weekly concrete project and it will start creating our project whenever this is done. Are we back to continue? Okay. As you can see, it is done. And we know that because it says that it's done. So we're going to continue and we are inside of our dashboard, which is pretty good. So the console for Firebase is really, really intuitive. We have over here on the side, we know that we are instead of a project because it says expense tracker over here. But we also have this thing called built over here. You can see all of the different services that Firebase provide to you. So Firebase isn't just a database or isn't just authentication or deployment. It's actually so much more. And I've made so many to Tourism firebase in the past. You can check them out. But if this is the first time you're working with Firebase, this is what we going to do. And this and this approach is going to have to service is being used. You're going to have a authentication and FIRESTORM database. Now, this is for the database. We're going to set that up later. But initially, let's just set up our authentication and click on it and it's going to open this up. We're going to click on Get started. Then it's going to ask what kind of authentication we want to have. It's really easy to set up any kind of authentication you desire. The one I like the most is Google, because everyone has a Google account and you just have to click on your email. I'm going to click Enable and I'm going to click on my email. Then over here we're going to click on Save and we're going to actually start adding the authentication provider to our project. Now, as you can see, it says that the status for Google authentication was enabled, which is pretty good, and means that we can actually start using our project. However, in order to connect the project that we have in our code, in our yes to the code and the project that we have in our console over here, we have to click on Project Overview right over here and then there's going to be an option over here called Web because we're setting Firebase for a web app. And then over here it's going to ask us to register our Firebase Web app. We're going to give a name. You have to give a good name, in my opinion, to something that is memorable for you. I'm literally going to call it expense Tracker just like this. And I'm going to set up Firebase hosting. The reason why I would do this is because in the end of the video, I'm going to be deploying this app and if you want to do such thing, you also set up Firebase hosting. Then I'm going to click on Register app and I'll have to wait a bit until it's done. So now that it's done, it asks us to install Firebase, which we already did. And then it asks us to copy this piece of code. This code over here will be, like I said, the full configuration of Firebase. Like, we're not going to have to do anything else other than just copy this and paste it into a file instead of a project. So to do that, I'm going to open this up, go to config and create a file called Firebase Dash config just like this dot. Yes. And then over here I'm just going to paste the entire code. now that this is here, I'm going to save this. And this should already connect everything with our own project. Now obviously if you want to make all of this into environment variables because you're going to post this on GitHub, you can do so. But since I made the lead this app right after I post this, it doesn't matter to me. You guys can see my API key and everything related to this, so I'm not going to waste time creating environment variables. But this pretty much configures everything for us. However, we do need to install the firebase yellow because this is what we're going to be using to actually deploy our website to Firebase now it's going to ask us to install the firebase. Yalla. Which we need to use in order to deploy our app in hosted inside of Firebase Choosing AMPM. It's really cool. You can just run this command in any one of your actual command lines because we need to install this here like globally to then use it inside of your project. So since I'm using yarn, I'm just going to run Yarn Globe go add and then you run Firebase Tools. I already have this global installed so it doesn't really matter to me. I'm not going to run the command, but you do have to do that if this is the first time you're running this app, particularly next. And then I'm going to go to this options over here. It's going to ask us to deploy our app into Firebase. Now, the reason why it's asking to do this first is because we can test our app already deployed. Obviously, we can prevent people from accessing it, but we can already start doing this process and I'm going to start doing just the first two, which is having a log into our very basic account inside of a project and then initiating it. The last one I'm going to leave for the end of the video so that we start the deployment process. So I'm going to run Firebase log in. It's going to ask us to log in. It says, I'm already logged in with my email. Now I'm going to run Firebase in it and it's going to initiate a project for us. We're going to choose which Firebase Features So now there's a couple of things that it's going to ask us to do, which is it's going to tell us to do ready deployed to Firebase hosting. I'm going to leave all of this to the end, but I will keep track of the commands that we actually need to use in our project. So this is something I only know the commands nowadays, but back in the day what I would do is I would come over here and I would just write again in Firebase log in and Firebase and it and then Firebase deploy. You can also just check the page again. But this is just so you don't forget. We're not going to do those now. We're going to do this later. Whenever we're done with the project and we start deploying. So we have everything organized, but we're basically done with the setup in our video. Now what we have to do is we have to start sending up authentication inside of our project and to do that, we have to first create our authentication page. so let's start setting up React Writer Dom In our project. I'm going to zoom in a bit so you guys can see better. We already have installed the package, so I'm just going to import from React Router DOM and what I'm going to import is first of all, I'm going to import a browser router and call it a router. Always do this, then import a route and then import routes. So if you're not familiar with directory dom, just keep in mind that this is the standard syntax for it. Each of these components have a different purpose and I have a lot of videos with extensive explanations on what each one means. But basically if you want to set up some routes, we have to first initiate the router in our project to let react, know that we have a router. Then we have to distinguish where in our code our routes will be defined by using the routes component. And then instead of your, we can define each route individually by using the routes component. The single, the single or route component. And instead of a routes, we're just we're just going to have to route in our app. We have to define a path which will just beware. Like how do you set up here you are el to go to this route. So the initial route is always going to be an empty slash because that's the base URL for a website. And I want to switch that. When you go to the website and you're not logged in, you're just going to automatically be sent to the authentication page. So they'll send the syndication page is going to be the initial route I'm going to set up which component will be rendered when you go to this route by using the element prop over here. And we haven't created the off page, but we will do that right now in order to pass it over here. We're going to come to pages and we're going to create an index dot GSX Now this is going to be a React component. That's why I called it a GSX. And the reason why I called it index is because the way I like to organize my folders is I already know that this is the authentication component, right? This indication page because of the name of the folder. So the file, I'll just call it index to keep everything standard. Then instead of here I'm going to export a constant called us, which will be our component. And at the end for now I'll just return a div saying Hello world, something like this. Right. And then over here the top import from the pages folder slash off slash index. I'll import the US component and all I have to do now is just plug this inside of here by defining the off component as the component that is going to render when we go to this route. now you see we put an exact thing over here and this is just because we want to tell basically our page that this has to match the exact route that we set up in order to go to this route. Then we want over here to define the second route, which is going to be the one with the expense tracker page. So I'm going to set up a path and call it expense tracker, and I'm also going to put a slash over here. Then I want to set up the element, but we have created the page, so I'm going to do exactly what I did with the US, which is I'll come over here and I would just copy this page pasted over here, but then change this to me. Expense tracker. Right. And I also add something over here so we can distinguish the two pages and kind of know that this everything is working right. So I just come over here, setup expense tracker, just like this. And now we just have to import at the top the expense tracker from Dogpatch pages slash expense trackers slash index. Right. So we set up our routes and we can check to see if everything is working by coming over here. And open this up. And you see that when we are in our basically normal routes, the initial route we see Hello world, which is the off page. But if we go to the expense tracker, we should see Hello world expense tracker, which is pretty nice, which means our routes are working. Now let's start working with the pages individually. I'm going to come over here and we're going to open up, close this and open up the off page. Right this part over here. Now, like I said, our off will be pretty simple because it's just going to be a Google authentication. Now in order to set up Google authentication, I'm going to first give this a class name. Like I said, we're going to be having some success, but it's going to be the focus of the video. And you guys can just copy the class and copy this just later if you're interested in doing that too. But we'll then put a P over here, a P tag, and then just write something like sign in with Google to continue. Then we're going to add a button which is going to be seen something like sign in with Google and the button. I'll give it a class name of log in with Google Button, something like this. And we actually have to set up the function which will sign in with Google. We'll call it sign in with Google. Now, this function seems like it would be hard, but it's actually going to be pretty simple. We're just going to create it over here and it will actually call a part of our Firebase package, which we have to define over here instead of our config part. So in order to set up authentication in our config, we just have to import some stuff from Firebase. So I'm going to say import from Firebase Slash auth because when you're going to import something from a specific service, you just do firebase slash to service and we're using the authentication service. Now. What we want to import is the function get us and the Google Google auth provider. The get our function just determines that you're going to be using authentication in this project. And the Google auth provider is obviously used for setting up Google specific authentication. Now all we have to do is come down here below the definition of the app and we'll create a constant called off and set it equal to get off and then pass in the app and then create of of variable called provider and set it equal to new Google auth provider. And we do need to access those two things elsewhere in this project. So we're going to export both of them and we'll just come to our ask over here and import both of them by basically just importing from and then we'll go back once, go back twice, go to Hong config, slash firebase config and import the path and the provider just like this. Then what I want to do is I want to import some functions from Firebase in itself. So we'll import from Firebase slash off like we were doing before. And we have a bunch of functions that we can use now, a bunch of different assigning functions. Like I said, you can sign in with Google or you can sign in with your email, with your phone number however you want. And the one that I want is the one where we sign in with a pop up. I want to pop up to appear and you click on your email account. So we just write, sign and with pop up just like this. And over here we'll just call this, we'll say sign in with pop up. Now, this is going to be an asynchronous call, so it's going to be gender promise. So we do need to make this into an async function, and we're going to create a variable called results, which is going to be equal to the oh wait response of this function. Results will include everything related to the user who just signed in. So we're going to be using this in a bit. The important part is this over here, where in order to sign in with pop up, we do have to pass in the other variable and the provider. So for starters, this should be working and we should console.log the results just to kind of see what we're going to get back whenever someone tries to log in. We'll check this out by going to the log in page, this one over here. And we see that currently we have over here the text and the button to sign in with Google. But does it actually work? So let's click on this button. You'll see that some emails popped up. We have to choose which one we want to use. Now I'm going to choose this email over here and hopefully when it's done, log in. It should return back. As you can see information about this user. So as you can see, it's an object called user credential. And there's a part of the object called user, which if we click there, it actually contains a bunch of information about the user. This is a fake email account you have, which includes a display name for the user, an email information like a profile picture and much more. But we're only going to be using those three things so we can see that we actually have the information coming back and we're actually logging in. So what do we want to do with this information? Well, what I want is I want to keep track of a couple of things. I want to keep track of the user ID, the name of the user, the profile picture, and if the user is logged in or not, because those are the things that we need in our app. So what we want to do is we want to actually store a bunch of information inside of our local storage. The reason why we want to do that is because if you go over here and go to local storage, it's on the application tab and you go over here, there's nothing right here. But what this does is whenever a user log in the first time, they log in and they see we saved the information to the local storage, they can close the tab, they can refresh the tab, they can switch tabs, they can leave the computer and come back. And that information will be kept there, meaning that they don't have to sign in again. So we do persistently keep the user logged in. Now, it's not the safest way to do it. I would actually recommend using cookies, but to make things simple, I'm going to store it in the local storage. They're not that different to to handle with So we can do this by just coming over here inside of our code and actually just saying that we want to set an item to the local storage. Now, what item do we need to set? Well, I want to set an item code off, and I want this to be an object full of information regarding the user. Now, if you've known or worked with local storage before, you know that you can't store objects. You can only store booleans, strings and numbers, I believe. So what we want to do is we want to turn the object into a string and then turn it back whenever we need it as an object again. So this is a very common way to do this. We're going to actually create the object over here that we want to keep for the user. So we're going to call this off in full and set it equal to an object. And then the object. What we want from it is we actually want an object that has a user ID which is going to be equal to the result from our users. Any in user recall we had a user object that you ID, this is the ID that we get. How do I know that this is the path to it? Well, if you go to the console, you remember we have the object, we go user and then we go dot you ID and we're going to do the same thing for the display name and the photo yourself. So we'll just come over here, set up the name to be equal to result, dot user, dot display name, and then the profile photo to be results. Dot user dot I think is is a profile photo is photo yourself just like this? I also want to know if the user's logged in or not because this is important. So say is off is equal to true because we're logging in, right? And then we have this object. What I want is I want to set the local storage item to be equal to this object. Right. But as I just said, you can't do this because you can store an object inside of a local storage. So what we need to do is we need to use a function from JSON called JSON, not string ify. And this function turns any objects into a string which can then be stored into the local storage. So this should pretty much be good. We're going to test to see if this is working by trying to log in again, by clicking this and then clicking on the email. We want to log in, we're going to check out to see if it's console logged. It wasn't because we removed the console log because now we're actually just storing it in the local storage and you'll see that it is over here. We have, as you can see, an off key instead of our local storage with a value of a string that kind of looks like a JSON object, but it's actually a string in the format over JSON object with the JSON package over here, we will actually be able to turn that back into an object later on. But this means that everything seems to be working. What we want to do after this is right after you log in. We don't want to keep the user inside of the sign in page. We actually want to navigate them towards the expense tracker page because they just logged in. So in order to redirect you to another page, we're going to be using something from React very dump called the navigate function. So we're going to go to import from React Rotterdam and we're going to import a hook called the use navigate hook just like this. Now, up here at the top, we're going to define a variable called navigate and set it equal to the use navigate hook. I'm actually going to call this a constant just for standardizing purposes, but basically this function, whenever we call it just doing this, we can actually put over here a route to which we want to navigate towards, and it will take the user there. So I'm going to put that we want to go to the expense tracker page and you'll see that whenever I log in and I'll try logging in again, Now we just choose the email we want to log in with and you'll see that we're now redirected to the expense tracker page, which is perfectly, exactly what we want it. Now it's time to start working on the expense tracker page. So we're going to go back to the index digest file that we created for it. Currently, we don't have anything, but we're pretty much done with the authentication page. We just have this text saying Hello world expense Tracker, but we want to add a lot more to this page. what we want to do is we want to set up this structure for how this page is going to look like. I'm going to delete this and I want to add over here this and we're going to call it I'm going to add a class name and we're going to call it expense tracker, just like this. Then I want to come over here and add another div and call it container. Then just delete this and add a name with a class name of container. Now instead of here we're going to add an H1 tag that is going to say expense tracker. We're later going to add the user's name over here, but for now we'll just keep it like this and we're then going to add a div which is going to display the balance, the total balance for user's expenses together with the user's total income and the user's total expense. So we're going to add class name to this and call it balance because it's the balance part of our app. And then I want to add an H three tag. Seeing your balance. Then down here, we're going to add some sort of number For now, it's going to be an education du tag to do this, but for now we'll just see that the balance is 0.00. Right? But we're going to add the actual dynamic balance in here and then going to add another div together with the summary of both the income and the expenses. We're going to give it a class name of summary and we're going to add another div with for the income and a div for the expenses. Right? So I'm going to call this income and I'm going to call this expenses. Okay, So expenses and for the income, I'm going to add an H4 tag because it's even smaller and it's going to be called income and I'm going to add a P tag to display the income. So a paragraph tag, which for now again similar to the balance, I would just put 0.00. I'll copy this and paste it over here and just change this to expenses. And we should see that it's starting to kind of look like what we want. But obviously all this SS hasn't been added yet and a lot more should be added to this website. So now that we pretty much set up the top part of this page, we're going to start creating the form that is going to be used to add a transaction. So I'm going to create an actual form. I'm going to call this form add transaction. And then over here we're just going to add an input which will be of obviously type text and then I'll put a placeholder for it so that you just can know what you're going to type here and it's going to be the description of the transaction. So like the name. So like if you added a haircut, it's going to be haircut, you know, something like that. We're going to make it required because you do have to do this in order to finish the form. And for now we'll just keep it like this. We're going to copy and paste it again because we need two of them. The second one is going to be for the amount of of of the price, right. Of the expense or the income, whatever you choose to use. It's not going to be if type text is going to be a type number because because this is going to be a number. It's also going to be required. So it's pretty much set like this. Then we need to add the radio buttons in order to determine whether it's an expense or an income. Right. So we're going to add an input of type radio. And since this is a radio button, it's a bit different. We're going to add an ID over here of expense and a value of expense. So this is going to be the expense one and for now we'll just remove this and also not add anything else. But we're going to add more stuff for both for all the inputs, to be honest, because we're going to have to make them dynamic and save the information in each of them. Then for the radio button for income, we're just going to change this to income and this to income as well. Now for each of this radio buttons, which you need to need, we you need a label to them. So I'm going to add a label to say exactly what they are. The first one is for expenses and the second one is for income just like this. And we have to attribute each of the labels by using the HTML for property. So this is for the expense one. So we just copy the idea of this one and put it over here. And this is for the income one. So we just copy the idea of the income one and put it over here. So now we have our form pretty much done. All we need is a button to submit the form, so we're going to put a button and give a type of submit so that whenever we submit this form, it knows that it will be submitted. Whenever you click on this button and we're going to write a text, same ad trend. This is action just like this. Let's check to see how it looks. And it looks kind of cool, but it's obviously not the format and structure that we'll look at the end, but it looks like we pretty much set up the skeleton of this. Now we also want to have the transactions part of it, which is going to be a list of transactions. And the wish structure in my UI made is such that I'm actually going to put the transactions part outside of this major expense tracker over here. We're going to create a separate div called transactions. And the reason why I added this fragments, if you don't know, is because whenever you have your returning, you need to return only one thing, right? So you can't return a Dave with the expense tracker. And then another div being it's sibling. You have to just return a fragment with two sibling diffs. Right. And then over here at the bottom, we're just going to give this a class name of transactions just like this. And for now we're just going to add an H3 set tags, same transactions. Just to keep in mind that we do have to add this later on. Now, I pretty much want to start adding the functionality to be able to add the transactions. Now, the reason why I want to add the transactions before I'm able to see the transactions is because how am I going to see a transaction if I haven't added one yet? Right. So that we were going to do this is we're going to start setting up our database inside of Firebase. So to do that, it's pretty simple. We're just going to open up the building over here and go to the far stored database service. Then it's going to open this up a Similar to the authentication. We're going to click on this button, which is the Create database button. Then it's going to ask us if we want to start this and prediction mode or in test mode since we are actually working and we're going to be deploying this, I'm actually going to choose production mode, but there's only one minor change that I need to do in order to get things running on production, which is to add this over here in the rules part of the database, if you're not familiar, I'll explain in a bit, but basically I'm going to choose next over here. Then it's going to ask us to choose the first for a location. Well, Firebase has a lot of locations, right? That's what makes it very reliable. And to be honest, I live next to the U.S. I live in Canada. So this is the closest part to me. So I'm going to use this one. Make sure you choose one that makes sense to you and your users because you can change it later. So I'm going to click on Enable and it's going to start creating our Firestorm database. Okay so I've finished grading everything. And as you can see, this is what we can see. This is basically the dashboard for your database. And Firebase kind of uses a no sequel approach to data. So it's not like you're using my squirrel or or Postgres or anything like that. It's a bit different. We have collections and documents. A collection would be almost like a table in a school database, and in our case we're just going to have one collection. The collection is actually going to be called transactions, right? Because we're going to keep track of all the transactions that happen. I'm going to click next and it's going to ask us to create our first transaction. To be honest, I'm just going to click Out of Idea over here and created. You do have to create a first one, but we're not we're not going to use it. The transaction that we're going to have is actually a different one. So I'm just going to delete this document over here. Documents would be like an entry or a row in and SQL database. If you're not familiar with this. Now, we pretty much created the structure of our collection in order to get it working. We do have to change the rules, remember, because this is working in production now, what are the rules in a fire fighter store? Data? This well fire store actually allows something really cool, which is for you to determine who is able to make changes inside of your database so you can allow them to be able to read, write, update, delete. You can allow them specify exactly what kind of interactions you want the users to have with your database. As of right now, it is said by default to only allow users to read and write the database if false, which means that it will never allow any user to read or write in the database because false is never true. So it's almost like a coding language over here just for for the rules section of the first our database. For now, we want to allow every single user to be able to make a change because this website is pretty simple and I don't need to actually specify anything. To be honest, I do whenever it's an important project, but for this we're just going to set it to true so that everyone can do whatever they want and add in and read the database. That doesn't mean that they have access to your database. That just means that if they go into your project and they try to add a transaction, they will have writing rights to be able to do that and reading rights to see the transactions. So this is pretty much resolved. We'll come back to our data and we have to think about what kind of data do we want in our collection. Right? So we already have a collection. I believed we had actually did. We did a change. Maybe it got deleted for some reason. So we'll create the collection again, I'll call it transactions and we'll do the exact same thing we just did before. Okay, it's been created and we'll just delete this document. So basically we already created our transactions. But what do we want each document to have? Well, I kind of want to keep track of the following. I want to keep track of the user who made the transaction. So the user idea of the user I want keep track of the description of the transaction, the transaction amount, also the transaction type. So if it's whether it's income or an expense and also when was this transaction created so that when we display the list of transactions we displayed an order by the date it was created, right? So we want to keep track of all of that. And to do that, it's pretty easy. We want to just create a function that is going to add a new document to this collection. Now, how are we going to do that? Well, I'm actually going to create what is known as a custom hook to handle all of the adding transactions functionality in our project. So for those who don't know, a custom hook is just react. Function is not a component because it's not returning gsx it's actually going to return data, but this function is going to use it has access to all of the, all the hooks that come with React, which is pretty nice because it will return data and it's almost like a helper function, but it also has access to React tools, right? So in this case we want to call this use ADD transaction. Now, if you're not familiar to custom hooks, just know that every hook has to start with the name use. So this is why it's called like this and it's standard. Whenever you want to add the functionality to add things into your project, you call it something like this use add transaction. Then over here we're going to create a function called Use ADD transaction. And for now it won't return anything. It would just return an empty object just like this. Right? But inside of this hook, what we want to do is we want to create a function called add transaction, and it's going to deal with Firebase. So it has to be an async await function. And at the end over here, we're just going to return this function. Now inside of you, what we want to use is this function from Firebase called Add Duck. So I'm going to import from Firebase slash Firestorm and I want to import the function add duck and add duck. As you might know, by the name means add document and we need to wait and duck. Whatever data we put in here will determine what what kind of document we're adding to our collection. Now, the first thing we need over here is what is known as the specification or a reference to which collection we want to add a document to. So you see over here we only have one collection called transactions, but we might have many of them. So in our code we have to specify which collection we're trying to add a document to. And in order to do that, we're actually going to import the collection function over here. Now we have to make a reference to that collection by calling it transaction collection. Ref Right. It's a reference to the transaction collection and we're going to send it equal to collection. Now the first thing we have to put over here is actually something we haven't created yet. We have to go to our config file over here and import at the top from Firebase slash Firestorm, something called Get Firestorm. Now Get Faster will allow us to create this variable similar to the other variable, but instead called d b, which means it's a reference to our database inside of our code. So we're going to say DB is equal to get faster and we're going to pass in the app over here. Now over here we need to import that DB variable by saying import from config slash firebase config and then DB. Now I'm going to press enter and we're just going to put the B inside of this thing. Then in order to actually reference the correct collection, we have to put the name of the collection over here. If you recall, we call the transactions as you can see. So this is what we add over here. Now we do have a reference to that collection. We just put it inside of the add function, right? We just copy this and put it over here. Then what we have to put over here is the data or the object we want to add to this document. So like I said, we want the following. We want a user ID to be something, right? We want to also pass in the description of the transaction. We want to add the transaction amount. So I'm going to put a mount over here, which will be a number. And we also want to add the transaction type, which is going to be a string. And finally we're going to add a property called Created at which it's going to be some sort of like timestamp given by Firebase. Actually, all of this over here, we'll have to get it somewhere. But the created at this firebase already allows this. It gives a function that will give us this information. this function we can actually just imported from faster over here it's called server timestamp. Well, this function does is it literally whenever you you call this function it will give you a timestamp of the current moment where the function was called. So if I put it over here, we'll know exactly when this was created at and it will make sense based on Firebase Time standards. So now all we need is this information. Well, let's think about this through. This is called add transaction. And where I'm actually going to call this is in the expense tracker component. I'm going to come over here at the top. I'm going to import the hook like this import from a dot slash, then go back and then go to hooks and then go to use that transaction. And I'm going to import the use transaction hook. Then over here I'm going to create a consent so that you could use add transaction and we're just going to get back the ADD transaction function. Now, what we want is to call this function whenever this form is submitted. So we have all of the data that the user is trying to use to add the transaction, such as what kind of transaction it is. So the transaction time, also the description and the amount, all of that we're going to have inside of this component, meaning that if we really want to have access to that data over here, we can technically just pass in as arguments to this add transaction function, meaning that over here we can accept an object containing the description that we want to add to the transaction, the transaction amount, and also the transaction type. So this fields over here that we still have left, we can just get it directly from the arguments and the parameters of this function so we can just delete this and it will be passed down from whatever whenever we call this function. Now the last one we need is the user ID now to get the user ID, we have to get it from our local storage. Right? Because if you recall, when we sign in, we set it over here. But since it's a lot of work to constantly whenever I want my my user ID to just get the item from the local storage and then convert it into an object. Because if you recall, it's a string right now in the local storage as we have to do that, I don't want to do this every single time. I want to use the user ID, I want to have a compact way to always have the information done. So I'm going to create another hook called Use Get user info. And what this is going to do is it's going to actually, whenever you call this, is going to return back to an object containing all of the user info inside of the local storage. But in as an object, not an actual string of fight object So I'm going to come over here, say export const use, get user info, which is going to be a not a function. Of course. And this book is actually going to be pretty simple. We're going to go over here and basically said this equal to Jason Pass. And what Jason the force does is it basically transforms string ified object in like we used the JSON string if I before and to back into an object. Right? So it's basically the other way around from what we did to put this into the local storage. So what I put inside of here is local storage dot get item and we put in the off key over here because this is why we called when we have this item. So we're just passing this JSON and it will come back as an object, but we will destructor the object with information that we want. So we know that in the local storage restored a name, a profile photo, a user ID, and also is off verbal. Over here at the bottom. We want to return this separately. Now, the reason why I want to return this symbol is because sometimes I just need the profile photo. Sometimes I just need the user ID. We want to have this quick and easy so that when you call this hook, you only get the specific thing that you want, right? So right now this is pretty much done. That means that in the user transaction, if I want to have access to the user ID that was in the local storage, all I have to do is just import this hook. So import use from that slash use get user info, import the hook and then just say const equal to use, get user info and just get the user ID from this. And then just put this over here. Right? Just like this, it will automatically use this if I just delete that. So you see we basically condensed and abstracted all of this logic into a simple hook that we can just call and get the info back. Now, we already did to cause some hooks so far in this project, right? And you might be asking yourself maybe what is the point of this? Couldn't we have just created add transactions function inside of this component over here and handled all the logic? Why are we doing all this extra work of putting it in another file and making it into a custom hook? Well, because to be honest, in the real world, when you want to create your components, you want to separate the logic from the UI. I think about this component, the expense tracker one as mostly for UI as you can see. Right. The logic should exist separately so that when you test your components, you can test the UI of this component and separately you can test the logic of this hook, right? There's separate things. So this is why people usually like to add custom hooks. It also makes this whole logic very reusable because if I had created the ADD transactions function over here I needed to add a transaction in another component, it would probably have to do something like add this into context or maybe like pass this as props. Something crazy like that. But to be honest, all we need to do now is just call this hook and it should all be good. So we basically finished the use add transactions hook, but we don't know if it's working yet. The only way to know if it's working is by trying to call it over here by default. I want to just try calling it once. I'm going to create a function called on Submit, which will be called whenever the form is submitted and I'm going to call it make it an async function like always, because it's going to call something. Then I'm going to basically paste it inside this form and give this an on submit. As you can see now, since this is a form, if we submitted, it will automatically try to refresh the page and add something that we don't want. So we always just prevent default like this right? Then I want to call the add transaction function right after this, because when we submit to form, we want to submit the transaction. Right now, I'm not currently getting the data from the inputs yet. We're going to do this in a second. But before we get that, I want to test out to see if the transaction function is working, but just feeding this some fake data. So the first big data I want to add is for the description. Let's pretend that we're adding a haircut, right? So just force feed some data over here. Let's see the transaction is 22. and let's say that the transaction type is equal to expense. Now, if I click on this button, hopefully we should see a new document in the transactions collection. But it seems like it deleted the collection, which happens whenever you create the collection too early and you spend a lot of time not adding anything to it and with no data in it. So basically Firebase just it. But that's not a problem, which is critical action for the third time. We're going to call it transactions and do the exact same thing we've done before. Fake a piece of data, save it, and then just delete this. And what I want to do is we see there's no no data in here, right? The transactions thing is still alive, as you can see, but there's no data. So we're going to come over here, click on Add transaction. I'm even going to open up the console just to see, just to see if there's any errors that appear. We click on transaction and it's asking me to fill this up, even though it's not going to use the data from this. But it's fine. I mean, if we're going to add transaction and let's see if the data is here, it seems like something was added, a document was added. If I click on it, it seems like it was correctly added. We have the haircut as a description, the amount as the number, the expense as a transaction type, and my user ID and also the date of when I created this. So the use transaction hook is or the use add transaction hook is working perfectly. Now we just have to get this data from the form instead of brute forcing it like this. Now to add the data to the form, I'm actually going to I already created the states and I'm just going to explain to you guys so this are the states we're going to be using. So we're going to be using one for each input in the form. And we only have three of them, right? We have to get the data for the description, the data for the amount and the data for which of the two radio buttons we clicked. So the way we're going to do this is we're going to create three different states. One is going to be called description, one is going to be called transaction amount and one is going to be called transaction type. Transaction type will be set to expense by default. I chose that because I think it makes sense, but you can change it to income, obviously, and all the other ones have just their initial values. So what we want is whenever there's a change in the description input, for example, so I'm going to put on change, I'm going to grab the event that is happening and then then I'm just going to set description as the event or targeted value. I'm pretty sure you guys are familiar with this approach of of like kind of keeping track of values and inputs. This is the most standard approach. So this is how I'm going to be following this. I'm going to do the exact same thing for the amount, but instead of set description, we'll call it set the transaction amount, which is the other state we created. And finally, we're going to do the same thing for both radio inputs. Now it's a bit different for them because they have the same function. So set transaction type and set transaction type. Now when we say event or target of value, this is going to refer to whatever value we put we put over here. And that's why I actually set a value, because now when you click on this radio button is going to set the transaction type to expense and over here it's going to set it to income. Now the only other thing we really need to do in this form is the fact that whenever I would say new status and I define let me just fix this real quick before continue, I'm just going to import You want to import at the top over here, import from React. We want to import the use state hook just like this. And you see it seems to be working. But what I wanted to say is even though we are keeping track of whether the expense or the income are being selected, you see we can first of all, select both of them at the same time, which doesn't make sense. And also there's none of them selected by default, which doesn't make sense because I did set expense as the default value of the state. So what we want to do is we want to come over here and to both the expense and the income radio. We want to add it checked property now. Well, this is going to refer to is if the transaction type is equal to in this case expense, then this input will be checked. If not, then you won't. Same thing with this one. But the difference is income. The difference that this makes is by default the transaction type is expense. So it checks expense. But then if we switch to income, it's not expense anymore. So you can select both of them at the same time. Some basic form handling for radio buttons. But it's really cool that it works as simple as it does. Now we're pretty much done with our one. The reason for that is because we are indeed keeping track of the necessary information. So instead of brute force in the data, we can just passing the, the description, the transaction amount and the transaction type just like this, and we just save it. Now let's test to see if it's working. We only have one document in our collection, as you can see. So we're going to add let's pretend like pretend I bought a keyboard with the amount of $35. It's an expense. I'm going to click add over here. It seems to have added and it seems to have kept track of the correct information. Let's try adding an income to see if it works as well. Let's say I sold a monitor, right? I'm going to put a monitor sold e b I don't know. Let's pretend I sold it on eBay and I sold it for $130 out in income. I'll could add transaction. And we should see that the new transaction is a monitor sold and it is of transaction type income, which is pretty good. So our whole functionality of adding stuff seems to be working. This is not the hardest part yet. I'm sorry. So the hardest part is now, which is getting this data in the correct format. So this is going to require a good amount of logic, but to be honest, it is not a complicated once you're done with it, you'll notice that everything makes sense and it's a good practice for whenever you're working with stuff like this in general, right? So since it is a lot of logic similar to the use at transaction, we're going to create a custom hook for getting the transactions. So I'm going to come over here, call this use, get transactions just like this. And over here we're going to export const use gets transactions and inside of this hook we're just going to first of all, try to understand what we're going to return, right? We're going to first return the actual transactions. So it will be list of the transactions that are in the database and that were made by the user currently logged in. So we will have to create some sort of state to keep track of this transactions so we can already create that by coming over here and first importing. They use state hook from React, say from React use state. And I want to create the transactions state and the set trends actions function, right? This is going to be a list. So by default it will have empty array. Now what I want to do is I want to create a function called get transactions. Now, the reason why I want to create this function is not that we're going to send this function as a return. We're not going to return this function, and no one is going to have access to this function. This function will actually actually only exist inside of this hook. The reason for this is because what I want to happen is whenever you load the expense tracker, it's going to be constantly listening for new transactions at all times referring to the current user. So you'll never have to call the transactions because it will be happening consistently depending on if new transactions have been added. So what I want to do here is actually want to call the use of fact hook and put the transactions inside of here just like this. Now, you might ask, Oh, why did I create an external function called get transactions instead of just putting the logic that I'm going to write inside of here instead of the use effect? Well, because get transactions have to be a sync because we're going to be dealing with Firebase inside of here and we can't have any async function inside of a use effect, meaning that we can't just do this right and then use the await inside of here. We have to create an external function and then just call it inside of the use effect. And also because it will abstract the logic inside of here without having to put a bunch of random stuff inside of the use effect. Now for the transactions, it's actually kind of cool what we're going to do. We're going to do what is known as a query inside of our database. Now it's going to be a bit different from a query, let's say on a my SQL database, because this is no SQL, right? But regardless, it's not that hard once you get used to it, I'll let you guys know that. So the first thing we want to do is we actually want to turn this into a try. Catch. Now, the reason why I want to turn this into a try catch is because I want to be handling any errors that might occur inside of this, inside of this function. Right. And in case there is any errors, I'll just console the error or the error message just like this. And instead of the try, I'm going to put all of the things that I want to attempt to do. And if any of them fail, it will be caught inside of this catch and it will just error them out so it won't break our code. So instead of the strike catch, the first thing I want to add is I want to make the query and I'm going to call this query transactions and I'm going to set this equal to something called query, which will automatically import from Firebase slash VAR Store. then, instead of you actually want to make a reference to which collection we want to query. So we're going to have to probably create something similar to what we did over here called transaction collection. Ref I'm going to put this below here, and for that we also have to import collection from Firebase and the B from our config file, right? So I'm going to import a go back twice and go to config and then firebase config and then import the DB variable. I also want to put both of this below the React import because I always like to have the React import at the top and obviously I forgot to put from over here and it should be working now. So we made a reference to our collection and we're just going to put it over here. Then what else do we need? Well, we have to specify what query we want to make. So when you're making queries in fire story, there's actually functions you can use in order to make the query work. So for example, what do we want to query here? Well, I want to query all the, the documents in this collection where the user I.D field in each document is equal to the user ID of the user that is currently logged in. So I have to say something like this. Well, I want to query from this collection where and where is an actual function that you can import from fire store where the field user ID is? And then we put over here the the actual operation we want to make on this where and you can see there's a lot of operations you can use in Firebase, but the one we want is the equal sign because we want to see that the user ID is equal to the user ID of the user. Now, we don't have access to this variable yet. We have to imported from the hook that we created and look how simple it will be because we already created that hook. All we have to do is just import from dot slash use, get it user info import they use get easier for hook and then just straight up const equal use, get user info and just get the user ID with no need to get any of that other information. Then we'll just pass it over here. Finally, we also want to order the transactions and this is more a personal reason, but we created the created add property and we add it to each of the documents so we can just say that we want to order this by and then specify that created add field, right this one over here, and it will return all the transactions ordered by that. So I'll save this. Now that we have our query transactions variable, I want to do something called on snapshot. So on snapshot is a function that comes from Firebase, as you can see, and it will basically keep tracking for a query if there is changes. So we'll passing this query over here and we'll then have a callback function called snapshot with a snapshot argument. Sorry. And it and whatever you put over here is whatever data you get back from the current snapshot of this query. So inside of this, it contains the data that we need for the users. So it's going to be a list, right? Because we're acquiring documents, multiple documents. So we'll probably have to loop through this list. So I'm going to say snapshot for each. And if you're not familiar with for each, it's basically going to loop through each element in an array, in this case, the snapshot of re and give you back a callback function containing each specific element. And each specific element will be the document that you need. So order to get the data from this document, we can actually separate it like this, create a verbal call data and set it equal to doc dot data just like this. The function that Oh, I'm sorry, I should have put an equal sign. It's actually a function that will return back. Exactly. This over here. But also I also want to have an D for each specific document. So for each specific transaction, I want have an ID and Firebase already gives us an ID. If we say doctor ID, it will give us a unique ID for each individual document. So I'm going to do that like this cast ID equal to duck ID and it's not a function, so it's just a variable. I can just do it like this. Then what I want to do is I want to have some sort of of array called docs, which is going to keep track of each of the documents that we want inside of this. On snapshot function. Now, the reason why we're doing this this way is and not just straight up saying something like set transactions equal to a snapshot since this is already the array of documents is because it's not in the format we want. What we want is an array containing an object that is going to contain all of this information together with the ID and much more. But we don't have that right now. Snapshot gives us a lot of data and we want to condense that data. That's why we're looping through each of the elements in the array and separating all of that information for each of the documents and adding them to the stock. Sorry. Now we can just say docs don't push and we're going to push in an object containing the data. Right? That is already an object that is going to contain all of this. But also we want to add the ID for the collection. It's not containing data, it's contained in this ID variable. So we're also going to add the ID inside of this object. And now we should have the correct data filter, right? So that means that after this snapshot is done right, we can actually come over here and just set transactions equal to docs because DOCS will now have the correct data that we want and we can just set it like this. So this is pretty much done. The only difference is we need to actually clean up this function after we're done, clean up the the use effect. So I'm going to let this be equal. To unsubscribe and set it equal to I wrote this completely wrong and send it equal to on Snapchat. If you're not familiar with this, this really doesn't matter. Just to return over here to the bottom, the cleanup function, which we'll just call the unsubscribe function. Now, this doesn't really matter if you're not familiar with what I'm doing, but oh, wait. Oh, because I'm defining this, I need to define this above the catch. So I'll say, let unsubscribe. And this should be working. So now at the bottom, we're returning transactions. This hook is not done. Trust me, it's not done. But it already has a lot of its initial functionality finished. So I do want to test it out to see if it's working. I'll come to our index charges and I'll import the hook over here at the top. I'll say use get transactions and use get transactions. Then I would just contact transactions over here. Oh, actually changes to use, get transactions and then just get the transactions directly and with this, what I can do is I can come over here at the bottom, right over here and below the transactions. I want to add a list, an unordered list for each transaction. So for each transaction I'm going to loop, right? I'm going to say transactions dot snap. So we're going to loop through the list and we're going to grab an individual transaction Then we're going to return back for each transaction list, write a list item which is going to contain, first of all, an H four tag, which is going to just display the description of the transaction. So we can grab this by saying either transaction description because transaction will contain the description, but also since we're going to be using a lot of the individual items of transaction multiple times, I actually want to before returning this, I want to structure the transaction by saying const transaction and getting the description, the transaction amount and the transaction type, which are the three things that I actually want to display for each individual transaction. Then instead of saying transaction description, I can just use the variable directly. Then what I want to do is I want to display both the transaction amount and also the transaction type. My first coming over here and adding the money sign, as you can see, and then saying transaction amount. And then I want to add this little dot over here and just add a label which is going to contain the transaction type, as you can see. Now let's check to see if this is actually working. It should, but it seems to not be working. Let's check out what is happening. It says that, oh yeah, the query here requires creating an index. I completely forgot about this, but yeah, this is something you do have to do. To be honest, I only remembered that I have to create an index when working with queries in firebase whenever I get this console.log because it doesn't matter to give, they automatically do it for you. An index it doesn't really matter for you don't have to understand it fully. Just know that you have to create one and you create one by clicking on this button and they will automatically do it for you When you sign in with your account associated with your Firebase project So I will sign in with the email account. It will open this up. It will ask or update indexes and it will automatically put all of the things you need to create this index. You'll click save and it will build. You do have to wait for the status to be completed in order to make this work, but it should go pretty quick. Okay. So as you can see, it now says status enabled, which means that if I refresh this, the the transactions should appear here. So I refresh this and they do. Right. As you can see, all of the transactions appear over here. Obviously, they look really ugly, but to be honest, it doesn't really matter. This is how it looks so far and the code seems to be working. One thing I do want to do, though, it is kind of styling, but it's it's not c assessed directly. I'm actually doing it over here is I actually want to add a caller to this label. So so that the color of the text is dependent on the transaction time. Now, the caller I wanted to be. Well, if transaction type is equal to expense. Well, I want the caller I want the caller to be read. And if not, if it's anything else such as income, I want it to be green. So let's try this out. You'll see that now it does change the color depending on the transaction type. Now, this is pretty much done, right? Kind of. But we do need to put information related to the user inside of here. Now, what I want to do is I'm going to add a bunch of the cases that like I've done personally. And the reason why I want to do this is because for the rest of the video, one, I want the website to kind of look cool, so I'll just copy and paste the cases that I already have done and just kind of glance over it so you guys can take a look. But again, I'm not going to go step by step and build it because it would waste too much time in the video. But basically how right here says here is for each individual page instead of the folders. I would create a styles that says file right? And this is obviously the case in which you're working with peers to assess if I was using something else like sets or if I was using Tailwind to assess everything, would be completely different. But in this case I would do something like this. I'm creating individual styles that says file for each individual page or each thing that I want to have styles for. And then I would just put the individual styles over here. And then on the respective pages such as this one over here, this is the expense tracker. I would just import dogs styles starts to assess and I would do the same thing for the other one over here. So I'll be back in the second win of the season as has been pasted. Okay. As you can see, I pasted all this here says this is the case for the log in the authentication page. It's pretty simple. Nothing major. Then this one is for the expense tracker. And just to make everything kind of look smoothly, you can see that this is how the website kind of looks right now. If I were to go to the off page, this is kind of how it looks. I just noticed I forgot to add one thing, which is there's kind of like a border by default in React types. So what I usually like to do is I usually like to come to the app that says and just add body padding is equal to zero and margin is equal to zero. That that should fix it up. But as you can see, we can come over here, sign in with Google, choose the option, So I'll just choose this option over here and again. And you can see we have our expenses. But one thing we don't have is it doesn't see the user's username over here, right? And it doesn't show the profile picture. So I'm going to add that right now. So in order to get that information, we already have a hook for it, right? We've been playing around with this for a while. It's called Use Get user info. Right. So I'll come to the expense tracker one and I'm going to import that actually at the top, so I'll just copy this piece over here, change this to use, get user info and change this to use, get user info and then over here I can literally just structure whatever I need. In previous examples, we to structure the user ID from this hook, but in this case for need that we only need to display any or the name the profile photo. And do we need anything else? No, we don't need the is off right. We just need those two things. So for the name, what I want to do is over here it says expense tracker. I want to kind of customize it by putting the name over here and then putting an apostrophe s. So it's going to be like Pedro Machado's, which is my name. Expense tracker. Oh, in this case, yeah. This is a fake email account. I mean, it says Moseby. though. So get the reference. We'll understand this. But yeah, like it seems to be working. We've put the name over here, but then for the profile picture is a little bit harder. It's not that it's harder, It's just that we haven't set up like, the proper thing for it. Yeah. So where are we going to set this up is above, below the form, below this dev, but not inside of transactions. We're actually going to see that if there is a profile photo, then because remember, we're getting this information from the Gmail account and maybe you don't have a profile picture in your Gmail account. So we all we we can't guarantee. So I'm going to put over here. If you haven't a profile picture, then I'm going to give a class name of profile. It's good that if based on the success, because now I just have to give the correct class names and the styling will automatically go. So the image will be like this. I give a class name for this image and call it profile photo, and then all I need to do is the source will be whatever we got back from Google. So I'll put proof photo and I'll save this. You'll see that the profile photo should appear over here. But I also want to add on top of that some sort of functionality or some sort of button that we can click to actually sign up. That's something that I don't know if you guys noticed, but we haven't done yet. I want to be able to sign out in case it's necessary. So we're going to come over here and add a button for signing out. We'll just write the sign out and we'll put a class name over here as sign out button and then I'll put an on click and we'll create a function called sign user out. And we'll have to create a function. Right now. Const sign user out. Well, to sign out is actually extremely simple and straightforward. You guys won't have any words, but this is basically with with Firebase there is a specific function called sign out for press enter. It will automatically imported from Firebase US. Let me just put this below the use of the state and this function is as simple as it can be. We're going to just say a contest actually to see a weight sign out and we just have to pass sign over here. The other variable that we have in our Firebase config file. So inside of this file over here and then it's going to actually sign sign up. But what we want to do is we want to try catch this so that we know if it's successful or not, because sometimes it might not be right. So if there's any errors, we're just going to console the error, this error, but actually you call it error like this. But one thing to notice is that if there's an error in signing out, this is what's going to be handled. It's going to like immediately leave this block and go to this run. So if there is no error, by putting the right catch, we can assume that every line after the weight sign out will mean that it has successfully logged out, which means we have to do some cleanup. For example, I want to clear the local storage because now we don't have a user signed in, right? So that information in local storage is useless. And it should be clear, not only that, but now I want to navigate towards not this way, I want to navigate towards the the initial page, the log in page. And to do that, all we have to do is just similar to what we've done already in the past, is we have to import from React Road. Adam and we import the use navigate hook. And then over here we just const navigate is equal to use navigate. And then you guys should know this should redirect us to this page. Let's check out to see if this working so you can see the styling for the button. I had put that in this SS but we should check the, the actual application so we do have a user logged in, as you can see over here. But if I click sign out, not only I get redirected back to the login page, but this has been cleared. So I log in again and then I'll click this and you should see that we're logged in again. One thing that we have to do, and it's probably the final thing we have to do, is handle this thing over here, right? We have a bunch of expenses. It's a bunch of income, but how are we going to do this? If you are interested in actually practicing your skills, want to challenge you guys to do this on your own and then come back to see if like, I mean, if you're able to do this, you guys know what? What? I was supposed to be here, right? This should be the zero balance. So the net income and expenses, this should be just income and this should be just the expenses. Well, try doing this on your own. If you succeed, then good job. Then check out my my version and see how different they are. But if you want to just watch this, be showing you guys how to do this right now. So in order to write this functionality, we're going to go back to the use get transactions. Hook I want to put that over here. Now, the reason why I want to put this over here is because there's a couple of ways to actually do this, which is kind of interesting. So you could create a table or collection in your database to keep track of the total income, total expenses and total and total balance, right. In general. And then every time there's a transaction for a specific person, you could just add or subtract that value. But there's another approach, which is easier because we don't have to have another collection. It might scale less properly because it's something you're handling in the frontend, but it's perfectly fine in my opinion. And this approach is the following. First of all, I want to create a state that is going to keep track of all the information we need. So it's going to be called transaction total transaction totals. I don't know if that English is correct, but it's fine. So this is going to be actually an object, not an array. And this object is going to contain both the like the balance, which is going to start at 0.0, then the income which is going to start a 0.0 as well, and the expenses which is going to start as 0.0. Now I'm doing 0.0 because we do want it to look like this. It doesn't go zero zero because of prettier, but 0.0 should be fine because we also accept decimals. Right? Then what I want to do is I want to come over here and to where we're dealing with our transactions. And if you recall this over here should return a list. A snapshot is a list of all the documents in this database, in this collection, where the IDs is equal to the user ID who's logged in. So that's perfect for us because we want to get the total income in the total expense and the balance from that specific user. So all we have to do is manipulate this data a little bit so that we can keep track of those two pieces of information. So I can say let total income equal to zero. We're initializing this variable variable and I'm going to do the same thing with total expenses equal to zero. And then over here, after we push, right, we've done that part of like just adding them to the array and listing the transactions. After that, I want to do a little bit of a calculation. Well, if we're dealing with with a transaction type, I shouldn't type equal to an expense. So if this is an expense, then what I want to do is I want to increase the total expenses by the actual transaction type. So data transaction type are transaction amount. Sorry about that transaction amount. Well, if it's not an expense, it's definitely an income. So I'm going to do the opposite. I'm going to do total income just like this. And this over here will be since we're getting from an input, from an input like a reasonable input, it's actually going to be represented as a string. So we do have to convert into a number so that we can edit property. So I'm going to come over here, turn this into a number as well, and this should be working fine. You'll see that in the end, when this loop is done, we're going to basically have the total income and total expenses as the correct values, right for that specific user. Now what we can do is right after we set the transactions right, we can also set our transaction totals. So we'll say set transaction totals and we'll make it such that the balance, well, we haven't dealt with the balance yet out for now, set it to zero, but then the total income and total expenses will be both set over here. So total income as well. And what we do for the balance is actually pretty simple, right? It's the net value. So what we can do is we can just let the balance equal to total income minus total expenses. That's simple. I don't know. Economics. Simple math balance is equal to the income, the positive minus the negative. Right. So what we have now is an object in a state containing the total balance, the total expenses and the total income. And what that means is that at the end over here, we now also return back the transaction totals state and we'll come over here to our expense tracker component and we will get back from use, get transactions, the transaction totals as well. Now there's a couple of things we want to do with transaction totals. So what I'd like to do is I'll actually destruction the structure, the three values because I think it will be easier to play around with them. So I'll set const equal to transaction totals and similar to how we've been doing over here since it's an object, I can just get the balance. The total income and the total expenses like this. I'm not sure why this values are are not correctly being distributed. Let me take a look at this. Oh, it's called income, not total income. So I'm sorry about this, guys, but it is should be expenses is equal to total expenses and income is equal to total income. Sorry if you guys notice this first. If so, that's pretty good. But over here we just have to grab the income and expenses. Perfect. Now what I need to do is will come down here the bottom, and we'll first start working with the balance. So with the balance, And first, I'm actually going to leave the balance for later our first work with the income. So for the income, what are we going to do is we can delete the preset amount over here. We're going to leave the money sign to be in front and over here we're just going to say income literally just this. And you'll see that now the income should appear over here. Write 187, which to be honest, it doesn't seem right. actually seems like it's adding everything. Is it? Actually, it doesn't. But it's weird that it says 187. Right. I'll just put the expenses and then check out to see what happened. But that was the income. Now, for the expenses, we'll do the exact same thing. Let's check out to see if the expenses is working. So we'll check over here. And it is not working. what does this mean? Well, we'll check out what we did wrong. This is actually a good exercise because I've said this a bunch of times already. I enjoy it whenever I make a mistake in a video like this because I can just show you guys how I debug. So it says total income equal to zero two or expenses equal to zero. And it seems like we've been properly checking this in, properly increasing this amount. Let's test to see what the value of total expenses and total income seems to be after each iteration. So we'll say console.log total expenses right next to total income after each loop will open up our console.log over here and we'll check. So first it definitely checks for the expense. 22 one is 57 and 87, so it seems like it's increasing the income instead of the expenses. Yeah, because see 22 is the first one and then he treats 35 as a as an, as an income and I'm making some mistake that is really dumb. Well, I am I actually just noticed and this is actually a good example to why you should use TypeScript. I wrote transaction wrong and you guys, you guys might have already figured this out, but I literally wrote transactions wrong. So this should be transaction transaction type and I hope this works. Yeah, it does work. So if you got annoyed at me because I was not seeing the mistake I made, I'm sorry, but this is debugging your time and I hope you guys can see that even I can make a really dumb mistake like this. But bottom line, we're now seeing the expenses in the income. Then all we have left is just to calculate the balance. And the balance will be pretty simple, but also kind of interesting. We already have the value for balance, but I want to show you guys what the problem with that is. I'm going to come over here and put balance right and balance, it says correctly, $73, but balance differently from income and expenses. It can actually be negative, right? Expenses is a negative. But when you say, oh, I have $80 of expenses, you never see or have -$80 of expenses. But if you have a negative balance, you say, Oh, I have -$73. So the problem with this is the way we structured, if I were to come over here and say that I went to McDonald's, right, and I ordered $200 worth of maintenance and I added this transaction and I would says that is -127. But the negative sign is not in front of the money sign. Well, that's not that big of a deal. But I want to show you guys how to fix that. And it's pretty simple, to be honest. You just come over here, your first ask, Oh, is the balance is greater than or equal to zero? Because if it is, then I know for a fact it's positive and I can just display the actual balance. But if it's not greater than or equal to zero, that means it's negative. So I'll actually display an H2 tag just like this, but I'll put the negative sign in front. And the problem with this yet there's one thing more and one more thing I have to do. The problem with this is because now it's double negative. So you have to make sure that the balance is actually the number is positive. So multiply this number by negative one and you'll see that it now correctly says the amount. I can also, for some reason work in McDonald's. So I'll put McDonald's salary and I'll earn something that will make this positive $500, change this income and you'll see that now it is positive. If I were to I don't know, let's pretend I got sued and I have to pay a settlement of, uh, I don't know, an absurd amount right now. It's an expense. Let's add this. And yeah, I'm -39,627. Also, with the costs, I added this cool thing where you can scroll if the information inside is overflowing, that's pretty simple. You can check out the code as well. But yeah, there is definitely other things you could add here. For example, you could add the column over here, but those things, to be honest, I don't I don't really care about that much. You can also clear this inputs if you wanted to. That could be kind of cool, to be honest. I might do that right now just because, I mean, you guys already here, I'll just show you how to do this real quick. So to do that, all you had to do is go to the individual inputs, set a value to them. So for example, this is the description input. I set the value equal to the description state. I'll do the same thing with the amount one but set it to the transaction amount, right? And then whenever we actually add right, we add a transaction at the end. I'll just set those two states to be an empty string and that will kind of clear out that the input. Right? So I'll just do this and you'll see that if I were to come over here and say, let's say I got I won the lottery, so lottery, I'll see that I won this amount right now, not only worked, but it also cleared both inputs. I also just remembered something that I still have to do. And I don't think you guys even notice that this was missing. Is this the fact that if I close this and I went over here and and opened it again, would see that although I am logged in, it's asking me to sign in with Google again. And the reason for this mistake, which is not really a mistake, is just because we're not redirecting in case the user is already authenticated. If we recall, when we created our local storage item, we actually created a property called is off. Let me try to open this up. I don't know if I'll even get there. I can. It doesn't matter. You guys remember that I created this property called Is off. It's literally in our code. So what I want to do is I actually want to. Whenever you go to the authentication page, I wanted to check to see if you were already authenticated because if you are, then I want to redirect you to the expense tracker page. Now, how do we get the information if you're authenticated or not? Well, we import the the hook, right. So import from two slashes. Two slashes and then plugs use get user info, import use get user info. We're going to const equal to use, get user info, and we're going to import that is off variable. And there's one thing that I wanted to show you guys. I've been showing you guys throughout the whole video that with Victory Dom, you can navigate by calling this function like this. But the problem with this is in this case, how would I navigate my this right? How would I do that? Well, there's a couple of ways of doing it. I could actually create a user effect over here, write something like this and just check, oh, if is us, then call the navigate function. This would work. But there's another way. And I wanted to show you guys, which is to actually use a component from where I tried it on called the navigate component. So just this two ways that you can actually do this and it's cool that I can do both of them and in their respective use cases inside of this component. This component navigate is really simple. You just say, Oh, if is office true, then return the navigate component and you can put a prop over here. Same too. And then you put the string saying Express use tracker. It's basically the same thing, but it serves for cases like this where you want to redirect because of some sort of condition immediately when this specific component has been rendered. Right. So in this case, the condition is off. Is off is true. We don't even have to check whatever is below here because we already want to navigate towards this component and we can check that this works because I was on the other page, right? I can even try to go to the other page, but it will automatically redirect me back to this one. Right? So everything seems to be working. So yeah, that's that's basically it. The last thing I want to do is not related to the code is just deploying this application so if you recall in the beginning of the video we actually got some instructions on how to deploy. The instructions included. We put it over here, included running Firebase log in Firebase and it and Firebase Deploy. Like in the beginning of the video I told you guys to install or install the the firebase Tools CLIA. We've done that already, so we're not going to do that again. But if you haven't, I recommend trying the Firebase log in command and see if you are logged. In my case, I'm already logged in. So now what we have to do is we actually have to first create a build version of our project. If you've ever deployed anything in the past, you know that that's a good thing for you to do. When you build. It will create this build folder over here in your project. And when it's done, it kind of creates an optimized version of your project inside of this folder, and this is what we're going to actually send to Firebase to deploy. Since we've done that, we're just going to run Firebase in it and it's actually going to start asking us a bunch of questions. And this is where things get a little bit confusing. But stick around with me and you'll see how it's done. So basically, it's going to first ask, what features do you want to set up for this directory? So there's a bunch of options over here, and the one I want is, well, think about it this way. What do I want? I want to have fire story as an option. I want to have hosting as an option when I have a bunch of those things right? But technically, what it's really asking here is what I want to set up right now, right? So I want to set up hosting and of the options, the one that I want is the first one, the hosting configure files for Firebase Hosting and optionally set up GitHub action deploys. So to choose that I'm going to press space, it's going to become green, as you can see, and then I want to press enter. Now it's going to ask to select select an option. I'm even going to zoom in a little bit. You guys can see everything better. It's going to say, Do you want to use an existing project, Create a new project, add to an existing Google Cloud platform project. But no, to be honest, we just want to using an existing project because we already created it in our console. So I'm choosing the first option then it's going to list all of the options of projects that we have inside of our application, and we have to choose which one we wanted to deploy. So I know I have to actually just have one one expense tracker. Let me just make sure the way we make sure which project we're talking about is well, actually check out this thing over here and the URL. It's the name of your project. This one ends with D one for E eight. So that's the one. So that's the one I'm going to choose. I'll come over here. I'll choose this one. Press enter. Then it's going to ask, Do you want to use public as your public directory? Yes, we've actually already built this thing, so we're going to press enter, then configure single page app. I'm just going to press enter as the default option, then set automatic builds and deploys that GitHub. I want to press yes for this because we want actually we're going to set up our GitHub account such that whenever you push any code into a GitHub repository, it will automatically trigger a redeploy, meaning that if you make changes to your project and you merged them into your main branch, it will automatically redeploy for all of your users. That's how actual websites usually work. There's obviously more checking and more stuff in the pipeline before going public, but in this case it will look something very similar. then is going to ask, okay, the file public slash index HMO already exists. Do you want to override it? I'm going to put no, I'm going to press enter and it's going to open up the GitHub login for this application. It actually opened up in a different monitor for me, but I have already connected my GitHub account with my Firebase CLIA, so it's already done. But in our case might prompt up for you to log in with with GitHub so that you connect both. And now what is asking is what GitHub repository inside of our GitHub account do we want to basically connect our Firebase project with? So we're going to go to GitHub right now, GitHub dot com and we're going to open up. This is my account. I'm going to create a new repository right in front of you guys and click on new repository over here. I'm going to give it a name as expense tracker Firebase React. I don't know, I'm just calling this because this is the name of the repository for the code. You guys will probably have access during the description. Then I'm just going to click create repository. It's going to give us a bunch of options as usual. When you deploy something, I'm going to actually open a visual Studio code and I'm going to open up a new terminal. This is going to be the terminal we're going to be using to deploy this app into a repository, right? So I'm going to run git init, then I'm going to say git add dot, then I'm just going to copy all of this stuff over here and paste it over there just like this. Hopefully everything works out with GitHub. It seems to have worked out and I refreshed this and you should see that all the code is here. Now that our code is displayed inside of this repository, we actually, if we look back at the commands said by by far the fire, basically it says that we need to enter here exactly in the format of a username and slash a repository. What repository do we want to connect. So what it means is basically you have to copy this part of the URL over here So all we have to do here is just paste that information and then press enter. It's going to retrieve the information from our repository and when it's done, it's going to ask, okay, do you want to set up a workflow to run a build script before ever deploy? Well, in this case, we don't want to do that. So I'm going to press no or end and we'll say no. Then it's going to say set up automatically deployment to your site's life channel when a PR is merged. So yes, we do want that. So I'm going to press enter, by the way. I press enter and right sometimes because the the default option is capitalized on this thing. So in this case, the why was capitalized. So yes, was the default option and it was the one that we want. So that's pretty good. Finally, it's going to ask what is the name of the GitHub branch associated with your site's life channel? Well, basically if you have multiple branches, it's going to ask which one you want to actually be the one that when everything is merged is going to trigger redeploy. In our case, we only have one branch. So I'm just going to press enter because by default it's already the main branch. And as you can see it says Firebase Initialization complete. If all has worked so far, this means that if I just run Firebase deploy, everything should be deployed inside of our project. Let's check how I just press that on and it seems like it is indeed working. We're going to click on the hosting URL to see if it is working and let's see. Well, it's kind of empty right now. doesn't seem to be showing anything. But while that is loading, I think there's no problem with it. And I think it's just a matter of like seconds between before that actually starts working. Make sure we're going to call copy the project console should don't know. This is where basically the information related to your project and how it's running if it's deployed or not, this is where you can see it. So, so far it seems that it's not working, but it says that it has been deployed. okay guys so I tried to figure it out what the problem was and it was a stupid one. It was my mistake. But I'm going to show you guys what it was, so I want you to actually run Firebase in it again and go through this again. But since you guys have already done it, it's pretty quick. Choose hosting then on this part over here instead of just going for your public directory to be public, actually choose, build and this was my mistake. I forgot I ran your own build before and then our project will actually be in the build folder, not in the public one. So I'll just write, build, press, enter then for this will just continue what we did before. We want to set that up. Build index that already exists. You want to overwrite? I'm going to press. No. And then it's going to ask for authentication. Like we've said before, we've already done it. It's also going to ask the again for the GitHub account. I'm just going to do the same thing for the GitHub repository pasted here. Press enter, then it's going to connect again up and then it's going to ask the same questions we've had before. And this for this I just put yes, I think it's the same question as before. Now, initialization complete. Now finally, I'll just put Firebase deploy, Press enter and it should be done. One thing to note, the reason why I actually made this mistake is because Firebase deployment seems to have changed. So if we do so, the changes that I had before doesn't really work anymore. So just copy that link. And actually I already have it here. I'll refresh it and then I can change meaning that it was deployed, but it's still blank, so let's check this out. Can't read name. Okay. Yeah. But there seems to be a bug here. So. Inspector element console. Mm hmm. Says that. Can I get properties in all every name from use get user info. Let's check this out. So I'm pretty sure I know where that is. I think somewhere this is where a name is, right? Yeah, I this thing over here is actually a problem, because if local storage doesn't have an item and it passes it, there won't be an item code name to actually return. Right. And it's not just mean. If I delete a name over here and left it like this, the error would say profile photo. It's just it's the first and then it stopped looking at the other errors. So what I have to do here is actually have to put a condition saying a fallback, saying that if this returns null, it's still an object and I can so that's destructor name from it. But the problem is that even if it's an empty object, it can try to destructor name even though we won't exist in it. So this will fix the issue. I believe I'll save this run yarn build and then remember that if you're going to deploy right, you'll always have to yarn build because that's the version that Firebase uses to actually host your project. And then I'm just going to run Firebase Deploy now, the reason why I'm running Firebase Deploy instead of just pushing to GitHub, which I will right now actually do it right now is because to be honest, I just when you push to GitHub, it will, I just have to fix this. So when you push again, it will trigger a deployment, but it might take a bit. In my case, I just wanted a quick deploy to show you guys that this is going to be working. I will refresh this over here. Hopefully, as you can see, our project seems to be life. And the good thing is if I try to sign in and I use the same email account I used in my testing, so like in the non live version, the data should still be there. As you can see, it's still there because it's all connected into the same database, right? And it seems to all be working. So this is basically it for the video. I really hope you guys enjoyed this video. Enjoy it. Please. Come on. Which one is next? Subscribe Because I'm posting, I'm actually going to be posting once every two weeks. I believe the reason is I decided to change the way I approach my videos. This video specifically is a little bit longer and I kind of like that. The reason for that is because if I make longer videos, I have a brand new group of videos that I couldn't make before because of my fast paced schedule and a lot of the views you guys have been asking me for years now, I couldn't make it because I had to post every week, but now I'm going to have a lot more time to do so. Not to mention I'm fully graduated now from from university, so I'm going to be starting work full time. For those who don't know, I'm going to be working for Twitch in about a month. So I believe I also have more time because. I do think school takes more time than full time work, which is crazy. But I will have more time and I'll be making videos like this. This is also my first video after a hiatus of almost of almost a month. And I'm really happy to be back. I'm really happy to be full on devoted to this, and I'm really excited to produce really good content for you guys. Again, if you're interested in check out the code, it will all be the description. I wanted to thank Skillshare again for sponsoring this video. I really enjoy the platform, have been using it for years and have been working with them for years now, so I really enjoy that. They gave me opportunity. So that's basically it. I really hope it gets enjoyed it. And I see you guys next time.
Info
Channel: PedroTech
Views: 34,476
Rating: undefined out of 5
Keywords: computer science, crud, javascript, learn reactjs, mysql, nodejs, programming, react tutorial, reactjs, reactjs beginner, reactjs tutorial, typescript, react js crash course, react js, node js, express js, pedrotech, traversy media, traversymedia, clever programmer, tech with tim, freecodecamp, deved, pedro tech, firebase, react firebase, react project, react portfolio, react best project, react resume project, firebase project, expense tracker, deploy react, react deploy, react host
Id: _ycD0d7skdQ
Channel Id: undefined
Length: 115min 28sec (6928 seconds)
Published: Wed Sep 06 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.